NGINX/nginx详解.md

3648 lines
153 KiB
Markdown
Raw Permalink Normal View History

2024-06-07 16:38:25 +08:00
# Nginx详解
## 一、HTTP协议
HTTP协议是Hyper Text Transfer Protocol超文本传输协议的缩写,是用于从万维网WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
### 1、HTTP 工作原理
**HTTP协议通信流程**
![1561893004169](assets/1561893004169.png)
www.baidu.com/index.html
静态页面
动态页面
python python-mysql api接口 mysql
**HTTP是基于客户端/服务端C/S的架构模型**,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。
**浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求**。一个HTTP"客户端"是一个应用程序Web浏览器或其他任何客户端通过连接到服务器达到向服务器发送一个或多个HTTP请求的目的。
**Web服务器根据接收到的请求后向客户端发送响应信息**。一个HTTP"服务器"同样也是一个应用程序通常是一个Web服务如Apache Web服务器或IIS服务器等通过接收客户端的请求并向客户端发送HTTP响应数据。
HTTP使用统一资源标识符Uniform Resource Identifiers, URI来传输数据和建立连接。
```bash
CGI
通用网关接口Common Gateway Interface/CGI描述了客户端和服务器程序之间传输数据的一种标准可以让一个客户端从网页浏览器向执行在网络服务器上的程序请求数据。CGI 独立于任何语言的CGI 程序可以用任何脚本语言或者是完全独立编程语言实现,只要这个语言可以在这个系统上运行
```
#### 关于URI
官方地址:[标识互联网上的内容 - HTTP | MDN (mozilla.org)](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/Identifying_resources_on_the_Web)
![image-20220613103532626](assets/image-20220613103532626.png)
HTTP 请求的内容通称为"资源"。"资源"这一概念非常宽泛,它可以是你能够想到的任何格式。每个资源都由一个 (URI) 来进行标识。
URI包含URL,URN
URL即统一资源定位符它是 URI 的一种。可以是一份文档一张图片或所有其他。不仅标识了Web 资源,还指定了操作或者获取方式,同时指出了主要访问机制和网络位置。
URN是URI的一种用特定命名空间的名字标识资源。使用URN可以在不知道其网络位置及访问方式的情况下讨论资源。
**例子**
这是一个URI"http://bitpoetry.io/posts/hello.html#intro"
```http
"http://" 定义访问资源的方式。
"bitpoetry.io/posts/hello.html" 是资源存放的位置。
"#intro" 是资源。
```
URL是URI的一个子集告诉我们访问网络位置的方式。上例URL应该如下所示
```http
http://bitpoetry.io/posts/hello.html
```
URN是URI的子集包括名字给定的命名空间内但是不包括访问方式如下所示
```http
bitpoetry.io/posts/hello.html#intro
```
#### URL统一资源标识符语法
**方案或协议**
![Protocol](assets/mdn-url-protocol@x2.png)
```
`http://`告诉浏览器使用何种协议。对于大部分 Web 资源,通常使用 HTTP 协议或其安全版本HTTPS 协议。另外,浏览器也知道如何处理其他协议。例如, `mailto:` 协议指示浏览器打开邮件客户端;`ftp:`协议指示浏览器处理文件传输。常见方案见下表
```
| 方案 | 描述 |
| :---------- | :----------------------------------------------------------- |
| data | [Data URIs](https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Basics_of_HTTP/Data_URLs) |
| file | 指定主机上文件的名称 |
| ftp | [文件传输协议](https://developer.mozilla.org/en-US/docs/Glossary/FTP) |
| http/https | [超文本传输协议/安全的超文本传输协议](https://developer.mozilla.org/en-US/docs/Glossary/HTTP) |
| mailto | 电子邮件地址 |
| ssh | 安全 shell |
| tel | 电话 |
| urn | 统一资源名称 |
| view-source | 资源的源代码 |
| ws/wss | (加密的)[WebSocket (en-US)](https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API) 连接 |
**主机**
![Domaine Name](assets/mdn-url-domain@x2.png)
```
`www.example.com` 既是一个域名,也代表管理该域名的机构。它指示了需要向网络上的哪一台主机发起请求。当然,也可以直接向主机的 [IP address](https://developer.mozilla.org/zh-CN/docs/Glossary/IP_Address) 地址发起请求。但直接使用 IP 地址的场景并不常见。
```
**端口**
![Port](assets/mdn-url-port@x2.png)
```
:80` 是端口。它表示用于访问 Web 服务器上资源的技术“门”。如果访问的该 Web 服务器使用 HTTP 协议的标准端口HTTP 为 80HTTPS 为 443授予对其资源的访问权限则通常省略此部分。否则端口就是 URI 必须的部分。
```
**路径**
![Path to the file](assets/mdn-url-path@x2.png)
```
`/path/to/myfile.html` 是 Web 服务器上资源的路径。在 Web 的早期,类似这样的路径表示 Web 服务器上的物理文件位置。现在,它主要是由没有任何物理实体的 Web 服务器抽象处理而成的。
```
**查询**
![Parameters](assets/mdn-url-parameters@x2.png)
```
`?key1=value1&key2=value2` 是提供给 Web 服务器的额外参数。这些参数是用 & 符号分隔的键/值对列表。Web 服务器可以在将资源返回给用户之前使用这些参数来执行额外的操作。每个 Web 服务器都有自己的参数规则,想知道特定 Web 服务器如何处理参数的唯一可靠方法是询问该 Web 服务器所有者。
```
**片段**
![Anchor](assets/mdn-url-anchor@x2.png)
```
`#SomewhereInTheDocument` 是资源本身的某一部分的一个锚点。锚点代表资源内的一种“书签”,它给予浏览器显示位于该“加书签”点的内容的指示。 例如,在 HTML 文档上,浏览器将滚动到定义锚点的那个点上;在视频或音频文档上,浏览器将转到锚点代表的那个时间。值得注意的是 # 号后面的部分,也称为片段标识符,永远不会与请求一起发送到服务器。
```
### 2、HTTP的特点
**HTTP是无连接的**
无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
**HTTP是媒体独立的**
只要客户端和服务器知道如何处理的数据内容任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
**HTTP是无状态的**
HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息则它必须重传。
### 3、客户端请求消息
客户端发送一个HTTP请求到服务器的请求消息包括以下格式请求行request line、请求头部header、空行和请求数据四个部分组成下图给出了请求报文的一般格式。
![1561893148999](assets/1561893148999.png)
### 4、服务器响应消息
HTTP响应也由四个部分组成分别是状态行、消息报头、空行和响应正文。
![1561893177737](assets/1561893177737.png)
**实例**
下面实例是一点典型的使用GET来传递数据的实例
客户端请求:
```shell
# curl -v http://www.testpm.cn
Connected to www.testpm.cn (47.244.247.240) port 80 (#0)
> GET /hello.txt HTTP/1.1 # 请求方式与版本协议。
> User-Agent: curl/7.29.0 #用什么客户端访问
> Host: www.testpm.cn #主机名,域名。主机和端口号,
> Accept: */* #匹配什么文件类型,“*” 是通用匹配。匹配所有类型
```
服务端响应:
```shell
< HTTP/1.1 200 OK # 请求返回的状态码
< Server: nginx/1.16.0 # 请求的服务和版本号
< Date: Thu, 04 Jul 2019 08:19:40 GMT # 当前时间北京时间为GMT+8小时
< Content-Type: text/plain # mime类型test/plain:普通文本,比如image/png...video
< Content-Length: 12 # 内容的长度
< Last-Modified: Thu, 04 Jul 2019 08:13:25 GMT #最后修改时间
< Connection: keep-alive #是否支持长连接
< ETag: "5d1db525-c" # 标识每次访问如果与最开始的值一样返回304否则校验不一致返回200
< Accept-Ranges: bytes # 接受的范围单位
```
输出结果:
```shell
hello world
```
### 5、HTTP 请求方法
根据HTTP标准HTTP请求可以使用多种请求方法。
```bash
HTTP1.0定义了三种请求方法: GET, POST 和 HEAD方法。
HTTP1.1新增了五种请求方法OPTIONS, PUT, DELETE, TRACE 和 CONNECT 方法。
```
![1561896279402](assets/1561896279402.png)
### 6、HTTP 响应头信息
HTTP响应头提供了关于请求响应或者其他的发送实体的信息。
| 应答头 | 说明 |
| ---------------- | ------------------------------------------------------------ |
| Allow | 服务器支持哪些请求方法如GET、POST等。 |
| Content-Encoding | 文档的编码Encode方法。只有在解码之后才可以得到Content-Type头指定的内容类型。利用gzip压缩文档能够显著地减少HTML文档的下载时间。Java的GZIPOutputStream可以很方便地进行gzip压缩但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它。因此Servlet应该通过查看Accept-Encoding头即request.getHeader("Accept-Encoding")检查浏览器是否支持gzip为支持gzip的浏览器返回经gzip压缩的HTML页面为其他浏览器返回普通页面。 |
| Content-Length | 表示内容长度。只有当浏览器使用持久HTTP连接时才需要这个数据。如果你想要利用持久连接的优势可以把输出文档写入 ByteArrayOutputStream完成后查看其大小然后把该值放入Content-Length头最后通过byteArrayStream.writeTo(response.getOutputStream()发送内容。 |
| Content-Type | 表示后面的文档属于什么MIME类型。Servlet默认为text/plain但通常需要显式地指定为text/html。由于经常要设置Content-Type因此HttpServletResponse提供了一个专用的方法setContentType。 |
| Date | 当前的GMT时间。你可以用setDateHeader来设置这个头以避免转换时间格式的麻烦。 |
| Expires | 应该在什么时候认为文档已经过期,从而不再缓存它? |
| Last-Modified | 文档的最后改动时间。客户可以通过If-Modified-Since请求头提供一个日期该请求将被视为一个条件GET只有改动时间迟于指定时间的文档才会返回否则返回一个304Not Modified状态。Last-Modified也可用setDateHeader方法来设置。 |
| Location | 表示客户应当到哪里去提取文档。Location通常不是直接设置的而是通过HttpServletResponse的sendRedirect方法该方法同时设置状态代码为302。 |
| Refresh | 表示浏览器应该在多少时间之后刷新文档以秒计。除了刷新当前文档之外你还可以通过setHeader("Refresh", "5; URL=http://host/path")让浏览器读取指定的页面。 注意这种功能通常是通过设置HTML页面HEAD区的META HTTP-EQUIV="Refresh" CONTENT="5;URL=http://host/path"实现这是因为自动刷新或重定向对于那些不能使用CGI或Servlet的HTML编写者十分重要。但是对于Servlet来说直接设置Refresh头更加方便。 注意Refresh的意义是"N秒之后刷新本页面或访问指定页面",而不是"每隔N秒刷新本页面或访问指定页面"。因此连续刷新要求每次都发送一个Refresh头而发送204状态代码则可以阻止浏览器继续刷新不管是使用Refresh头还是META HTTP-EQUIV="Refresh" ...>。 注意Refresh头不属于HTTP 1.1正式规范的一部分而是一个扩展但Netscape和IE都支持它。 |
| Server | 服务器名字。Servlet一般不设置这个值而是由Web服务器自己设置。 |
| Set-Cookie | 设置和页面关联的Cookie。Servlet不应使用response.setHeader("Set-Cookie", ...)而是应使用HttpServletResponse提供的专用方法addCookie。参见下文有关Cookie设置的讨论。 |
| WWW-Authenticate | 客户应该在Authorization头中提供什么类型的授权信息在包含401Unauthorized状态行的应答中这个头是必需的。例如response.setHeader("WWW-Authenticate", "BASIC realm="executives"")。 注意Servlet一般不进行这方面的处理而是让Web服务器的专门机制来控制受密码保护页面的访问例如.htaccess。 |
### 7、HTTP 状态码
当浏览者访问一个网页时浏览者的浏览器会向网页所在服务器发出请求。当浏览器接收并显示网页前此网页所在的服务器会返回一个包含HTTP状态码的信息头server header用以响应浏览器的请求。
HTTP状态码的英文为HTTP Status Code。
**下面是常见的HTTP状态码**
- 200 - 请求成功
- 301 - 资源网页等被永久转移到其它URL 永久重定向
- 404 - 请求的资源(网页等)不存在
- 500 - 内部服务器错误
- 403 - 访问被拒绝
- 302 - 临时重定向
- 304 - 缓存
**HTTP状态码分类**
HTTP状态码由三个十进制数字组成第一个十进制数字定义了状态码的类型后两个数字没有分类的作用。HTTP状态码共分为5种类型
![1561896413177](assets/1561896413177.png)
###### HTTP状态码列表
| 状态码 | 状态码英文名称 | 中文描述 |
| ------ | ------------------------------- | ------------------------------------------------------------ |
| 100 | Continue | 继续。客户端应继续其请求 |
| 101 | Switching Protocols | 切换协议。服务器根据客户端的请求切换协议。只能切换到更高级的协议例如切换到HTTP的新版本协议 |
| | | |
| 200 | OK | 请求成功。一般用于GET与POST请求 |
| 201 | Created | 已创建。成功请求并创建了新的资源 |
| 202 | Accepted | 已接受。已经接受请求,但未处理完成 |
| 203 | Non-Authoritative Information | 非授权信息。请求成功。但返回的meta信息不在原始的服务器而是一个副本 |
| 204 | No Content | 无内容。服务器成功处理,但未返回内容。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
| 205 | Reset Content | 重置内容。服务器处理成功,用户终端(例如:浏览器)应重置文档视图。可通过此返回码清除浏览器的表单域 |
| 206 | Partial Content | 部分内容。服务器成功处理了部分GET请求 |
| | | |
| 300 | Multiple Choices | 多种选择。请求的资源可包括多个位置,相应可返回一个资源特征与地址的列表用于用户终端(例如:浏览器)选择 |
| 301 | Moved Permanently | 永久移动。请求的资源已被永久的移动到新URI返回信息会包括新的URI浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
| 302 | Found | 临时移动。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
| 303 | See Other | 查看其它地址。与301类似。使用GET和POST请求查看 |
| 304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
| 305 | Use Proxy | 使用代理。所请求的资源必须通过代理访问 |
| 306 | Unused | 已经被废弃的HTTP状态码 |
| 307 | Temporary Redirect | 临时重定向。与302类似。使用GET请求重定向 |
| | | |
| 400 | Bad Request | 客户端请求的语法错误,服务器无法理解 |
| 401 | Unauthorized | 请求要求用户的身份认证 |
| 402 | Payment Required | 保留,将来使用 |
| 403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
| 404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面 |
| 405 | Method Not Allowed | 客户端请求中的方法被禁止 |
| 406 | Not Acceptable | 服务器无法根据客户端请求的内容特性完成请求 |
| 407 | Proxy Authentication Required | 请求要求代理的身份认证与401类似但请求者应当使用代理进行授权 |
| 408 | Request Time-out | 服务器等待客户端发送的请求时间过长,超时 |
| 409 | Conflict | 服务器完成客户端的PUT请求是可能返回此代码服务器处理请求时发生了冲突 |
| 410 | Gone | 客户端请求的资源已经不存在。410不同于404如果资源以前有现在被永久删除了可使用410代码网站设计人员可通过301代码指定资源的新位置 |
| 411 | Length Required | 服务器无法处理客户端发送的不带Content-Length的请求信息 |
| 412 | Precondition Failed | 客户端请求信息的先决条件错误 |
| 413 | Request Entity Too Large | 由于请求的实体过大服务器无法处理因此拒绝请求。为防止客户端的连续请求服务器可能会关闭连接。如果只是服务器暂时无法处理则会包含一个Retry-After的响应信息 |
| 414 | Request-URI Too Large | 请求的URI过长URI通常为网址服务器无法处理 |
| 415 | Unsupported Media Type | 服务器无法处理请求附带的媒体格式 |
| 416 | Requested range not satisfiable | 客户端请求的范围无效 |
| 417 | Expectation Failed | 服务器无法满足Expect的请求头信息 |
| | | |
| 500 | Internal Server Error | 服务器内部错误,无法完成请求 |
| 501 | Not Implemented | 服务器不支持请求的功能,无法完成请求 |
| 502 | Bad Gateway | 作为网关或者代理工作的服务器尝试执行请求时,从远程服务器接收到了一个无效的响应 |
| 503 | Service Unavailable | 由于超载或系统维护服务器暂时的无法处理客户端的请求。延时的长度可包含在服务器的Retry-After头信息中 |
| 504 | Gateway Time-out | 充当网关或代理的服务器,未及时从远端服务器获取请求 |
| 505 | HTTP Version not supported | 服务器不支持请求的HTTP协议的版本无法完成处理 |
## 二、Nginx入门
### Nginx 介绍
Nginx (engine x) 是一个轻量级,高性能的 HTTP 和 反向代理服务也是一个邮件代理服务使用C编写。因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。其特点是占有内存少并发能力强。
单机环境下并发连接数在7000+ -8000左右集群模式20000+
中国大陆使用nginx网站用户有百度、[京东](https://baike.baidu.com/item/%E4%BA%AC%E4%B8%9C/210931)、[新浪](https://baike.baidu.com/item/%E6%96%B0%E6%B5%AA/125692)、[网易](https://baike.baidu.com/item/%E7%BD%91%E6%98%93/185754)、[腾讯](https://baike.baidu.com/item/%E8%85%BE%E8%AE%AF/112204)、[淘宝](https://baike.baidu.com/item/%E6%B7%98%E5%AE%9D/145661)等。
IIS 套件 windows apache
netcraft.com
创始人伊戈尔·赛索耶夫
![1561897072438](assets/1561897072438.png)
### 为什么选择 nginx
```bash
1.高并发,高性能
2.高可靠---可以7*24小时不间断运行
3.可扩展性强--模块化设计,使得添加模块非常的平稳。
4.热部署--可以在不停止服务器的情况下升级nginx
5.BSD许可证--nginx不止开源免费的我们还可以更具实际需求进行定制修改源代码
```
**Nginx 安装非常的简单配置文件非常简洁Bugs非常少的服务器**: Nginx 启动特别容易,并且几乎可以做到**7*24**不间断运行,即使运行数个月也不需要重新启动。还能够在不间断服务的情况下进行软件版本的升级。
**作为 Web 服务器**:相比 ApacheNginx 使用更少的资源,支持更多的并发连接,能够支持高达 50,000 个并发连接数的响应。
**作为负载均衡服务器**可以进行自定义配置支持虚拟主机支持URL重定向支持网络监控支持流媒体传输等。
**作为邮件代理服务器**: Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮件代理服务器)。
### IO多路复用
#### 1、I/O multiplexing多并发
**传统的多进程并发模型**
每进来一个新的I/O流会分配一个新的线程管理。
![1561897144109](assets/1561897144109.png)
**I/O多路复用**
单个线程通过记录跟踪每个I/O流(sock)的状态来同时管理多个I/O流 。
```bash
I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态来同时管理多个I/O流。发明它的原因是尽量多的提高服务器的吞吐能力。在同一个线程里面 通过拨开关的方式来同时传输多个I/O流
```
![1561897166658](assets/1561897166658.png)
#### 2、Nginx实现I/O多路复用的方式
Nginx基于事件驱动模型实现I/O多路复用
**select, poll, epoll**都是I/O多路复用的具体的实现他们的出现是有先后顺序的。 I/O多路复用这个概念被提出来以后 相继出现了多个方案但是都需要linux内核支持
select是第一个实现 (1983 左右实现的)。 select 被实现以后,很快就暴露出了很多问题。
```bash
select 任何一个sock(I/O stream)出现了数据select 仅仅会返回但是并不会告诉你是那个sock上有数据于是你只能自己一个一个的找10几个sock可能还好要是几万的sock每次都找一遍...
select 只能监视1024个链接。
select 线程不是安全的。
```
于是14年以后(1997年一帮人又实现了**poll,** poll 修复了select的很多问题比如
```bash
poll 去掉了1024个链接的限制于是可以有多个连接进来。
但是poll仍然线程不是安全的 这就意味着不管服务器有多强悍也只能在一个线程里面处理一组I/O流。
```
epoll 是I/O 多路复用最新的一个实现epoll 修复了poll 和select绝大部分问题, 比如:
```bash
epoll 现在是线程安全的。
epoll 现在不仅告诉你sock组里面数据还会告诉你具体哪个sock有数据你不用自己去找了。
ngnix 会有很多连接进来, 默认采用epoll会把他们都监视起来然后像拨开关一样谁有数据就拨向谁然后调用相应的代码处理。
```
#### 3、异步非阻塞
```bash
# pstree |grep nginx
|-+= 81666 root nginx: master process nginx
\--- 82500 nobody nginx: worker process
\--- 82501 nobody nginx: worker process
```
1个master进程n个work进程
每进来一个request会有一个worker进程去处理。但不是全程的处理处理到什么程度呢处理到可能发生阻塞的地方比如向上游后端服务器转发request并等待请求返回。那么这个处理的worker不会这么一直等着他会在发送完请求后注册一个事件“如果upstream返回了告诉我一声我再接着干”。于是他就休息去了。这就是异步。此时如果再有request进来他就可以很快再按这种方式处理。这就是非阻塞和IO多路复用。而一旦上游服务器返回了就会触发这个事件worker才会来接手这个request才会接着往下走。这就是异步回调。
### Nginx vs Apache
内核和功能上的比较
| 特性 | Nginx | Apache |
| :------- | :-------------------------------------------------------- | :----------------------------------------------------------- |
| 请求管理 | 事件驱动模型使用异步套接字处理占用较少的内存和CPU开销 | 同步套接字、进程和线程每个请求都要使用一个单独的进程或线程,使用同步套接字 |
| 设计语言 | C | C、C++ |
| 可移植性 | 多平台 | 多平台 |
| 诞生时间 | 2002 | 1994 |
一般功能比较
| 功能 | Nginx | Apache |
| --------- | ------------- | ---------------- |
| HTTPS支持 | 作为模块支持 | 作为模块支持 |
| 虚拟主机 | 原生支持 | 原生支持 |
| CGI支持 | 仅支持FastCGI | 支持CGI和FastCGI |
| 系统模块 | 静态模块系统 | 动态模块系统 |
```bash
FastCGI
快速通用网关接口Fast Common Gateway InterfaceFastCGI是通用网关接口CGI的改进描述了客户端和服务器程序之间传输数据的一种标准。FastCGI致力于减少Web服务器与CGI程序之间互动的开销从而使服务器可以同时处理更多的Web请求。与为每个请求创建一个新的进程不同FastCGI使用持续的进程来处理一连串的请求。这些进程由FastCGI进程管理器管理而不是web服务器。
```
从以上功能上的对比我们很难发现哪些功能Apache无法实现。那我们为什么更喜欢用Nginx 呢Nginx 相对Apache 有那些有优点呢?
```bash
- 轻量级同样是web服务比Apache占用更少的内存及资源
- 静态处理Nginx 静态处理性能比 Apache 高 3倍以上
- 抗并发Nginx 处理请求是异步非阻塞的而Apache则是阻塞型的。在高并发下Nginx能保持低资源低消耗高性能。
- 高度模块化的设计,编写模块相对简单
```
1. 并发量
apache 200 - 2000
nginx 50000 20000 8000
100000/86400
2. 稳定性
3. 功能
### Nginx安装
#### 1、Yum方式安装
nginx的官方网站<http://www.nginx.org/>
Nginx现在属于f5
f5做硬件负载均衡器
**Nginx版本**
```bash
Mainline version 主线版
Stable version 最新稳定版,生产环境上建议使用的版本
Legacy versions 遗留的老版本的稳定版
```
**配置Yum源**
官方download页面
![image-20220613141023470](assets/image-20220613141023470.png)
Install the prerequisites:
> ```bash
> # yum install yum-utils -y
> ```
To set up the yum repository, create the file named `/etc/yum.repos.d/nginx.repo` with the following contents:
> ```bash
> [nginx-stable]
> name=nginx stable repo
> baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
> gpgcheck=1
> enabled=1
> gpgkey=https://nginx.org/keys/nginx_signing.key
> module_hotfixes=true
>
> [nginx-mainline]
> name=nginx mainline repo
> baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
> gpgcheck=1
> enabled=0
> gpgkey=https://nginx.org/keys/nginx_signing.key
> module_hotfixes=true
> ```
By default, the repository for stable nginx packages is used. If you would like to use mainline nginx packages, run the following command:
> ```bash
> # yum-config-manager --enable nginx-mainline
> ```
To install nginx, run the following command:
> ```bash
> # yum install nginx -y
> ```
查看详细安装信息
```bash
# nginx -V
# nginx -v
-V 查看详细安装信息
-v 单独查看版本信息
```
启动并设置开机启动
```bash
注意防火墙和selinux
# systemctl start nginx
# systemctl enable nginx
```
浏览器测试访问
```http
http://localhost
```
#### 2、编译方式安装
安装编译环境
```bash
# yum -y install gcc gcc-c++ pcre pcre-devel gd-devel openssl-devel zlib zlib-devel
pcre使nginx支持http rewrite模块
openssl 使nginx支持ssl
zlib提供nginx对http包的内容进行gzip压缩
```
创建用户nginx
```bash
# useradd nginx
```
下载安装Nginx
![image-20220613145148325](assets/image-20220613145148325.png)
查询下载链接
```bash
# wget http://nginx.org/download/nginx-1.22.0.tar.gz
# tar xzf nginx-1.22.0.tar.gz -C /usr/local/
# cd /usr/local/nginx-1.22.0/
# ./configure --prefix=/usr/local/nginx --group=nginx --user=nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/tmp/nginx/client_body --http-proxy-temp-path=/tmp/nginx/proxy --http-fastcgi-temp-path=/tmp/nginx/fastcgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-pcre --with-http_realip_module --with-stream
# make && make install
```
Nginx 编译参数
```bash
# 查看 nginx 安装的模块
#/usr/local/nginx/sbin/nginx -V
--prefix=/usr/local/nginx //指向安装目录
--conf-path=/etc/nginx/nginx.conf //指定配置文件
--http-log-path=/var/log/nginx/access.log //指定访问日志
--error-log-path=/var/log/nginx/error.log //指定错误日志
--lock-path=/var/lock/nginx.lock //指定lock文件
--pid-path=/run/nginx.pid //指定pid文件
--http-client-body-temp-path=/var/lib/nginx/body //设定http客户端请求临时文件路径
--http-fastcgi-temp-path=/var/lib/nginx/fastcgi //设定http fastcgi临时文件路径
--http-proxy-temp-path=/var/lib/nginx/proxy //设定http代理临时文件路径
--http-scgi-temp-path=/var/lib/nginx/scgi //设定http scgi临时文件路径
--http-uwsgi-temp-path=/var/lib/nginx/uwsgi //设定http uwsgi临时文件路径
--with-debug //启用debug日志
--with-ipv6 //启用ipv6支持
--with-http_ssl_module //启用ssl支持
--with-http_stub_status_module //获取nginx自上次启动以来的状态
--with-http_realip_module //允许从请求标头更改客户端的IP地址值默认为关
--with-http_auth_request_module //实现基于一个子请求的结果的客户端授权。如果该子请求返回的2xx响应代码所述接入是允许的。如果它返回401或403中访问被拒绝与相应的错误代码。由子请求返回的任何其他响应代码被认为是一个错误。
--with-http_addition_module //作为一个输出过滤器,支持不完全缓冲,分部分响应请求
--with-http_dav_module //增加PUT,DELETE,MKCOL创建集合,COPY和MOVE方法 默认关闭,需编译开启
--with-http_geoip_module //使用预编译的MaxMind数据库解析客户端IP地址得到变量值
--with-http_gunzip_module //它为不支持“gzip”编码方法的客户端解压具有“Content-Encoding: gzip”头的响应。
--with-http_gzip_static_module //在线实时压缩输出数据流
--with-http_spdy_module //SPDY可以缩短网页的加载时间
--with-http_sub_module //允许用一些其他文本替换nginx响应中的一些文本
--with-http_xslt_module //过滤转换XML请求
--with-mail //启用POP3/IMAP4/SMTP代理模块支持
--with-mail_ssl_module //启用ngx_mail_ssl_module支持启用外部模块支持
#--with 表示在编译过程中需要给nginx添加的模块
#--without 表示编译nginx时默认该模块是添加进去的当使用这个参数时表示将默认编译的模块移除
```
**修改配置文件**
**nginx.conf的组成**
nginx.conf一共由三部分组成分别为全局块、events块、http块。
在http块中又包含http全局块、多个server块。
每个server块中又包含server全局块以及多个location块。
```bash
# vim /etc/nginx/nginx.conf
# 全局参数设置
user nginx;
worker_processes 4; #设置nginx启动进程的数量一般设置成与逻辑cpu数量相同
error_log logs/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024; #设置一个进程的最大并发连接数
}
# http 服务相关设置
http {
include mime.types; #关联资源的媒体类型
default_type application/octet-stream; #根据文件的后缀来匹配相应的MIME类型
log_format main 'remote_addr - remote_user [time_local] "request" '
'status body_bytes_sent "$http_referer" '
'"http_user_agent" "http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on; #用于开启文件高效传输模式一般设置为on若nginx是用来进行磁盘IO负载应用时可以设置为off降低系统负载
tcp_nopush on; # 减少网络报文段数量,当有数据时,先别着急发送, 确保数据包已经装满数据, 避免了网络拥塞
tcp_nodelay on; # 提高I/O性能确保数据尽快发送, 提高可数据传输效率
gzip on; #是否开启gzip压缩
keepalive_timeout 65; #设置长连接的超时时间,请求完成之后还要保持连接多久,
# 虚拟服务器的相关设置
server {
listen 80;
server_name localhost; #设置绑定的主机名、域名或ip地址
charset koi8-r; # 设置编码字符
location / {
root /var/www/nginx; #设置服务器默认网站的根目录位置,需要手动创建
index index.html index.htm; #设置默认打开的文档
}
error_page 500 502 503 504 /50x.html; #设置错误信息返回页面
location = /50x.html {
root html; #这里的绝对位置是/usr/local/nginx/html
}
}
}
```
检测Nginx配置文件语法
```shell
# mkdir -p /tmp/nginx //创建临时测试数据目录
# /usr/local/nginx/sbin/nginx -t
```
Nginx 服务管理
```bash
# /usr/local/nginx/sbin/nginx //启动服务
# nginx -c /path/nginx.conf # 以特定目录下的配置文件启动nginx:
# nginx -s reload # 修改配置后重新加载生效
# nginx -s stop # 快速停止nginx
# nginx -s quit # 正常停止nginx
# nginx -t # 测试当前配置文件是否正确
# nginx -t -c /path/to/nginx.conf # 测试特定的nginx配置文件是否正确
#注意
nginx -s reload 命令加载修改后的配置文件,命令下达后发生如下事件
1. Nginx的master进程检查配置文件的正确性若是错误则返回错误信息nginx继续采用原配置文件进行工作因为worker未受到影响
2. Nginx启动新的worker进程采用新的配置文件
3. Nginx将新的请求分配新的worker进程
4. Nginx等待以前的worker进程的全部请求已经都返回后关闭相关worker进程
5. 重复上面过程知道全部旧的worker进程都被关闭掉
```
### Nginx 虚拟主机
服务器配置比较高 访问量不大
可以用一个nginx提供多个网站服务 每一个网站都是一个虚拟主机
虚拟机
虚拟主机 提供一个独立的网站 客户端连接的时候服务器看起来像一台主机,实际不是
紧紧是web服务器提供的一个能发布网站的目录
每个web服务器可以同时发布多个网站每个网站都是一个虚拟主机在服务器上是目录形式存在
云主机
vps虚拟专用服务器)
虚拟机 vps 云主机
虚拟主机
nginx apache 配置的一个能发布网站的环境
虚拟主机是一种特殊的软硬件技术它可以将网络上的每一台计算机分成多个虚拟主机每个虚拟主机可以独立对外提供www服务这样就可以实现一台主机对外提供多个web服务每个虚拟主机之间是独立的互不影响。
![1561605672295](assets/1561605672295.png)
Nginx支持三种类型的虚拟主机
```bash
1、基于域名的虚拟主机 server_name来区分虚拟主机——应用外部网站
2、基于ip的虚拟主机 一块主机绑定多个ip地址
3、基于端口的虚拟主机 (端口来区分虚拟主机——应用:公司内部网站,外部网站的管理后台)
```
#### 基于域名的虚拟主机
1、配置通过域名区分的虚拟机
```bash
# cat /etc/nginx/nginx.conf
worker_processes 4;
#error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
server {
listen 80;
server_name web.testpm.com;
location / {
root /var/www/nginx/;
index index.html index.htm;
limit_rate 2k;
}
}
server {
listen 80;
server_name www.1000phone.com;
location / {
root /1000phone/html;
index index.html index.htm;
}
}
}
```
2、 为域名为 web.1000phone.com 的虚拟机,创建 index 文件
```bash
[root@localhost ~]# mkdir -p /1000phone/html
[root@localhost ~]# vim /1000phone/html/index.html
<html>
<p>
this is my 1000phone
</p>
</html>
```
3、重新加载配置文件
```bash
# /usr/local/nginx/sbin/nginx -s reload
```
4、客户端配置路由映射
```bash
在 C:\Windows\System32\drivers\etc\hosts 文件中添加两行(linux:/etc/hosts)
```
```bash
10.0.105.199 web.testpm.com
10.0.105.199 web.1000phone.com
```
5、 测试访问
```bash
浏览器输入http://web.testpm.com/
浏览器输入http://web.1000phone.com/
```
#### 基于ip的虚拟主机
```bash
[root@localhost ~]# ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: ens33: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:0c:29:17:f1:af brd ff:ff:ff:ff:ff:ff
inet 10.0.105.199/24 brd 10.0.105.255 scope global dynamic ens33
valid_lft 81438sec preferred_lft 81438sec
inet6 fe80::9d26:f3f0:db9c:c9be/64 scope link
valid_lft forever preferred_lft forever
[root@localhost ~]# ifconfig ens33:1 10.0.105.201/24
[root@localhost ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.105.199 netmask 255.255.255.0 broadcast 10.0.105.255
inet6 fe80::9d26:f3f0:db9c:c9be prefixlen 64 scopeid 0x20<link>
ether 00:0c:29:17:f1:af txqueuelen 1000 (Ethernet)
RX packets 9844 bytes 1052722 (1.0 MiB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 5567 bytes 886269 (865.4 KiB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
ens33:1: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.0.105.201 netmask 255.255.255.0 broadcast 10.0.105.255
ether 00:0c:29:17:f1:af txqueuelen 1000 (Ethernet)
2、配置通过ip区分的虚拟机
# cat /etc/nginx/nginx.conf
user root;
worker_processes 4;
#error_log logs/error.log;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
server {
listen 10.0.105.199:80;
server_name web.testpm.com;
location / {
root /var/www/nginx/;
index index.html index.htm;
limit_rate 2k;
}
server {
listen 10.0.105.201:80;
server_name www.testpm.com;
location / {
root /1000phone/html/;
index index.html index.htm;
}
}
}
3、重新加载配置文件
# /usr/local/nginx/sbin/nginx -s reload
4、 测试访问
浏览器输入http://10.0.105.199
浏览器输入http://10.0.105.201
5、补充
-- 删除绑定的vip
[root@localhost ~]# ifconfig ens33:1 10.0.105.201/24 down
重启一下nginx
[root@localhost ~]# systemctl restart nginx
```
#### 基于端口的虚拟主机
```bash
# cat /etc/nginx/nginx.conf
user root;
worker_processes 4;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
sendfile on;
keepalive_timeout 65;
server {
listen 80;
server_name web.testpm.com;
location / {
root /var/www/nginx/;
index index.html index.htm;
limit_rate 2k;
}
server {
listen 8080;
server_name www.testpm.com;
location / {
root /1000phone/html/;
index index.html index.htm;
}
}
}
重新加载配置文件:
# /usr/local/nginx/sbin/nginx -s reload
测试访问:
浏览器输入http://web.testpm.com/
浏览器输入http://web.1000phone.com:8080
```
## 三、Nginx进阶
### Nginx 日志
`Nginx` 有一个非常灵活的日志记录模式,每个级别的配置可以有各自独立的访问日志, 所需日志模块为 `ngx_http_log_module` ,日志格式通过 `log_format` 命令来定义,日志对于统计和排错是非常有利的.`nginx` 日志相关的配置包括 `access_log`、`log_format`、`rewrite_log`、`error_log`。
#### **access_log指令**
作用域
可以应用`access_log`指令的作用域分别有`http``server``location`也就是说在这几个作用域外使用该指令Nginx会报错。
```shell
# 设置访问日志
access_log path; //path指定日志的存放位置。
# 关闭访问日志
access_log off;
```
```shell
access_log /var/logs/nginx-access.log
```
该例子指定日志的写入路径为`/var/logs/nginx-access.log`,日志格式使用默认的`main`。
#### **log-format指令**
Nginx 预定义了名为 "main"的日志格式,如果没有明确指定日志格式默认使用该格式:
```bash
log_format main 'remote_addr - remote_user [time_local] "request" '
'status body_bytes_sent "$http_referer" '
'"http_user_agent" "http_x_forwarded_for"';
```
![1561599608585](assets/1561599608585.png)
![1561599718230](assets/1561599718230.png)
如果不想使用Nginx预定义的格式可以通过`log_format`指令来自定义。
语法
```shell
log_format name [escape=default|json] string ...;
```
```bash
- name 格式名称。在 access_log 指令中引用。
- escape 设置变量中的字符编码方式是`json`还是`default`,默认是`default`。
- string 要定义的日志格式内容。该参数可以有多个。参数中可以使用Nginx变量。
```
`log_format` 指令中常用的一些变量:
```shell
$remote_addr, $http_x_forwarded_for #记录客户端IP地址
$remote_user #记录客户端用户名称
$request #记录请求的URL和HTTP协议
$status #记录请求状态
$body_bytes_sent #发送给客户端的字节数,不包括响应头的大小
$bytes_sent #发送给客户端的总字节数
$connection #连接的序列号
$connection_requests #当前通过一个连接获得的请求数量
$msec #日志写入时间。单位为秒,精度是毫秒。
$http_referer #记录从哪个页面链接访问过来的,可以根据该参数进行防盗链设置
$http_user_agent #记录客户端浏览器相关信息
$request_length #请求的长度(包括请求行,请求头和请求正文)。
$request_time #请求处理时间,单位为秒,精度毫秒; 从读入客户端的第一个字节开始,直到把最后一个字符发送给客户端后进行日志写入为止。
$time_iso8601 #ISO8601标准格式下的本地时间
$time_local #通用日志格式下的本地时间
```
自定义日志格式的使用:
```shell
access_log /var/logs/nginx-access.log main
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
```
```shell
注意:
如果Nginx位于负载均衡器nginx反向代理之后web服务器无法直接获取到客户端真实的IP地址。
$remote_addr获取的是反向代理的IP地址。反向代理服务器在转发请求的http头信息中可以增加X-Forwarded-For信息用来记录客户端IP地址。
```
使用`log_format`指令定义了一个`main`的格式,并在`access_log`指令中引用了它。客户端发起请求访问:`http://192.168.246.154/`,看一下请求的日志记录:
```shell
10.0.105.207 - - [01/Jul/2019:10:44:36 +0800] "GET / HTTP/1.1" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "-"
```
我们看到最终的日志记录中`$remote_user`、`$http_referer`、`$http_x_forwarded_for`都对应了一个`-`,这是因为这几个变量为空。
**面试时注意日志里面的ip地址一定在第一列。**
#### **error_log 指令**
错误日志在Nginx中是通过`error_log`指令实现的。该指令记录服务器和请求处理过程中的错误信息。
**语法**
配置错误日志文件的路径和日志级别。
```shell
error_log file [level];
Default:
error_log logs/error.log error;
```
`file` 参数指定日志的写入位置。
`level` 参数指定日志的级别。level可以是`debug`, `info`, `notice`, `warn`, `error`, `crit`, `alert`,`emerg`中的任意值。可以看到其取值范围是按紧急程度从低到高排列的。只有日志的错误级别等于或高于level指定的值才会写入错误日志中。默认值是`error`。
```shell
日志级别
debug低级别包含的信息非常详细
info稍微的高一点了。用的多一些。
notice相当于提示
warning警告 和warn 一样
err和error 一样。
crit比较严重了
alert告警很严重
emerg 恐慌级别, 级别最高的
```
**基本用法**
```shell
error_log /var/logs/nginx/nginx-error.log emerg;
```
配置段: `http`, `server`, `location`作用域。
例子中指定了错误日志的路径为:`/var/logs/nginx/nginx-error.log`,日志级别使用默认的 `error`
#### **rewrite_log 指令**
由`ngx_http_rewrite_module`模块提供的。用来记录重写日志的。对于调试重写规则建议开启,启用时将在`error log`中记录`notice`级别的重写日志。
**基本语法:**
```shell
rewrite_log on | off;
默认值:
rewrite_log off;
```
配置段: `http`, `server`, `location`, `if`作用域。
**nginx 日志配置总结**
Nginx中通过`access_log`和`error_log`指令配置访问日志和错误日志,通过`log_format`我们可以自定义日志格式。
详细的日志配置信息可以参考[Nginx官方文档](https://link.juejin.im/?target=http%3A%2F%2Fnginx.org%2Fen%2Fdocs%2Fvarindex.html)
**单独开启server的访问日志**
```shell
[root@nginx-client ~]# cd /etc/nginx/conf.d/
[root@nginx-client conf.d]# vim nginx.conf
server {
listen 80;
server_name localhost;
charset koi8-r;
access_log /var/log/nginx/test.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /admin {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
# nginx -s reload
访问
# curl -I http://192.168.1.10
```
![1567612056482](assets/1567612056482.png)
当我们访问的这个server的时候日志将会输出到test.access.log.
![1567612351854](assets/1567612351854.png)
#### Nginx日志轮转
```shell
[root@192 ~]# rpm -ql nginx |grep log
/etc/logrotate.d/nginx
/var/log/nginx
[root@192 ~]# vim /etc/logrotate.d/nginx
/var/log/nginx/*.log { #指定需要轮转处理的日志文件
daily #日志文件轮转周期,可用值为: daily/weekly/yearly
missingok #忽略错误信息
rotate 7 #轮转次数即最多存储7个归档日志会删除最久的归档日志
minsize 5M #限制条件大于5M的日志文件才进行分割否则不操作
dateext #以当前日期作为命名格式
compress #轮循结束后已归档日志使用gzip进行压缩
delaycompress #与compress共用,最近的一次归档不要压缩
notifempty # 日志文件为空,轮循不会继续执行
create 640 nginx nginx #新日志文件的权限
sharedscripts #有多个日志需要轮询时,只执行一次脚本
postrotate #将日志文件转储后执行的命令。以endscript结尾命令需要单独成行
if [ -f /var/run/nginx.pid ]; then #判断nginx的PID。默认logrotate会以root身份运行
kill -USR1 cat /var/run/nginx.pid
fi
endscript
}
执行命令:
# /usr/sbin/logrotate -f /etc/logrotate.conf
创建计划任务:
# crontab -e
59 23 * * * /usr/sbin/logrotate -f /etc/logrotate.conf
```
### Nginx Proxy 反向代理
![1561616855038](assets/1561616855038.png)
#### 正向代理
**正向代理的过程隐藏了真实的请求客户端,服务器不知道真实的客户端是谁,客户端请求的服务都被代理服务器代替请求。**我们常说的代理也就是正向代理正向代理代理的是请求方也就是客户端比如我们要访问youtube可是不能访问只能先安装个FQ软件代你去访问通过FQ软件才能访问FQ软件就叫作正向代理。
![image-20200907163034923](assets/image-20200907163034923.png)
#### 反向代理
**反向代理的过程隐藏了真实的服务器,客户不知道真正提供服务的是谁,客户端请求的服务都被代理服务器处理。反向代理代理的是响应方,也就是服务端;**我们请求www.baidu.com时这www.baidu.com就是反向代理服务器真实提供服务的服务器有很多台反向代理服务器会把我们的请求分转发到真实提供服务的各台服务器。Nginx就是性能非常好的反向代理服务器
![1561617154985](assets/1561617154985.png)
![image-20200907163302135](assets/image-20200907163302135.png)
两者的区别在于代理的对象不一样
```bash
正向代理中代理的对象是客户端。
反向代理中代理的对象是服务端。
```
知识扩展HTTP Server和Application Server的区别和联系
```bash
HTTP ServerNginx/Apache常用做静态内容服务和代理服务器将外来请求转发给后面的应用服务tomcatjboss,php等
应用服务器(tomcat/jboss/php)是动态服务器Application Server
```
#### Nginx Proxy 配置
模块ngx_http_proxy_module
a、nginx-1 作为应用服务器ip地址为:10.0.105.199
b、nginx-2 作为代理服务器ip地址为:10.0.105.202
```shell
nginx-2的配置文件:
# vim /etc/nginx/conf.d/default.conf
http {
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://10.0.105.199:80; #nginx-1地址真实后端服务器地址可以是ip或域名
proxy_redirect default;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 30;
proxy_send_timeout 60;
proxy_read_timeout 60;
}
}
}
```
```shell
proxy_redirect 如果真实服务器使用的是的真实IP:非默认端口。则改成IP默认端口。
proxy_set_header重新定义或者添加发往后端服务器的请求头
在有多层代理的情况下通过下面两个选项可以记录真正客户端机器的ip地址
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout后端服务器连接的超时时间发起三次握手等候响应超时时间
proxy_send_timeout后端服务器数据回传时间就是在规定时间之内后端服务器必须传完所有的数据
proxy_read_timeout nginx接收upstream上游/真实) server数据超时, 默认60s, 如果连续的60s内没有收到1个字节, 则连接关闭。像长连接
```
```bash
使用PC客户端访问nginx-2服务器地址
浏览器中输入http://10.0.105.202成功则访问到的是nginx-1服务器页面
```
观察nginx-1服务器的日志
```shell
10.0.105.202 - - [27/Jun/2019:15:54:17 +0800] "GET / HTTP/1.0" 304 0 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36" "10.0.105.207"
```
```bash
10.0.105.202 代理服务器地址
10.0.105.207 客户机地址。
访问成功。 记录了客户机的IP和代理服务器的IP
```
#### **proxy_redirect解析**
```bash
环境:
代理服务ip192.168.26.142
真实服务ip192.168.26.143
代理服务器配置如下:
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#location / {
# root html;
# index index.html index.htm;
#}
location / {
proxy_pass http://192.168.26.143:8080;
proxy_redirect off;
}
问题描述:
1.端口为非默认80端口
2.被访问地址为子目录
3.客户端访问的URL地址最后没有加/
客户端访问后则会出现如下响应头信息且访问出错
错误信息:
# curl http://192.168.26.142/test
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>nginx/1.22.0</center>
</body>
</html>
响应头信息:
# curl -I http://192.168.26.142/test
HTTP/1.1 301 Moved Permanently
Server: nginx/1.22.0
Date: Mon, 13 Jun 2022 10:22:36 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://192.168.26.143:8080/test/
```
**问题所在:**
响应头信息带 http://192.168.26.143:8080/test/
**这里location为带有后端服务器实际地址跟端口的响应头信息这样在实际线上会暴露后端服务器的信息所以是不允许的。**
**解决:**
通过proxy_redirect将被代理服务器的响应头中的location字段进行修改后返回给客户端**注意此配置不会解决错误**但是错误响应头信息发生了变化返回客户端的地址变成了http://192.168.26.142/test/
```bash
server {
listen 80;
server_name localhost;
location / {
proxy_pass http://192.168.26.143:8080;
#proxy_redirect http://192.168.26.143:8080/test/ http://192.168.26.142/test/;
proxy_redirect ~^ http://192.168.26.143:8080(.*) http://192.168.26.142$1;
}
```
结果如下:
```
# curl -I http://192.168.26.142/test
HTTP/1.1 301 Moved Permanently
Server: nginx/1.22.0
Date: Mon, 13 Jun 2022 10:44:00 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: http://192.168.26.142/test/
```
### Nginx负载均衡
haproxy 专业负载均衡
lvs负载均衡和nginx负载均衡的区别
**upstream模块**
upstream 配置的是一组被代理的服务器地址+负载均衡算法
```shell
upstream testapp {
ip_hash;
server 10.0.105.199:8081 weight=1;
server 10.0.105.202:8081 weight=2;
}
server {
....
location / {
proxy_pass http://testapp; #请求转向 testapp 定义的服务器列表
}
```
**负载均衡算法**
```bash
upstream支持4种负载均衡调度算法
1、轮询(默认):每个请求按时间顺序逐一分配到不同的后端服务器; 加权轮询 轮叫
2、ip_hash:每个请求按访问IP的hash结果分配同一个IP客户端固定访问一个后端服务器。可以保证来自同一ip的请求被打到固定的机器上可以解决session问题。
3、url_hash:按访问url的hash结果来分配请求使每个url定向到同一个后端服务器。
4、fair:这是比上面两个更加智能的负载均衡算法。此种算法可以依据页面大小和加载时间长短智能地进行负载均衡也就是根据后端服务器的响应时间来分配请求响应时间短的优先分配。Nginx本身是不支持 fair的如果需要使用这种调度算法必须下载Nginx的 upstream_fair模块。
```
**配置实例**
1、热备如果你有2台服务器当一台服务器发生事故时才启用第二台服务器给提供服务。服务器处理请求的顺序AAAAAA突然A挂啦BBBBBBBBBBBBBB.....
```shell
upstream myweb {
server 172.17.14.2:8080;
server 172.17.14.3:8080 ; #热备
}
```
2、轮询nginx默认就是轮询其权重都默认为1服务器处理请求的顺序ABABABABAB....
```shell
upstream myweb {
server 172.17.14.2:8080;
server 172.17.14.3:8080;
}
```
3、加权轮询跟据配置的权重的大小而分发给不同服务器不同数量的请求。如果不设置则默认为1。下面服务器的请求顺序为ABBABBABBABBABB....
```shell
upstream myweb {
server 172.17.14.2:8080 weight=1;
server 172.17.14.3:8080 weight=2;
}
```
4、ip_hash:nginx会让相同的客户端ip请求相同的服务器。
```shell
upstream myweb {
server 172.17.14.2:8080;
server 172.17.14.3:8080;
ip_hash;
}
```
5、nginx负载均衡配置状态参数
```bash
- down表示当前的server暂时不参与负载均衡。
- backup预留的备份机器。当其他所有的非backup机器出现故障或者忙的时候才会请求backup机器因此这台机器的压力最轻。
- max_fails允许请求失败的次数默认为1。当超过最大次数时返回proxy_next_upstream 模块定义的错误。
- fail_timeout在经历了max_fails次失败后暂停服务的时间单位秒。max_fails可以和fail_timeout一起使用。
```
```shell
upstream myweb {
server 172.17.14.2:8080 weight=2 max_fails=2 fail_timeout=2;
server 172.17.14.3:8080 weight=1 max_fails=2 fail_timeout=1;
}
```
### 会话保持(sticky模块了解)
Nginx会话保持主要有以下几种实现方式。
1、ip_hash
```bash
ip_hash使用源地址哈希算法将同一客户端的请求总是发往同一个后端服务器除非该服务器不可用。
ip_hash语法
```
```shell
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
}
```
```bash
ip_hash简单易用但有如下问题
当后端服务器宕机后session会丢失
来自同一局域网的客户端会被转发到同一个后端服务器,可能导致负载失衡;
```
2、sticky_cookie_insert---是基于cookie实现
```bash
使用sticky_cookie_insert这会让来自同一客户端的请求被传递到一组服务器的同一台服务器。与ip_hash不同之处在于它不是基于IP来判断客户端的而是基于cookie来判断。(需要引入第三方模块才能实现)---sticky模块
```
语法:
```shell
编译安装sticky模块#给yum安装的nginx添加模块
[root@nginx-server ~]# yum install -y pcre* openssl* gcc gcc-c++ make 安装编译环境
[root@nginx-server ~]# wget https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/get/08a395c66e42.zip #下载sticky模块
[root@nginx-server ~]# nginx -v
nginx version: nginx/1.18.0
[root@nginx-server ~]# wget http://nginx.org/download/nginx-1.18.0.tar.gz #下载yum安装nginx对应版本的源码包
[root@nginx-server ~]# yum install -y unzip #安装解压工具
[root@nginx-server ~]# unzip 08a395c66e42.zip #解压模块包
[root@nginx-server ~]# mv nginx-goodies-nginx-sticky-module-ng-08a395c66e42/ nginx-sticky-module-ng/
[root@nginx-server ~]# tar xzvf nginx-1.18.0.tar.gz -C /usr/local/ #解压nginx的源码包
[root@nginx-server ~]# cd /usr/local/nginx-1.18.0/
[root@nginx-server nginx-1.18.0]# nginx -V #查看yum安装nginx所有模块
[root@nginx-server nginx-1.18.0]# ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-module=/root/nginx-sticky-module-ng
[root@nginx-server nginx-1.18.0]# make && make install
配置基于cookie会话保持
[root@nginx-server conf.d]# vim upstream.conf
upstream qfedu {
server 192.168.198.143;
server 192.168.198.145;
sticky;
}
[root@nginx-server conf.d]# vim proxy.conf
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;
location / {
proxy_pass http://qfedu;
}
}
[root@nginx-server conf.d]# nginx -t
[root@nginx-server conf.d]# nginx -s reload
或者:
upstream qfedu {
server 192.168.198.143;
server 192.168.198.145;
sticky expires=1h domain=testpm.com path=/;
}
说明:
expires设置浏览器中保持cookie的时间
domain定义cookie的域
path为cookie定义路径
```
浏览器测试访问
### Nginx 实现动静分离
为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。 简单来说,就是使用正则表达式匹配过滤,然后交给不同的服务器。
**1、准备环境**
准备一个nginx代理 两个http 分别处理动态和静态。
```ini
expires功能说明---(为客户端配置缓存时间)
  nginx缓存的设置可以提高网站性能对于网站的图片尤其是新闻网站图片一旦发布改动的可能是非常小的为了减小对服务器请求的压力提高用户浏览速度我们可以通过设置nginx中的expires让用户访问一次后将图片缓存在用户的浏览器中且时间比较长的缓存。
原理当nginx设置了expires后例如设置为expires 10d; 那么用户在10天内请求的时候都只会访问浏览器中的缓存而不会去请求nginx。
需要注意的是这种缓存方式只能在用户不对浏览器强制刷新的情况下生效如果用户通过url来进行访问是可以访问到缓存的。
```
1.静态资源配置
```shell
server {
listen 80;
server_name localhost;
location ~ \.(html|jpg|png|js|css) {
root /home/www/nginx;
expires 1d; #为客户端设置静态资源缓存时间
}
}
测试:
[root@nginx-yum2 conf.d]# curl -I http://10.0.105.200/test.jpg
HTTP/1.1 200 OK
Server: nginx/1.16.0
Date: Mon, 07 Sep 2019 11:35:08 GMT
Content-Type: image/jpeg
Content-Length: 27961
Last-Modified: Mon, 07 Sep 2019 11:31:17 GMT
Connection: keep-alive
ETag: "5f561a05-6d39"
Expires: Tue, 08 Sep 2019 11:35:08 GMT #缓存到期时间
Cache-Control: max-age=86400 #缓存持续时间秒
Accept-Ranges: bytes
```
2.动态资源配置
**centos7步骤如下**
```shell
yum 安装php7.1
[root@nginx-server ~] # rpm -Uvh https://mirror.webtatic.com/yum/el7/epel-release.rpm
[root@nginx-server ~] # rpm -Uvh https://mirror.webtatic.com/yum/el7/webtatic-release.rpm
[root@nginx-server ~] # yum install php71w-xsl php71w php71w-ldap php71w-cli php71w-common php71w-devel php71w-gd php71w-pdo php71w-mysql php71w-mbstring php71w-bcmath php71w-mcrypt -y
[root@nginx-server ~]# yum install -y php71w-fpm
```
**centos-stream 8因为软件比较新无需使用第三方库步骤如下**
```bash
# yum install php-cli php-fpm php-gd php-mysqlnd php-mbstring php-opcache php-pdo -y
只需要修改/etc/php-fpm.d/www.conf配置文件中的apache用户和组为nginx即可启动服务
# vim /etc/php-fpm.d/www.conf
....
user = nginx #默认是apache
group = nginx
....
# systemctl start php-fpm
# systemctl enable php-fpm
```
PHP-FPM(FastCGI Process ManagerFastCGI进程管理器)是一个PHP FastCGI管理器。它提供了更好的PHP进程管理方式可以有效控制内存和进程、可以平滑重载PHP配置。在日常运维中我们主要将PHP-FPM 的配置文件分为主配置文件和 pool配置文件(每个pool配置文件通常对应一个Nginx虚拟主机)。
1、PHP-FPM 主配置文件--主要是php的优化
**注意:本次安装不需要配置这些优化选项**
```ini
主配置文件 php-fpm.conf常用配置如下:
设置php-fpm最大进程数
[root@nginx-yum2 ~]# vim /etc/php-fpm.conf
[global]
...
;动态方式下开启的php-fpm进程的最大数量
process.max = 2048
;设置 fpm 在后台运行
daemonize = yes
; 设置进程可以打开的文件描述符数量
rlimit_files = 65535
; 设置FPM 的事件处理机制
events.mechanism = epoll
; 加载pool 配置
include = /etc/fpm.d/*.conf
设置进程池
[root@nginx-yum2 ~]# vim /etc/php-fpm.d/www.conf
[www]
...
; 设置动态dynamic进程池/静态static
pm = dynamic
; 设置每个进程可处理的请求数,当进程达到这个请求数量后会自动释放在重新生成新的进程。避免内存泄漏等情况
pm.max_requests = 1500
; 终止请求超时时间。一个请求若处理大于20s 则会自动kill掉。避免进程堆积
request_terminate_timeout = 20
; 限制 FPM 允许解析的脚本扩展名. 这里不限制FPM可以解析任何扩展名的文件
security.limit_extensions = ""
修改php上传文件的大小
[root@nginx-yum2 ~]# vim /etc/php.ini
max_execution_time = 0 #默认的该脚本最久执行时间为30秒.就是说超过30秒,该脚本就停止执行0为不限制时间
post_max_size = 150M #默认POST数据大小为8M,可以按实际情况修改
upload_max_filesize = 100M #默认上传文件最大为2M,可以按实际情况修改。
注:另外要说明的是,post_max_size 大于 upload_max_filesize 为佳
```
**动态服务器编辑nginx连接php**
```bash
编辑nginx的配置文件:
server {
listen 80;
server_name localhost;
location ~ \.php$ {
root /home/nginx/html; #指定网站目录
#fastcgi_pass 127.0.0.1:9000; #开启fastcgi连接php地址在centos8中yum默认为unix方式使用下面的配置
fastcgi_pass unix:/run/php-fpm/www.sock;
fastcgi_index index.php; #指定默认文件
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #站点根目录取决于root配置项
include fastcgi_params; #包含fastcgi使用的常量
}
}
```
**编辑php测试页面**
```php
# cat /home/nginx/html/index.php
<?php
phpinfo();
?>
```
**本地访问测试**
![image-20220613224341842](assets/image-20220613224341842.png)
```ini
通过 expires 指令设置的缓存,主要是针对客户端浏览器的。如果我们能将静态资源的缓存设置在服务器端,当多个用户访问同一个资源时,缓存命中率及系统的性能将大大提升。
proxy_cache介绍当nginx作为反向代理时通常只有动态的请求也就是不同的用户访问的同一个url看到的内容是不同的这个时候才会交由上游服务器处理但是有些内容可能是一段时间内是不会变化的这个时候为了减轻上游服务器的压力那么就让nginx把上游返回的内容缓存一段时间比如缓存一天在一天之内即是上游服务器内容发生了变化也不管nginx只返回缓存到的内容给用户。
proxy_cache--实现服务器端缓存,主要设置在反向代理上面
```
```bash
3.配置nginx反向代理upstream并实现服务器端缓存时间
upstream static {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=60s;
}
upstream php {
server 10.0.105.200:80 weight=1 max_fails=1 fail_timeout=60s;
}
proxy_cache_path /tmp/proxy_cache levels=1:2 keys_zone=proxy_cache:64m inactive=1d max_size=128m;
server {
listen 80;
server_name localhost
#动态资源加载
location ~ \.(php|jsp)$ {
proxy_pass http://php;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#静态资源加载
location ~ .*\.(html|jpg|png|css|js)$ {
proxy_pass http://static;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_cache proxy_cache; #配置设置的缓存空间名称
proxy_cache_valid 200 302 304 30d ; #根据响应码设置缓存时间超过这个时间即使缓存文件中有缓存数据nginx也会回源请求新数据。
proxy_cache_key $host$uri$is_args$args; #对不同用户的请求展示不同的内容
}
}
参数详解:
proxy_cache_path 缓存文件路径
levels 设置缓存文件目录层次levels=1:2 表示两级目录
keys_zone 设置缓存名字和共享内存大小.【在使用的地方要使用缓存名】
inactive 在指定缓存时间内没人访问则被删除
max_size 最大缓存空间,如果缓存空间满,默认覆盖掉缓存时间最长的资源。
```
```bash
当访问静态页面的时候location 匹配到 (html|jpg|png|js|css) 通过转发到静态服务器静态服务通过location的正则匹配来处理请求。
当访问动态页面时location匹配到 .\php 结尾的文件转发到后端php服务处理请求。
```
### location指令
Nginx 的 HTTP 配置主要包括三个区块,结构如下:
```nginx
http { # 协议块
  include mime.types;
  default_type application/octet-stream;
  keepalive_timeout 65;
  gzip on;
  server { # 服务块
      listen 80;
      server_name localhost;
      location / { # 请求块
          root html;
          index index.html index.htm;
      }
  }
}
浏览器-->地址栏-->URI -->http:// --> 主机:80 -->http协议块的配置 --> server --> location -->页面内容-->返回客户端浏览器
```
1、location 区段
```bash
- location 是在 server 块中配置,根据不同的 URI 使用不同的配置,来处理不同的请求。
- location 是有顺序的会根据不同请求配置的优先级来匹配的location 处理。
基本语法如下:
location [=|~|~*|^~|@] pattern{……}
```
2、location 前缀含义
```shell
= 表示精确匹配,优先级也是最高的
^~ 表示uri以某个常规字符串开头,理解为匹配url路径即可
~ 表示区分大小写的正则匹配
~* 表示不区分大小写的正则匹配
!~ 表示区分大小写不匹配的正则
!~* 表示不区分大小写不匹配的正则
/ 通用匹配,任何请求都会匹配到
@ 内部服务跳转
```
**查找顺序和优先级**
```shell
= 大于 ^~ 大于 ~|~* 大于 !~|!~* 大于 /
多个location配置的情况下匹配顺序为首先匹配 =,其次匹配^~, 其次是按正则匹配,最后是交给 / 通用匹配。当有匹配成功时候,停止匹配,按当前匹配规则处理请求。
Named Location (location @) 使用@符号定义的命名位置块不匹配URI前缀而是根据具体的用途或配置定义来执行内部重定向。它们通常不与请求URI的前缀直接匹配因此不涉及前缀匹配的优先级。
```
3、location 配置示例
1、没有修饰符 表示:必须以指定模式开始
```nginx
server {
listen 80;
server_name localhost;
location /abc {
root /home/www/nginx;
index 2.html;
}
那么,如下是对的:
http://192.168.1.9/abc
```
2、=表示:必须与指定的模式精确匹配
```nginx
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/http_access.log main;
location / {
root /usr/share/nginx/html;
index a.html index.htm;
}
location = / {
root /usr/share/nginx/html;
index b.html index.htm;
}
}
测试:
http://192.168.1.9
=/
http://192.168.1.9/a.html
/
```
3、~ 表示:指定的正则表达式要区分大小写
```nginx
server {
server_name localhost;
  location ~ /ab* {
root /home/www/nginx;
index 2.html index.html;
}
}
测试访问:
http://192.168.1.9/abc
不正确的
http://192.168.1.9/ABC
========================================
如果将配置文件修改为
location ~ /ABC {
root /home/www/nginx;
index 2.html index.html;
}
在创建目录和文件:
[root@ansible-server html]# cd /home/www/nginx/
[root@ansible-server nginx]# mkdir ABC
[root@ansible-server nginx]# vim ABC/2.html
访问:
http://192.168.1.9/ABC/
结论:~ 需要区分大小写。而且目录需要根据大小写定义。
```
4、^~和~*匹配案例
```nginx
location ^~ /static/ {
root /usr/share/nginx/html;
index f.html;
}
location ~* .jpg$ {
root /usr/share/nginx/html; #上传图片到发布目录中
}
浏览器访问:
http://192.168.198.144/static/
http://192.168.198.144/test.jpg
```
```nginx
location 区段匹配示例
location = / {
  # 只匹配 / 的查询.
  [ configuration A ]
}
location / {
  # 匹配任何以 / 开始的查询,但是正则表达式与一些较长的字符串将被首先匹配。
  [ configuration B ]
}
location ^~ /images/ {
  # 匹配任何以 /images/ 开始的查询并且停止搜索,不检查正则表达式。
  [ configuration C ]
}
location ~* \.(gif|jpg|jpeg)$ {
  # 匹配任何以gif, jpg, or jpeg结尾的文件
  [ configuration D ]
}
各请求的处理如下例:
/ → configuration A
/documents/document.html → configuration B
/images/1.gif → configuration C
/documents/1.jpg → configuration D
```
### nginx 地址重写 rewrite
**什么是Rewrite**
**Rewrite对称URL Rewrite即URL重写就是把传入Web的请求重定向到其他URL的过程**
- URL Rewrite最常见的应用是URL伪静态化是将动态页面显示为静态页面方式的一种技术。比如http://www.123.com/news/index.php?id=123 使用URLRewrite 转换后可以显示为 http://www.123.com/news/123.html
- 从安全角度上讲如果在URL中暴露太多的参数无疑会造成一定量的信息泄漏可能会被一些黑客利用对你的系统造成一定的破坏所以静态化的URL地址可以给我们带来更高的安全性。
- 实现网站地址跳转例如用户访问360buy.com将其跳转到jd.com。例如当用户访问tianyun.com的
80端口时将其跳转到443端口。
**Rewrite 相关指令**
相关指令有 if、rewrite、set、return
**if 语句**
- 应用环境
```shell
serverlocation
```
语法:
```shell
if (condition) { … }
if 可以支持如下条件判断匹配符号
~ 正则匹配 (区分大小写)
~* 正则匹配 (不区分大小写)
!~ 正则不匹配 (区分大小写)
!~* 正则不匹配 (不区分大小写)
-f 和!-f 用来判断是否存在文件
-d 和!-d 用来判断是否存在目录
-e 和!-e 用来判断是否存在文件或目录
-x 和!-x 用来判断文件是否可执行
在匹配过程中可以引用一些Nginx的全局变量
$args 请求中的参数;
$document_root 针对当前请求的根路径设置值;
$host 请求信息中的"Host"如果请求中没有Host行则等于设置的服务器名;
$limit_rate 对连接速率的限制;
$request_method 请求的方法,比如"GET"、"POST"等;
$remote_addr 客户端地址;
$remote_port 客户端端口号;
$remote_user 客户端用户名,认证用;
$request_filename 当前请求的文件路径名(带网站的主目录/usr/local/nginx/html/images/a.jpg
$request_uri 当前请求的文件路径名(不带网站的主目录/images/a.jpg
$query_string 与$args相同;
$scheme 用的协议比如http或者是https
$server_protocol 请求的协议版本,"HTTP/1.0"或"HTTP/1.1";
$server_addr 服务器地址如果没有用listen指明服务器地址使用这个变量将发起一次系统调用以取得地址(造成资源浪费);
$server_name 请求到达的服务器名;
$document_uri 与$uri一样URI地址;
$server_port 请求到达的服务器端口号;
```
**Rewrite flag**
**rewrite** 指令根据表达式来重定向URI或者修改字符串。可以应用于**server,location, if**环境下每行rewrite指令最后跟一个flag标记支持的flag标记有
rewrite 正则 替换之后的内容 flag
```shell
last 相当于Apache里的[L]标记表示完成rewrite。默认为last。last 标记在本条 rewrite 规则执行完后,会对其所在的 server { … } 标签重新发起请求;
break 本条规则匹配完成后终止匹配不再做后续的匹配使用proxy_pass指令时,则必须使用break。
redirect 返回302临时重定向浏览器地址会显示跳转后的URL地址
permanent 返回301永久重定向浏览器地址会显示跳转后的URL地址
http://www.test.com/a.html http://www.test.com/a.htmlfdafkdjkajfkdjfk;ajd;kjf;dja;fjd;a
```
redirect 和 permanent区别则是返回的不同方式的重定向:
```
对于客户端来说一般状态下是没有区别的。而对于搜索引擎相对来说301的重定向更加友好如果我们把一个地址采用301跳转方式跳转的话搜索引擎会把老地址的相关信息带到新地址同时在搜索引擎索引库中彻底废弃掉原先的老地址。
使用302重定向时搜索引擎(特别是google)有时会查看跳转前后哪个网址更直观然后决定显示哪个如果它觉的跳转前的URL更好的话也许地址栏不会更改。
www.baidu.com/bre
```
**last,break示例**
![1561898537620](assets/1561898537620.png)
```shell
[root@localhost test]# cat /etc/nginx/conf.d/last_break.conf
server {
listen 80;
server_name localhost;
access_log /var/log/nginx/last.access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location /break/ {
root /usr/share/nginx/html;
rewrite .* /test/break.html break;
}
location /last/ {
root /usr/share/nginx/html;
rewrite .* /test/last.html last;
}
location /test/ {
root /usr/share/nginx/html;
rewrite .* /test/test.html break;
}
}
[root@localhost conf.d]# cd /usr/share/nginx/html/
[root@localhost html]# mkdir test
[root@localhost html]# echo "last" > test/last.html
[root@localhost html]# echo "break" > test/break.html
[root@localhost html]# echo "test" > test/test.html
http://10.0.105.196/break/break.html
http://10.0.105.196/last/last.html
```
**Rewrite匹配参考示例**
```shell
本地解析host文件
# http://www.testpm.com/a/1.html ==> http://www.testpm.com/b/2.html
location /a {
root /html;
index 1.html index.htm;
rewrite .* /b/2.html permanent;
}
http://www.testpm.com/a/1
location /b {
root /html;
index 2.html index.htm;
}
例2
# http://www.testpm.com/2019/a/1.html ==> http://www.testpm.com/2018/a/1.html
location /2019/a {
root /var/www/html;
index 1.html index.hml;
rewrite ^/2019/(.*)$ /2018/$1 permanent;
}
location /2018/a {
root /var/www/html;
index 1.html index.htl;
}
例3
# http://www.qf.com/a/1.html ==> http://jd.com
location /a {
root /html;
if ($host ~* www.qf.com ) {
rewrite .* http://jd.com permanent;
}
}
例4
# http://www.qf.com/a/1.html ==> http://jd.com/a/1.html
location /a {
root /html;
if ( $host ~* qf.com ){
rewrite .* http://jd.com$request_uri permanent;
}
}
rewrite http://qf.com/test/a.html http://jd.com/test/a.html flag
s / 正则表达式 / 替换后的内容 / g
例5
# http://www.tianyun.com/login/tianyun.html ==> http://www.tianyun.com/reg/login.html?user=tianyun
location /login {
root /usr/share/nginx/html;
rewrite ^/login/(.*)\.html$ http://$host/reg/login.html?user=$1;
}
location /reg {
root /usr/share/nginx/html;
index login.html;
}
例6
#http://www.tianyun.com/qf/11-22-33/1.html ==> http://www.tianyun.com/qf/11/22/33/1.html
location /qf {
rewrite ^/qf/([0-9]+)-([0-9]+)-([0-9]+)(.*)$ /qf/$1/$2/$3$4 permanent;
}
location /qf/11/22/33 {
root /html;
index 1.html;
}
```
**set 指令**
set 指令是用于定义一个变量,并且赋值
应用环境:
```shell
server,location,if
a=8
set a 8
```
应用示例
```shell
例8
#http://alice.testpm.com ==> http://www.testpm.com/alice
#http://jack.testpm.com ==> http://www.testpm.com/jack
[root@nginx-server conf.d]# cd /usr/share/nginx/html/
[root@nginx-server html]# mkdir jack alice
[root@nginx-server html]# echo "jack.." >> jack/index.html
[root@nginx-server html]# echo "alice.." >> alice/index.html
本地解析域名host文件
10.0.105.202 www.testpm.com
10.0.105.202 alice.testpm.com
10.0.105.202 jack.testpm.com
编辑配置文件:
server {
listen 80;
server_name www.testpm.com;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
if ( $host ~* ^www.testpm.com$) {
break;
}
if ( $host ~* "^(.*)\.testpm\.com$" ) {
set $user $1;
rewrite .* http://www.testpm.com/$user permanent;
}
}
location /jack {
root /usr/share/nginx/html;
index index.html index.hml;
}
location /alice {
root /usr/share/nginx/html;
index index.html index.hml;
}
}
```
**return 指令**
return 指令用于返回状态码给客户端
```shell
server,location,if
```
应用示例:
```shell
例9如果访问的.sh结尾的文件则返回403操作拒绝错误
server {
listen 80;
server_name www.testpm.cn;
#access_log /var/log/nginx/http_access.log main;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
location ~* \.sh$ {
return 403;
}
}
例1080 ======> 443 80转443端口 重点实验
server {
listen 80;
server_name www.testpm.cn;
access_log /var/log/nginx/http_access.log main;
return 301 https://www.testpm.cn$request_uri;
}
http://www.testpm.cn/abc/hello/index.html
https://www.testpm.cn/abc/hello/index.html
server {
listen 443 ssl;
server_name www.testpm.cn;
access_log /var/log/nginx/https_access.log main;
#ssl on;
ssl_certificate /etc/nginx/cert/2447549_www.testpm.cn.pem;
ssl_certificate_key /etc/nginx/cert/2447549_www.testpm.cn.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
ssl_prefer_server_ciphers on;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
[root@nginx-server ~]# curl -I http://www.testpm.cn
HTTP/1.1 301 Moved Permanently
Server: nginx/1.16.0
Date: Wed, 03 Jul 2019 13:52:30 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.testpm.cn/
```
**Nginx 的 https ( rewrite )**
```shell
server {
listen 80;
server_name *.vip9999.top vip9999.top;
if ($host ~* "^www.vip9999.top$|^vip9999.top$" ) {
return 301 https://www.vip9999.top$request_uri;
}
}
# Settings for a TLS enabled server.
server {
listen 443 ssl;
server_name www.vip9999.top;
ssl_certificate cert/214025315060640.pem;
ssl_certificate_key cert/214025315060640.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
#pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
location ~ \.php$ {
root /usr/share/nginx/html;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
```
### nginx 错误页面配置(扩展)
nginx错误页面包括404 403 500 502 503 504等页面只需要在server中增加以下配置即可
```shell
#error_page 404 403 500 502 503 504 /404.html;
location = /404.html {
root /usr/local/nginx/html;
}
```
注意:
```bash
/usr/local/nginx/html/ 路径下必须有404.html这个文件
404.html上如果引用其他文件的png或css就会有问题显示不出来因为其他文件的访问也要做配置
为了简单可以将css嵌入文件中图片用base编码嵌入如下
```
base64 图片---转码
```shell
[root@localhost html]# vim 404.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<title>404</title>
<style>
.layout-table{display:table;height:100%;width:100%;vertical-align: middle;margin-top:150px}
.layout-table-cell{display: table-cell;vertical-align: middle;text-align:center}
.layout-tip{font-size:28px;color:#373737;margin: 0 auto;margin-top:16px;border-bottom: 1px solid #eee;padding-bottom: 20px;width: 360px;}
#tips{font-size:18px;color:#666666;margin-top:16px;}
</style>
</head>
<body class="layui-layout-body">
<div class="layui-layout layui-layout-admin">
<div class="layui-body">
<div class="layout-table">
<div class="layout-table-cell">
<img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAV4AAACMCAYAAAA0qsGKAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOkIwRDQ0ODRFMzMyODExRThBQ0Q5Q0UyNkNCMkE4MDk0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOkIwRDQ0ODRGMzMyODExRThBQ0Q5Q0UyNkNCMkE4MDk0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6QjBENDQ4NEMzMzI4MTFFOEFDRDlDRTI2Q0IyQTgwOTQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6QjBENDQ4NEQzMzI4MTFFOEFDRDlDRTI2Q0IyQTgwOTQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4Qz6opAAAU5klEQVR42uxd3XXjthKe+Oy7dCsQU4GUAnLEfc7DKhWI20CsrcDcCiynAcsVhH7I81InBaxUQagKIlXga9wML2Ga4B9ACiC/7xyeXeuHpDD4PswMBuAPLy8vBAAAAPSHGzQBAAAAhBcAAADCCwAAAEB4AQAAILwAAAAAhBcAAMBKfCh68ec/f0PLAH1j+nosXg//9fCkY1bju6fXI3k9DtK/MZoUsAl//fJ7ufACQE9CK0R2xf/ONM4142OZe33/ekR8JGhywGqPFwA6xIqPdQ/XWvJx/3ocX48dH+eK73kQaqBL2JzjFV7QS0cHwtD+vdsNi9kfPYluHnMW4ITF1yv5bMCfC2A68HdswtslILz9IWQRu9dMJ5jChIX/7xIB3vJ7j3zvPswI/kJ49XFQvC7ItkC/NpZSEKJ1x2JnI9bcF8Lc62d+7Sf+/zcmuwezgr8QXrOGWzARv7NoAO3TChGnFGYO3O+EB4dDAWnT1x7o31yx+HsDE4O/EN7mSEuP8ghqjKhAOXxu208O3vucSRsWvCfE9lf+/z0PLFOYG/xtC5urGroK6+IS0RA4Ema02yBkz9EELpTV4iYV9vD48Nnr0U1r3FFW5iZXP0T8+o4HlpjJjkEa/IXwtjScxx5PmWEBNXakX6lwoazcS0fM0kUYgWTTpkjTCqvcvRz43DGfO+a/Ib7gL1INFYgKXlvlRASohymLjo7oitDxM2UlZ7oiJr6/ZQEWk2NPLc8zk4RVxpnP/cTedUwoOwN/ByS8fgfnPFJx8XwgiQC8l/qiG2t4lQJf2VvpiiwHtq0Q4H2L7wth/aYQ1kAS30eIL/gLj7c8JC4LUyLwpRfRPbIYhjWus2IPVlyvrKg+YfuFVFydIITgC6c0muKxQnzTz6AUEfx1XniXCFOsJkBb0d1TdV7U52v8Q/+Wpd3W6A8iNSAmvcTk2HcW4pDeVh9s+dwmxXfDAwnx4ADxBX8rYevkmtdRmJIgzWBEdNuWiz1VhOQLFsdlge0its9Z4Rmnk2pLSYjvWBi3kned1uZGLQaPR76+LABnejvhFvH5zyPuI+AvhLd0NFwgzdAIopOvNYhTtvhAvHcv/X2hbOlu0tAbWvG9igEiXSARUFalkPDfMTUvP9sVeOxn6XwzykrPILzgr1Ophi46bVQyWiLNUI0Fe3xtcGGbnktIJYtuOukWUruazIhF9sfX41nygL9LNj+07GdpJUN+AcVBOveSqvPXQwb466jwmh4xnxUEXiHNUAtTTY9iVSG6a8kO6aSbiVA94Wt/5HOn6YKdJJZfWopvpBCHB/7/3Yi9XvDXUeE1PUERKUblGdIMtbCl9vsuPJC6qF0W3SPbvQsCxXzutKRsLYnvltqVmqm82pCyybYdjXNpMfjrqPDODZ7roghDgpywAGpvda3R9qqQe5MTXZ+6nZBKJ8GeJPENCvpCExR5tWfKctmzkaYcwF8Hhdd0eLZThM4rifQJ9FWZYtDp1KqUgfCI7iViBdRfFUBAb2tvfbb/g0b/mhZ42On5bmlcJWbgL4RXORoKo03g7VZio5FiuJS07TYnzn3n52TxjTQHmBkVV2uElNULj6mPgb+OCq9J72BP1VvIIb9bDI/0dhvblojeUrLPtYgTsLc04XtIqP2+Dnf0fkJJTjksaTx7PIO/8HiVSwxT4j/RuAvdq9IEpj2V/HmDK//GFWWLM3S9p53itdPIvF7w10HhNbGfaoqTwnCbCsMC/3ZunR3HVIQIpNTFE10/N5dQtopN4EBZRUJTLBWiE0opiWDg/Qb8dVR4TY6WZaFuatgYGtuJtxtVtL2Ja3SFneF2k73ecOD9Bvx1VHhN5cHKSlAm8HY79XYvCuGVQ8Q92TsTrZMzrOP1DjnXC/46KrymdjTaUvm+nWUjKpWMtOkWhEOG7sMcoxrntZk0iUa6QeXVRpRVOAz5YZngr4PCa9ITKDLKgton5XXrWV3BlPTzkHEN+9o+E23a6z1Lg82ShvmYePB35MKrMoqOxxWRuUkD28mj+ztVaYZ0Uu2Z7J+JjjW/H1SIyWagfQf8dVB4fUPnCRUjnrw8NW54vmWNTjeE1Um6gqB6NItL3q4J4V3T+9VscgojoOEB/HVQeBfUfoVUfrRMKgSlScghDFJnEYE4/3dyuyZYeKW6a+zjGqSMHWmPveb3i8Q19dQmNKxJNvDXUeE15QGoRsvUcJcGYcqiRUjjcjrChBAcKoT3RO6sq+8i3RB1EJqDvw7y92ZApFeNlm3WdS+o3dMJxk6eWNGWE8e83bJBpC7m9H4STU43DEl4wV8HhddUmBJWvH6pabgxiq6JNAMpiLNwMM1gQnhVghQNLN0A/joqvCY8rQdSb6Yhb5Z8HoLROoBv4Bz7kjY1KWZ9Iemobw8t3QD+Oiq8up3vUmO0LBtRZSOPUXRNCe9hYMJbNpg0STcUPZvtYrDdwV8H+Xtt4TURppStcqm7IYsw6uNIRdeU53WuEN6jg+1y7qhtY/53Rm6XIYK/jgqvbt1oWd6nzmiZPsTxjsYLUztKxYr2nRgM3fuGCQ/dr2grl71e8LclPjjuaW00Rst0O8AZjRumPK7zwNIMpgYLv0LQXfZ4wV8HPd5A09NS7ddZZ7QUr32H6BolfpGweoZFzEXhndH7srIheLzgr6Me78qA4anhaOnxKDknwKTwXhSvQ3izNs6f68j9cMYhs2urHsFfBz1e0YCfNL6/J3VNaNlo6UF038HEVn4HCG/jwc3ldAP466jwBprf35QYzaZHy7hAoL7OP2Zb+BWi7lq6AfwdofA+KDysac6gIXS1N+Gtmji7ONxGewPnKPJo4x4HQPAXwvu/3FDbpHhVsfUE3q62J9YGVTW
<p class="layout-tip">哎呀,找不到该页面啦!</p>
<p id="tips">请检查您的网络连接是否正常或者输入的网址是否正确</p>
</div>
</div>
</div>
</div>
</body>
</html>
```
展示效果;
![1561964133748](assets/1561964133748.png)
注意测试时需要访问不存在页面才会报404比如下路径需要把index.html页面删除并且测试时把index.html写入地址栏不能空着。http://192.168.26.87/index.html
### Nginx 流量控制
**流量限制** (rate-limiting)我们可以用来限制用户在给定时间内HTTP请求的数量。流量限制可以用作安全目的比如可以减慢暴力密码破解的速率更常见的情况是该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。
1、Nginx如何限流
Nginx的"流量限制"使用漏桶算法(leaky bucket algorithm),就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在请求处理方面,水代表来自客户端的请求,水桶代表根据”先进先出调度算法”(FIFO)等待被处理的请求队列,桶底漏出的水代表离开缓冲区被服务器处理的请求,桶口溢出的水代表被丢弃和不被处理的请求。
2、配置基本的限流--ngx_http_limit_req_module模块实现
“流量限制”配置两个主要的指令,`limit_req_zone`和`limit_req``limit_req_zone`指令设置流量限制和内存区域的参数,但实际上并不限制请求速率。所以需要通过添加`limit_req`指令启用流量限制,应用在特定的`location`或者`server`块。(示例中,对于”/login/”的所有请求)。
`limit_req_zone`指令通常在HTTP块中定义它需要以下三个参数
```ini
-Key - 定义应用限制的请求特性。示例中的 Nginx 变量$binary_remote_addr保存客户端IP地址的二进制形式。
-Zone - 定义用于存储每个IP地址状态以及被限制请求URL访问频率的内存区域。通过zone=keyword标识区域的名字(自定义)以及冒号后面跟区域大小。16000个IP地址的状态信息大约需要1MB。
-Rate - 连接请求。在示例中速率不能超过每秒1个请求。
```
实战
```shell
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
```shell
10.0.105.196配置:
server {
listen 80;
server_name localhost;
location /login {
root /usr/share/nginx/html;
index index.html index.html;
}
}
```
**测试**
```ini
客户端安装压力测试工具
[root@nginx-yum ~]# yum install httpd-tools
[root@nginx-yum ~]# ab -n1000 -c2 http://10.0.105.196/
-n 请求数
-c 并发数
代理机器看错误日志:
[root@nginx-server ~]# tail -f /var/log/nginx/error.log
2019/09/10 07:32:09 [error] 1371#0: *1095 limiting requests, excess: 0.390 by zone "mylimit", client: 10.0.105.196, server: localhost, request: "GET / HTTP/1.0", host: "10.0.105.196"
```
日志字段
```bash
- limiting requests - 表明日志条目记录的是被“流量限制”请求
- excess - 每毫秒超过对应“流量限制”配置的请求数量
- zone - 定义实施“流量限制”的区域
- client - 发起请求的客户端IP地址
- server - 服务器IP地址或主机名
- request - 客户端发起的实际HTTP请求
- host - HTTP报头中host的值
```
```ini
查看访问日志出现503
[root@nginx-server nginx]# tail -f /var/log/nginx/access.log
10.0.105.196 - - [10/Sep/2019:07:32:09 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3" "-"
```
实战二
```ini
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
#limit_req zone=mylimit;
limit_req zone=mylimit burst=5;
#limit_req zone=mylimit burst=5 nodelay;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
burst=5 表示最大延迟请求数量不大于5。超出的请求返回503状态码。
客户端测试--burst
[root@nginx-yum ~]# ab -n1000 -c50 http://10.0.105.195/
代理机器上面看日志
[root@nginx-server ~]# tail -f /var/log/nginx/access.log
10.0.105.196 - - [10/Sep/2019:08:05:10 +0800] "GET / HTTP/1.0" 503 197 "-" "ApacheBench/2.3" "-"
10.0.105.196 - - [10/Sep/2019:08:05:11 +0800] "GET / HTTP/1.0" 200 2 "-" "ApacheBench/2.3" "-"
nodelay不延迟转发请求。速度变快
客户端测试--burst
[root@nginx-yum ~]# ab -n1000 -c50 http://10.0.105.195/
总结:
如果不加nodelay只有burst的时候只会延迟转发请求超过限制的请求出现503错误
如果nodelay和burst参数都有不会延迟转发请求并且超出规定的请求次数会返回503
```
4、发送到客户端的错误代码
一般情况下客户端超过配置的流量限制时Nginx响应状态码为**503(Service Temporarily Unavailable)**。可以使用`limit_req_status`指令来设置为其它状态码(例如下面的**404**状态码):
```shell
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
upstream myweb {
server 10.0.105.196:80 weight=1 max_fails=1 fail_timeout=1;
}
server {
listen 80;
server_name localhost;
location /login {
limit_req zone=mylimit;
limit_req_status 404;
proxy_pass http://myweb;
proxy_set_header Host $host:$server_port;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
```
### nginx 访问控制
1、nginx 访问控制模块
```bash
1基于IP的访问控制http_access_module
2基于用户的信任登录http_auth_basic_module
```
2、基于IP的访问控制
**配置语法**
```shell
Syntaxallow address | all;
default默认无
Contexthttpserverlocation
Syntaxdeny address | all;
default默认无
Contexthttpserverlocation
===================================================
allow 允许 //ip或者网段
deny 拒绝 //ip或者网段
```
**配置测试**
编辑`/etc/nginx/conf.d/access_mod.conf`内容如下:
```shell
[root@192 ~]# vim /etc/nginx/conf.d/access_mod.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.hml;
deny 192.168.1.8;
allow all;
}
}
[root@192 ~]# nginx -t
[root@192 ~]# nginx -s reload
#需要注意:
1.按顺序匹配已经被匹配的ip或者网段后面不再被匹配。
2.如果先允许所有ip访问再定义拒绝访问。那么拒绝访问不生效。
3.默认为allow all
```
宿主机IP为`192.168.1.8`虚拟机IP为`192.168.1.11`故这里禁止宿主机访问允许其他所有IP访问。
宿主机访问`http://192.168.1.11`,显示`403 Forbidden`。
当然也可以反向配置同时也可以使用IP网段的配置方式如`allow 192.168.1.0/24;`表示满足此网段的IP都可以访问。
**指定`location`拒绝所有请求**
如果你想拒绝某个指定URL地址的所有请求,只需要在`location`块中配置`deny` **all**指令:
```shell
[root@192 ~]# vim /etc/nginx/conf.d/access_mod.conf
server {
listen 80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.hml;
deny all; #拒绝所有
}
}
[root@192 ~]# nginx -t
[root@192 ~]# nginx -s reload
```
3、基于用户的信任登录
2基于用户的信任登录模块http_auth_basic_module
有时我们会有这么一种需求,就是你的网站的某些页面不希望公开,我们希望的是某些特定的客户端可以访问。那么我们可以在访问时要求进行身份认证,就如给你自己的家门加一把锁,以拒绝那些不速之客。
**配置语法**
```shell
Syntaxauth_basic string | off;
defaultauth_basic off;
Contexthttpserverlocation
Syntaxauth_basic_user_file file;
default默认无
Contexthttpserverlocation
file存储用户名密码信息的文件。
```
**配置示例**
```shell
[root@192 ~]# vim /etc/nginx/conf.d/auth_mod.conf
server {
listen 80;
server_name localhost;
location ~ /admin {
root /var/www/html;
index index.html index.hml;
auth_basic "Auth access test!";
auth_basic_user_file /etc/nginx/auth_conf;
}
}
[root@192 ~]# nginx -t
[root@192 ~]# nginx -s reload
[root@192 ~]# mkdir /var/www/html #创建目录
[root@192 ~]# vim /var/www/html/index.html #创建文件
```
`auth_basic`不为`off`,开启登录验证功能,`auth_basic_user_file`加载账号密码文件。
**建立口令文件**
```shell
[root@192 ~]# yum install -y httpd-tools #htpasswd 是开源 http 服务器 apache httpd 的一个命令工具,用于生成 http 基本认证的密码文件
[root@192 ~]# htpasswd -cm /etc/nginx/auth_conf user10 # -c 创建解密文件,-m MD5加密新版本的apache不用-m可默认加密
[root@192 ~]# htpasswd -m /etc/nginx/auth_conf user20
[root@192 ~]# cat /etc/nginx/auth_conf
user10:$apr1$MOa9UVqF$RlYRMk7eprViEpNtDV0n40
user20:$apr1$biHJhW03$xboNUJgHME6yDd17gkQNb0
```
**访问测试**
![1561996355328](assets/1561996355328.png)
使用 limit_rate 限制客户端传输数据的速度
编辑/etc/nginx/nginx.conf
```bash
location / {
root /var/www/nginx/;
index index.html index.htm;
limit_rate 2k; #对每个连接的限速为2k/s
}
重启服务
```
### nginx 变量
Nginx的配置文件使用语法的就是一门微型的编程语言。既然是编程语言一般也就少不了“变量”这种东西。
##### 1、nginx变量简介
```ini
- 所有的 Nginx变量在 Nginx 配置文件中引用时都须带上 $ 前缀
- 在 Nginx 配置中,变量只能存放一种类型的值,而且也只存在一种类型,那就是字符串类型
```
所有的变量值都可以通过这种方式引用:
```ini
$变量名
```
##### 2、nginx 变量的定义和使用
nginx中的变量分为两种自定义变量与内置预定义变量
###### 1、自定义变量
**1、声明变量**
可以在sever,http,location等标签中使用set命令声明变量语法如下
```shell
set $变量名 变量值
set $A 8
$A
```
**注意:**
```ini
- nginx 中的变量必须都以$开头
- nginx 的配置文件中所有使用的变量都必须是声明过的,否则 nginx 会无法启动并打印相关异常日志
```
###### nginx安装echo模块
```shell
查看已经安装的nginx的版本
[root@192 ~]# nginx -V
上传或者下载一个相同版本的nginx包
[root@192 ~]# ls
anaconda-ks.cfg nginx-1.16.0.tar.gz
下载echo模块的安装包
[root@192 ~]# wget https://github.com/openresty/echo-nginx-module/archive/v0.61.tar.gz
[root@192 ~]# ls
anaconda-ks.cfg nginx-1.16.0.tar.gz v0.61.tar.gz
解压到相同路径下:
[root@192 ~]# tar xzf nginx-1.16.0.tar.gz -C /usr/local/
[root@192 ~]# tar xzf v0.61.tar.gz -C /usr/local/
安装编译工具
[root@192 ~]# cd /usr/local/
[root@192 local]# yum -y install pcre pcre-devel openssl openssl-devel gcc gcc-c++ zlib zlib-devel gd-devel
添加模块:
[root@192 local]# cd nginx-1.16.0/
添加上原来已经有的参数和新添加的模块:
[root@192 nginx-1.16.0]# ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-module=/usr/local/echo-nginx-module-0.61
[root@192 nginx-1.16.0]# make #编译不要make install 否则会覆盖原来的文件
[root@192 nginx-1.16.0]# mv /usr/sbin/nginx /usr/sbin/nginx_bak #将原来的nignx备份
[root@192 nginx-1.16.0]# cp objs/nginx /usr/sbin/ 拷贝nignx
[root@192 nginx-1.16.0]# systemctl restart nginx #启动
[root@192 nginx-1.16.0]# nginx -V 查看模块是否添加成功
nginx version: nginx/1.16.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC)
built with OpenSSL 1.0.2k-fips 26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --add-module=/usr/local/echo-nginx-module-0.61
```
**2、配置 $foo=hello**
```shell
[root@192 ~]# cd /etc/nginx/conf.d/
[root@192 conf.d]# vim echo.conf
server {
listen 80;
server_name localhost;
location /test {
set $foo hello;
echo "foo: $foo";
}
}
```
输出
```shell
[root@192 conf.d]# nginx -s reload
[root@192 conf.d]# curl localhost/test
foo: hello
```
Nginx 变量的创建只能发生在 Nginx 配置加载的时候,或者说 Nginx 启动的时候。而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致启动失败。
###### 2、内置预定义变量
内置预定义变量即无需声明就可以使用的变量通常包括一个http请求或响应中一部分内容的值以下为一些常用的内置预定义变量
| **变量名** | **定义** |
| ------------------- | ------------------------------------------------------------ |
| $arg_PARAMETER | GET请求中变量名PARAMETER参数的值。 |
| $args | 这个变量等于GET请求中的参数。例如foo=123&bar=blahblah;这个变量只可以被修改 |
| $binary_remote_addr | 二进制码形式的客户端地址。 |
| $body_bytes_sent | 传送页面的字节数 |
| $content_length | 请求头中的Content-length字段。 |
| $content_type | 请求头中的Content-Type字段。 |
| $cookie_COOKIE | cookie COOKIE的值。 |
| $document_root | 当前请求在root指令中指定的值。 |
| $document_uri | 与$uri相同。 |
| $host | 请求中的主机头(Host)字段如果请求中的主机头不可用或者空则为处理请求的server名称(处理请求的server的server_name指令的值)。值为小写,不包含端口。 |
| $hostname | 机器名使用 gethostname系统调用的值 |
| $http_HEADER | HTTP请求头中的内容HEADER为HTTP请求中的内容转为小写-变为_(破折号变为下划线),例如:$http_user_agent(Uaer-Agent的值); |
| $sent_http_HEADER | HTTP响应头中的内容HEADER为HTTP响应中的内容转为小写-变为_(破折号变为下划线),例如: $sent_http_cache_control, $sent_http_content_type…; |
| $is_args | 如果$args设置值为"?",否则为""。 |
| $limit_rate | 这个变量可以限制连接速率。 |
| $nginx_version | 当前运行的nginx版本号。 |
| $query_string | 与$args相同。 |
| $remote_addr | 客户端的IP地址。 |
| $remote_port | 客户端的端口。 |
| $remote_user | 已经经过Auth Basic Module验证的用户名。 |
| $request_filename | 当前连接请求的文件路径由root或alias指令与URI请求生成。 |
| $request_body | 这个变量0.7.58+包含请求的主要信息。在使用proxy_pass或fastcgi_pass指令的location中比较有意义。 |
| $request_body_file | 客户端请求主体信息的临时文件名。 |
| $request_completion | 如果请求成功,设为"OK";如果请求未完成或者不是一系列请求中最后一部分则设为空。 |
| $request_method | 这个变量是客户端请求的动作通常为GET或POST。包括0.8.20及之前的版本中这个变量总为main request中的动作如果当前请求是一个子请求并不使用这个当前请求的动作。 |
| $request_uri | 这个变量等于包含一些客户端请求参数的原始URI它无法修改请查看$uri更改或重写URI。 |
| $scheme | 所用的协议比如http或者是https比如rewrite ^(.+)$ $scheme://example.com$1 redirect; |
| $server_addr | 服务器地址在完成一次系统调用后可以确定这个值如果要绕开系统调用则必须在listen中指定地址并且使用bind参数。 |
| $server_name | 服务器名称。 |
| $server_port | 请求到达服务器的端口号。 |
| $server_protocol | 请求使用的协议通常是HTTP/1.0或HTTP/1.1。 |
| $uri | 请求中的当前URI(不带请求参数参数位于args)不同于浏览器传递的args)不同于浏览器传递的args)不同于浏览器传递的request_uri的值它可以通过内部重定向或者使用index指令进行修改。不包括协议和主机名例如/foo/bar.html |
### nginx 监控
**1、nginx的基础监控**
```ini
- 进程监控
- 端口监控
```
注意: 这两个是必须要加在zabbix监控加触发器有问题及时告警。
nginx 提供了 ngx_http_stub_status_module.这个 模块提供了基本的监控功能
**2、监控的指标**
1、基本活跃指标
Accepts接受、Handled已处理、Requests请求数是一直在增加的计数器。Active活跃、Waiting等待、Reading、Writing随着请求量而增减。
2、服务器错误率
通过监控固定时间间隔内的错误代码4XX代码表示客户端错误5XX代码表示服务器端错误
3、请求处理时间
请求处理时间也可以被记录在 access log 中,通过分析 access log统计请求的平均响应时间。 ----$request_time 变量
1、nginx Stub Status 监控模块安装
 先使用命令查看是否已经安装这个模块:
```shell
# -V大写会显示版本号和模块等信息、v小写仅显示版本信息
[root@localhost ~]# nginx -V
```
注意:是如果没有此模块,需要重新安装,编译命令如下:
```shell
./configure --with-http_stub_status_module
```
具体的使用方法是在执行 ./configure 时,指定 --with-http_stub_status_module然后通过配置
```shell
[root@localhost ~]# vim /etc/nginx/conf.d/status.conf
server {
listen 80;
server_name localhost;
location /nginx-status {
stub_status on;
access_log on;
}
}
```
2、nginx 状态查看
配置完成后在浏览器中输入http://10.0.105.207/nginx-status 查看显示信息如下:
```shell
Active connections: 2
server accepts handled requests
26 26 48
Reading: 0 Writing: 1 Waiting: 1
```
3、Stub Status 参数说明
![1562035977477](assets/1562035977477.png)
```shell
connection #连接数tcp连接
request #http请求GET/POST/DELETE/UPLOAD
```
![1567697012994](assets/1567697012994.png)
```ini
nginx总共处理了26个连接成功创建26次握手也就是成功的连接数connection. 总共处理了48个请求
失败连接=(总连接数(accepts)-成功连接数(handled)(相等表示中间没有失败的),
Reading nginx读取到客户端的Header信息数。请求头 -----速度快。
Writing nginx返回给客户端的Header信息数。响应头
Waiting 开启keep-alive的情况下意思就是Nginx说已经处理完正在等候下一次请求指令的驻留连接。
```
基于脚本监控nginx的端口
```ini
[root@nginx-server ~]# vim check_port.sh
#!/usr/bin/bash
curl -I http://127.0.0.1 &> /dev/null
if [ $? -ne 0 ];then
echo "nginx 未运行,正在启动中..."
sleep 1
systemctl start nginx
echo "正在检查nginx是否启动..."
port=`netstat -lntp | grep nginx |awk '{print $4}' | awk -F':' '{print $NF}'`
echo "nginx已经启动,端口为: $port"
fi
```
```bash
访问状态脚本
# vim nginx.sh
curl 192.168.26.87 > cache.txt 2>/dev/null
case $1 in
connections)
cat cache.txt | awk 'NR==1{print $3}'
;;
accepts)
cat cache.txt | awk 'NR==3{print $1}'
;;
handled)
cat cache.txt | awk 'NR==3{print $2}'
;;
requests)
cat cache.txt | awk 'NR==3{print $3}'
;;
Reading)
cat cache.txt | awk 'NR==4{print $2}'
;;
Writing)
cat cache.txt | awk 'NR==4{print $4}'
;;
Waiting)
cat cache.txt | awk 'NR==4{print $6}'
;;
*)
echo 参数错误
;;
esac
```
Alias 虚拟目录
```nginx
server {
listen 80;
server_name localhost;
location /test {
root /var/www/html;
index index.html;
}
www.baidu.com/test
url/var/www/html/test/index.html
location /qfedu {
alias /var/www/nginx; #访问http://x.x.x.x/qfedu时实际上访问是/var/www/nginx/index.html
index index.html;
}
}
```
`root`和`alias`的主要区别是:
```bash
#使用root实际的路径就是root值 + location值。root会将完整的url映射进文件路径。
#使用alias实际的路径就是alias值。alias只会将localhost后的url映射到文件路径。
#alias只能位于location块中root可以放在http,server,location块中
```
### HTTPS 原理(扩展)
HTTPS全称HyperText Transfer Protocol over Secure Socket Layer其实 HTTPS 并不是一个新鲜协议Google 很早就开始启用了,初衷是为了保证数据安全。 国内外的大型互联网公司很多也都已经启用了HTTPS这也是未来互联网发展的趋势。
1、加密算法
**对称加密**
```ini
A要给B发送数据
1A做一个对称密钥
2使用密钥给文件加密
3发送加密以后的文件和钥匙
4B拿钥匙解密
```
加密和解密都是使用的同一个密钥。
**非对称加密** ---- 公钥加密,私钥解密
A要给B发送数据
```ini
1.B做一对非对称的密钥
2.发送公钥给A
3.A拿公钥对数据进行加密
4.发送加密后的数据给B
5.B拿私钥解密
```
1. 哈希算法
将任意长度的信息转换为较短的固定长度的值,通常其长度要比信息小得多。
例如MD5、SHA-1、SHA-2、SHA-256 等
2. 数字签名
```ini
签名就是在信息的后面再加上一段内容信息经过hash后的值可以证明信息没有被修改过。hash值一般都会加密后也就是签名再和信息一起发送以保证这个hash值不被修改。
```
2、HTTPS 协议介绍
- HTTP 协议HyperText Transfer Protocol超文本传输协议是客户端浏览器与Web服务器之间的应用层通信协议 。
- HTTPS 协议HyperText Transfer Protocol over Secure Socket Layer可以理解为HTTP+SSL/TLS 即 HTTP 下加入 SSL 层HTTPS 的安全基础是 SSL因此加密的详细内容就需要 SSL用于安全的 HTTP 数据传输.
![1562050089966](assets/1562050089966.png)
- 如上图所示 HTTPS 相比 HTTP 多了一层 SSL/TLS
**SSL/TLS :SSL(Secure Sockets Layer 安全套接层),及其继任者传输层安全Transport Layer SecurityTLS是为网络通信提供安全及数据完整性的一种安全协议。TLS与SSL在传输层为数据通讯进行加密提供安全支持。**
```ini
SSL协议可分为两层SSL握手协议SSL Handshake Protocol它建立在SSL记录协议之上用于在实际的数据传输开始前通讯双方进行身份认证、协商加密算法、交换加密密钥等。---相当于连接
SSL记录协议SSL Record Protocol它建立在可靠的传输协议如TCP之上为高层协议提供数据封装、压缩、加密等基本功能的支持。---相当于通信
```
**SSL协议提供的服务主要有**
```ini
ssl:身份认证和数据加密。保证数据完整性
1认证用户和服务器确保数据发送到正确的客户机和服务器
2加密数据以防止数据中途被窃取
3维护数据的完整性确保数据在传输过程中不被改变。
```
3、HTTPS 原理
1、HTTP 访问过程
![1562050123147](assets/1562050123147.png)
如上图所示HTTP请求过程中客户端与服务器之间没有任何身份确认的过程数据全部明文传输“裸奔”在互联网上所以很容易遭到黑客的攻击如下
![1562050246969](assets/1562050246969.png)
可以看到,客户端发出的请求很容易被黑客截获,如果此时黑客冒充服务器,则其可返回任意信息给客户端,而不被客户端察觉。
**所以 HTTP 传输面临的风险有:**
```ini
- 窃听风险:黑客可以获知通信内容。
- 篡改风险:黑客可以修改通信内容。
- 冒充风险:黑客可以冒充他人身份参与通信。
```
那有没有一种方式既可以安全的获取公钥,又能防止黑客冒充呢? 那就需要用到终极武器了SSL 证书(申购)
- 证书:.crt, .pem
- 私钥:.key
- 证书请求文件:.csr
![1562050492512](assets/1562050492512.png)
如上图所示,在第 ② 步时服务器发送了一个SSL证书给客户端SSL 证书中包含的具体内容有:
1证书的发布机构CA
2证书的有效期
3公钥
4证书所有者
5签名 ----- 签名就可以理解为是钞票里面的一个防伪标签。
**客户端在接受到服务端发来的SSL证书时会对证书的真伪进行校验以浏览器为例说明如下**
```ini
1首先浏览器读取证书中的证书所有者、有效期等信息进行一一校验
2浏览器开始查找操作系统中已内置的受信任的证书发布机构CA与服务器发来的证书中的颁发者CA比对用于校验证书是否为合法机构颁发
3如果找不到浏览器就会报错说明服务器发来的证书是不可信任的。
4如果找到那么浏览器就会从操作系统中取出 颁发者CA 的公钥,然后对服务器发来的证书里面的签名进行解密
5浏览器使用相同的hash算法计算出服务器发来的证书的hash值将这个计算的hash值与证书中签名做对比
6对比结果一致则证明服务器发来的证书合法没有被冒充
7此时浏览器就可以读取证书中的公钥用于后续加密了
(8client与web协商对称加密算法client生成对称加密密钥并使用web公钥加密发送给web服务器web服务器使用web私钥解密
(9)使用对称加密密钥传输数据,并校验数据的完整性
```
4、所以通过发送SSL证书的形式既解决了公钥获取问题又解决了黑客冒充问题一箭双雕HTTPS加密过程也就此形成
**所以相比HTTPHTTPS 传输更加安全**
1 所有信息都是加密传播,黑客无法窃听。
2 具有校验机制,一旦被篡改,通信双方会立刻发现。
3 配备身份证书,防止身份被冒充。
3、HTTPS 总结
**综上所述,相比 HTTP 协议HTTPS 协议增加了很多握手、加密解密等流程,虽然过程很复杂,但其可以保证数据传输的安全。**
HTTPS 缺点:
1. SSL 证书费用很高,以及其在服务器上的部署、更新维护非常繁琐
2. HTTPS 降低用户访问速度(多次握手)
3. 网站改用HTTPS 以后由HTTP 跳转到 HTTPS 的方式增加了用户访问耗时多数网站采用302跳转
4. HTTPS 涉及到的安全算法会消耗 CPU 资源需要增加大量机器https访问过程需要加解密
4、CA 机构
CACertificate Authority证书颁发机构主要负责证书的颁发、管理以及归档和吊销。证书内包含了拥有证书者的姓名、地址、电子邮件帐号、公钥、证书有效期、发放证书的CA、CA的数字签名等信息。**证书主要有三大功能:加密、签名、身份验证。**
阿里云申请配置ssl证书---实战
1.准备一台阿里云服务器,我的机器在香港
![image-20200213200532113](assets/image-20200213200532113.png)
2.准备一个域名
![image-20200213200455861](assets/image-20200213200455861.png)
3.申请ssl证书
![image-20200213200630056](assets/image-20200213200630056.png)
![image-20200213200649217](assets/image-20200213200649217.png)
![image-20200213200720968](assets/image-20200213200720968.png)
![image-20200213200802120](assets/image-20200213200802120.png)
![image-20200213200818583](assets/image-20200213200818583.png)
![image-20200213200845265](assets/image-20200213200845265.png)
![image-20200213200859612](assets/image-20200213200859612.png)
![image-20200213201008494](assets/image-20200213201008494.png)
**开始配置信息**
![image-20200213201131777](assets/image-20200213201131777.png)
![image-20200213201151813](assets/image-20200213201151813.png)
![image-20200213201339745](assets/image-20200213201339745.png)
![image-20200213201357363](assets/image-20200213201357363.png)
到这需要等待大约10几分钟然后刷新
![image-20200213202534828](assets/image-20200213202534828.png)
先查看帮助文档
![image-20200213202726173](assets/image-20200213202726173.png)
![image-20200213202749549](assets/image-20200213202749549.png)
然后在下载
![image-20200213202618176](assets/image-20200213202618176.png)
阿里云配置证书案例:
```shell
yum安装nginx ----略
将证书上传到服务器中
1.首先需要在服务器创建对应的文件夹,参考命令如下
[root@nginx ~]# cd /etc/nginx/ && mkdir cert
2.在服务器创建完成对应文件夹之后,将证书文件复制到服务器中
[root@nginx nginx]# cd
[root@nginx ~]# ls
2447549_www.testpm.cn_nginx.zip
[root@nginx ~]# yum install -y unzip
[root@nginx ~]# unzip 2447549_www.testpm.cn_nginx.zip
Archive: 2447549_www.testpm.cn_nginx.zip
Aliyun Certificate Download
inflating: 2447549_www.testpm.cn.pem
inflating: 2447549_www.testpm.cn.key
[root@nginx ~]# ls
2447549_www.testpm.cn.key 2447549_www.testpm.cn_nginx.zip 2447549_www.testpm.cn.pem
[root@nginx ~]# cp 2447549_www.testpm.cn* /etc/nginx/cert/
[root@nginx ~]# cd /etc/nginx/cert/
改名:
[root@nginx cert]# mv 2447549_www.testpm.cn.key www.testpm.cn.key
[root@nginx cert]# mv 2447549_www.testpm.cn.pem www.testpm.cn.pem
证书配置
证书复制完成之后可以对nginx配置文件进行更改使用vim命令
[root@nginx ~]# cd /etc/nginx/conf.d/
[root@nginx conf.d]# vim nginx_ssl.conf
[root@nginx conf.d]# cat /etc/nginx/conf.d/nginx_ssl.conf
server {
listen 443 ssl; #https端口
server_name www.testpm.cn;
access_log /var/log/nginx/https_access.log main;
ssl_certificate /etc/nginx/cert/www.testpm.cn.pem; #指定证书路径
ssl_certificate_key /etc/nginx/cert/www.testpm.cn.key; #指定私钥路径
ssl_session_timeout 5m; #配置用于SSL会话的缓存
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #指定使用的协议
ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP; #密码指定为OpenSSL支持的格式
ssl_prefer_server_ciphers on; #设置协商加密算法
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
```
需要给域名做一个解析香港的服务器添加一个A记录即可
![image-20200213210056748](assets/image-20200213210056748.png)
![image-20200213210116172](assets/image-20200213210116172.png)
![image-20200213210202138](assets/image-20200213210202138.png)
![image-20200213210312751](assets/image-20200213210312751.png)
![image-20200213210344925](assets/image-20200213210344925.png)
解析完成之后大约需要等待3-5分钟
**测试访问**
![image-20200213205917877](assets/image-20200213205917877.png)
![image-20200213205951802](assets/image-20200213205951802.png)
访问成功!
### Nginx 性能优化
当我需要进行性能优化时,说明我们服务器无法满足日益增长的业务,需要从以下几个方面进行探讨
1、当前系统结构瓶颈
首先需要了解的是当前系统瓶颈,用的是什么,跑的是什么业务。里面的服务是什么样子,每个服务最大支持多少并发。
可以通过查看当前cpu负荷内存使用率来做简单判断。还可以通过操作系统的一些工具来判断当前系统性能瓶颈如分析对应的日志查看请求数量。也可以通过nginx http_stub_status_module模块来查看对应的连接数总握手次数总请求数。
2、了解业务模式
虽然我们是在做性能优化,但还是要熟悉业务,最终目的都是为业务服务的。我们要了解每一个接口业务类型是什么样的业务,比如电子商务抢购模式,这种情况平时流量会很小,但是到了抢购时间,流量一下子就会猛涨。也要了解系统层级结构,每一层在中间层做的是代理还是动静分离,还是后台进行直接服务。
3、系统与nginx性能优化
对相关的系统瓶颈及现状有了一定的了解之后,就可以根据影响性能方面做一个全体的评估和优化。
- 网络(网络流量、是否有丢包,网络的稳定性都会影响用户请求)
- 系统(系统负载、内存使用率、系统的稳定性、硬件磁盘是否有损坏)
- 服务连接优化、内核性能优化、http服务请求优化都可以在nginx中根据业务来进行设置
- 程序(接口性能、处理请求速度、每个程序的执行效率)
- 数据库、底层服务
上面列举出来每一级都会有关联也会影响整体性能这里主要关注的是nginx服务这一层。
#### 文件句柄
在linux/unix操作系统中一切皆文件我们的设备是文件文件是文件文件夹也是文件。当我们用户每发起一次请求就会产生一个文件句柄。文件句柄可以简单的理解为`文件句柄就是一个索引`。文件句柄就会随着请求量的增多,进程调用频繁增加,那么产生的文件句柄也就会越多。
fd file descriptor 文件描述符
文件句柄
a.txt b.txt 1000 1000
limit -n
系统默认对文件句柄是有限制的不可能会让一个进程无限制的调用句柄。因为系统资源是有限的操作系统默认使用的文件句柄是1024个句柄。
**设置方式**
- 系统全局性修改
- 用户局部性修改
- 进程局部性修改
**系统全局性修该和用户局部性修改**
```shell
[root@nginx-server ~]# vim /etc/security/limits.conf
```
```shell
#* soft core 0
#* hard rss 10000
#@student hard nproc 20
#@faculty soft nproc 20
#@faculty hard nproc 50
#ftp hard nproc 0
#@student - maxlogins 4
#root只是针对root这个用户来限制soft只是发提醒操作系统不会强制限制,一般的站点设置为一万左右就ok了
root soft nofile 65535
root hard nofile 65535
# *代表通配符 所有的用户
* soft nofile 25535
* hard nofile 25535 #hard硬控制,到达设定值后,操作系统会采取机制对当前进程进行限制,这个时候请求就会受到影响
```
root代表是root用户*代表的是所有用户,后面的数字就是文件句柄大小。大家可以根据个人业务来进行设置。
**进程局部性修改**
```shell
[root@nginx-server ~]# vim /etc/nginx/nginx.conf
user nginx; #运行nginx的用户。可以修改
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
worker_rlimit_nofile 65535; #进程限制配置Nginx worker进程最大打开文件数
events {
worker_connections 1024; #一个worker进程的并发
}
...
```
`worker_rlimit_nofile` 是在进程上面进行限制。
```ini
ulimit 命令---用于查看系统限制的值
# -a  显示目前资源限制的设定。
# -n <文件数目>  指定同一时间最多可开启的文件数。
2、ulimit -n 65535 #修改打开句柄数 ---临时
```
#### nginx通用配置优化
```nginx
#将nginx进程设置为普通用户为了安全考虑
user nginx;
#当前启动的worker进程官方建议是与系统核心数一致
worker_processes auto;
#将work进程绑定到每个cpu的核数上
worker_cpu_affinity auto;
#日志配置成warn
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
#针对 nginx 句柄的文件限制配置Nginx worker进程最大打开文件数>worker_connections值
worker_rlimit_nofile 35535;
#事件模型
events {
#使用epoll内核模型
use epoll;
#每一个进程可以处理多少个连接,如果是多核可以将连接数调高 worker_processes * 1024
worker_connections 2048;
}
http {
server_tokens off; #隐藏nginx的版本号
include /etc/nginx/mime.types;
default_type application/octet-stream;
charset utf-8; #设置字符集,服务端返回给客户端报文的时候Nginx强行将报文转码为utf-8
#设置日志输出格式,根据自己的情况设置
log_format main '$http_user_agent' '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'"$args" "$request_uri"';
access_log /var/log/nginx/access.log main;
sendfile on; # 用于开启文件高效传输模式一般设置为on若nginx是用来进行磁盘IO负载应用时可以设置为off降低系统负载
tcp_nopush on; # 减少网络报文段数量,当有数据时,先别着急发送, 确保数据包已经装满数据, 避免了网络拥塞
tcp_nodelay on; # 提高I/O性能确保数据尽快发送, 提高数据传输效率
keepalive_timeout 65; #设置长连接的超时时间,请求完成之后还要保持连接多久.
open_file_cache max=35535 inactive=20s #这个将为打开文件指定缓存默认是没有启用的max指定缓存数量建议和打开文件数一致inactive 是指经过多长时间文件没被请求后删除缓存。
########
#Gzip module
gzip on; #文件压缩默认可以打开.告诉nginx采用gzip压缩的形式发送数据。这将会减少发送的数据量。
gzip_disable "MSIE [1-6]\."; #对于有些浏览器不能识别压缩需要过滤如ie6
gzip_min_length 1k; #允许压缩的最小字节数
gzip_http_version 1.1; #设置识别 http 协议版本,默认是 1.1
gzip_types text/css text/xml application/javascript #用来指定压缩的类型text/html类型总是会被压缩。
autoindex on; //用于http或者location模块
开启目录列表访问,合适下载服务器,默认关闭。
Nginx默认是不允许列出整个目录的。如需此功能打开nginx.conf文件在location server 或 http段中加入autoindex on;
另外两个参数最好也加上去:
autoindex_exact_size off;
默认为on显示出文件的确切大小单位是bytes。
改为off后显示出文件的大概大小单位是kB或者MB或者GB
autoindex_localtime on;
默认为off显示的文件时间为GMT时间。
改为on后显示的文件时间为文件的服务器时间
include /etc/nginx/conf.d/*.conf;
}
```
### Nginx 平滑升级(扩展)
1、Nginx 平滑升级原理
```ini
1在不停掉老进程的情况下启动新进程。
2老进程负责处理仍然没有处理完的请求但不再接受处理请求。
3新进程接受新请求。
4老进程处理完所有请求关闭所有连接后停止。
这样就很方便地实现了平滑升级。一般有两种情况下需要升级 nginx一种是确实要升级 nginx 的版本,另一种是要为 nginx 添加新的模块
```
2、Nginx信号简介
主进程支持的信号
```ini
- TERM, INT: 立刻退出
- QUIT: 等待工作进程结束后再退出
- KILL: 强制终止进程
- HUP: 重新加载配置文件,使用新的配置启动工作进程,并逐步关闭旧进程。
- USR1: 重新打开日志文件
- USR2: 启动新的主进程,实现热升级
- WINCH: 逐步关闭工作进程
```
工作进程支持的信号
```ini
- TERM, INT: 立刻退出
- QUIT: 等待请求处理结束后再退出
- USR1: 重新打开日志文件
```
3、nginx 平滑升级实战
查看现有的 nginx 编译参数
```shell
[root@nginx-server ~]# /usr/local/nginx/sbin/nginx -V
```
按照原来的编译参数安装 nginx 的方法进行安装,**只需要到 make千万不要 make install** 。如果make install 会将原来的配置文件覆盖(还有另外的方法,直接把新版安装到另一个目录,然后拷贝新版启动文件到旧版目录)
```shell
[root@nginx-server ~]# cd /usr/local/nginx-1.16.0/
[root@nginx-server nginx-1.16.0]# ./configure --prefix=/usr/local/nginx --group=nginx --user=nginx --sbin-path=/usr/local/nginx/sbin/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/tmp/nginx/client_body --http-proxy-temp-path=/tmp/nginx/proxy --http-fastcgi-temp-path=/tmp/nginx/fastcgi --pid-path=/var/run/nginx.pid --lock-path=/var/lock/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_gzip_static_module --with-pcre --with-http_realip_module --with-stream --with-http_image_filter_module
[root@nginx-server nginx-1.16.0]# make
make完之后会在解压目录下产生一个objs目录里面就是新版的nginx二进制启动文件
```
备份原 nginx 二进制文件
备份二进制文件和 nginx 的配置文件期间nginx不会停止服务
```shell
[root@nginx-server nginx-1.16.0]# mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx_$(date +%F)
```
复制新版nginx二进制文件进入旧版nginx安装目录
```shell
[root@nginx-server nginx-1.16.0]# cp /usr/local/nginx-1.16.0/objs/nginx /usr/local/nginx/sbin/
```
测试新版本的nginx是否正常
```shell
[root@nginx-server nginx-1.16.0]# /usr/local/nginx/sbin/nginx -t
```
给nginx发送平滑迁移信号若不清楚pid路径请查看nginx配置文件
```shell
[root@nginx-server ~]# kill -USR2 `cat /var/run/nginx.pid`
```
查看nginx pid会出现一个nginx.pid.oldbin
```shell
[root@nginx-server ~]# ll /var/run/nginx.pid*
-rw-r--r-- 1 root root 5 Jul 1 11:29 /var/run/nginx.pid
-rw-r--r-- 1 root root 5 Jul 1 09:54 /var/run/nginx.pid.oldbin
```
从容关闭旧的Nginx进程
```shell
[root@nginx-server ~]# kill -WINCH `cat /var/run/nginx.pid.oldbin`
```
结束工作进程,完成此次升级
```shell
[root@nginx-server ~]# kill -QUIT `cat /var/run/nginx.pid.oldbin`
```
验证Nginx是否升级成功
```shell
[root@nginx-server ~]# /usr/local/nginx/sbin/nginx -V
```
# Last和break
在NGINX中`rewrite`是用于重写URL的指令可以在服务器块或 location 块中使用。`rewrite` 指令通常有两个重要选项:`break` 和 `last`。这两个选项用于控制重写操作的行为。以下是对它们的详细解释:
1. `break` 选项:
- 当使用 `rewrite ... break;` 时,它会立即停止当前 location 块内的 rewrite 过程。
- 如果有其他 rewrite 规则定义在同一个 location 块内,这些规则将不会再被执行。
- 这个选项通常用于阻止后续的 rewrite 规则在当前 location 块中执行。它可以用于执行某种条件检查后立即中断处理。
示例:
```nginx
location /example/ {
rewrite ^/example/old(/.*)$ /example/new$1 break;
rewrite ^/example/other(/.*)$ /example/something$1; # 这个不会被执行
}
```
在上面的示例中,如果请求匹配 `/example/old/...`,第一条 rewrite 规则将被执行,并使用 `break` 选项终止当前 location 块中的 rewrite 处理,不会继续执行第二条 rewrite 规则。
2. `last` 选项:
- 当使用 `rewrite ... last;` 时,它会终止当前 rewrite 指令的处理但会重新启动请求的处理同时将请求的URI与新的URI传递给新的 location。
- 这可以用于执行重定向操作,将请求传递到新的 location 块,而不终止整个请求处理。
- 如果有其他 rewrite 规则定义在同一个 location 块内,它们也将会被重新执行。
示例:
```nginx
location /example/ {
rewrite ^/example/old(/.*)$ /example/new$1 last;
rewrite ^/example/other(/.*)$ /example/something$1; # 这个也会被执行
}
```
在上面的示例中,如果请求匹配 `/example/old/...`,第一条 rewrite 规则将被执行,但使用 `last` 选项,它会重新启动请求处理,并传递给新的 location 或匹配的 location 块。然后,第二条 rewrite 规则也会被执行。
综上所述,`break` 用于终止当前 location 块内的 rewrite 处理,而 `last` 用于重新启动请求的处理并将请求传递给新的 location。这两个选项在不同情况下非常有用可以根据需要选择使用哪个。
**Last和Break练习示例**
示例1-无break和last
请求:访问/1.html
结果最终会匹配到b.html
执行顺序连续执行两次rewrite后匹配到location /3.html最终匹配到b.html
server{
listen 80;
server_name test.com;
root /tmp;
location / {
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
示例2-break在location外
请求:访问/1.html
结果最终返回403
执行顺序因为rewrite后有break所以不再匹配后面的rewrite了但会继续匹配后面的location。
server{
listen 80;
server_name test.com;
root /tmp;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
location /2.html {
return 403;
}
}
示例3-break在location中
请求:访问/1.html
结果最终得到2.html
执行顺序在location块中匹配到rewrite /1.html /2.html break;后不再执行任何语句包括当前location块中的语句。
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
示例4-last
请求:访问/1.html
结果最终得到a.html
执行顺序遇到了last的后会再次进行匹配最终得到a.html。
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
# 制作CA证书(扩展)
## 证书制作
RHEL5、RHEL6 、RHEL7中在/etc/pki/tls/certs 目录有个脚本可以帮助我们简化证书生成的过程
\#cd /etc/pki/tls/certs
\#make server.key //生成私钥
\#openssl rsa -in server.key -out server.key //去除密码以便使用时不询问密码
rsa是一种采用非对称密钥的加密算法
\#make server.csr //生成证书颁发机构,用于颂发公钥
\#openssl x509 -in server.csr -req -signkey server.key -days 365 -out server.crt //颁发公钥
x509是一种非常通用的证书格式
## Nginx配置证书使用
由于我们并不是去 CA 证书中心申请的公钥,所以在使用的时候,客户端浏览器会跳出未受信任的警告。如果你 money 够多,请去 CA 申请。
server {
listen 443;
#listen 443 ssl; 1.26版本以上使用此配置 并且取消下面ssl的开关行配置
server_name web.wing.com;
ssl on; # 1.26 版本需要删除本行配置
**ssl_certificate /etc/pki/tls/certs/server.crt;**
** ssl_certificate_key /etc/pki/tls/certs/server.key;** #指定证书所存放的路径
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
**ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;** # 密码指定为OpenSSL支持的格式Nginx模板配置不可用注意修改此行
ssl_prefer_server_ciphers on;
location / {
root /wing/html;
index index.html index.htm;
}
}
1.生成一个CA中心
2.有自己的网站(web服务器)
3.生成证书请求
4.把证书请求发送给CA中心
5.CA中心给证书请求签名
6.把签名成功证书下发给WEB服务器
7.在web上配置证书生效(创建一个https的虚拟主机指定指定私钥和证书的位置)
8.浏览器访问web服务器443端口下载证书到客户端浏览器**
9.浏览器去CA中心验证证书真假
10.客户端生成自己的对称加密密钥,连同网页内容的请求一起发给服务器
11.客户端会使用WEB服务器的公钥对对称加密密钥进程加密
12.服务器对客户端发来的加密对称密钥解密并使用解密出来的对称密钥对将要传输的数据加密,然后传给客户端