Nginx 从入门到入坟(十一)- Nginx 反向代理深入研究及优化
1. Nginx反向代理概述
关于反向代理、正向代理的概念,在《Nginx从入门到入坟(一)- Nginx入门篇》中已经说的很清楚了,简而言之就是正向代理代理的对象是客户端
,反向代理代理的是服务端
,这是两者之间最大的区别。
下面先通过一个简单案例实现 Nginx 正向代理的应用。
2. 正向代理案例
需求:
实现:
- 服务端的配置:
1
2
3
4
5
6
7
8
9
10
11
12http {
log_format main 'client send request=>clientIp=$remote_addr serverIp=>$host';
server{
listen 80;
server_name localhost;
access_log logs/access.log main;
location {
root html;
index index.html index.htm;
}
}
} - 使用客户端访问服务端,打开日志查看结果:
- 代理服务器设置:
1
2
3
4
5
6
7
8server {
listen 82;
resolver 8.8.8.8;
location /{
proxy_pass http://$host$request_uri;
}
} - 查看代理服务器的 IP 和 Nginx 配置监听的端口;
- 在客户端配置代理服务器:
- 设置完成后,再次通过浏览器访问服务端:
通过对比,上下两次的日志记录,会发现虽然我们是客户端访问服务端,但是如何使用了代理,那么服务端能看到的只是代理发送过去的请求,这样的话,就使用 Nginx 实现了正向代理的设置。
但是 Nginx 正向代理,在实际的应用中不是特别多,所以我们简单了解下,接下来我们继续说 Nginx 的反向代理,这是 Nginx 比较重要的一个功能。
3. Nginx反向代理的配置语法
Nginx 反向代理模块的指令是由ngx_http_proxy_module
模块进行解析,该模块在安装 Nginx 的时候已经自己加装到 Nginx 中了,接下来我们把反向代理中的常用指令一一介绍下:
1 | proxy_pass |
3.1 proxy_pass
该指令用来设置被代理服务器地址,可以是主机名称、IP 地址加端口号形式。
语法 | proxy_pass URL |
---|---|
默认值 | - |
位置 | location |
URL
:为要设置的被代理服务器地址,包含传输协议(http , https:// )、主机名称或 IP 地址加端口号、URI 等要素。
举例:
1 | proxy_pass http://www.baidu.com; |
那么现在有一个问题,在编写 proxy_pass 的时候,后面的值要不要加/
?
下面通过一个例子来说明这个问题:
1 | server { |
3.2 proxy_set_header
该指令可以更改 Nginx 服务器接收到的客户端请求的请求头信息,然后将新的请求头发送给代理的服务器。
语法 | proxy_set_header field value |
---|---|
默认值 | proxy_set_header Host $proxy_host proxy_set_header Connection close |
位置 | http、server、location |
需要注意的是,如果想要看到结果,必须在被代理的服务器上来获取添加的头信息。
被代理服务器:192.168.200.146
1 | server { |
代理服务器:192.168.200.133
1 | server { |
访问测试正常。
3.3 proxy_redirect
该指令用来修改被代理服务器返回的响应头中的Location
头域和refresh
头域。
语法 | proxy_redirect redirect replacement proxy_redirect default proxy_redirect off |
---|---|
默认值 | proxy_redirect default |
位置 | http、server、location |
语法结构:
1 | proxy_redirect 旧地址 新地址; |
案例:
- 假设被代理服务器返回 Location 字段为:
http://localhost:8000/two/some/uri/
1
2proxy_redirect http://localhost:8000/two/ http://frontend/one/;
将 Location 字段重写为http://frontend/one/some/uri/ - .参数
off
将在这个字段中禁止所有的proxy_redirect
指令:1
proxy_redirect off;
注意,proxy_redirect default 必须在 proxy_pass 下方配置。
4. Nginx反向代理实例
现在有以上三台服务器,这三台服务器存在两种情况:
- 三台服务器的内容不一样;
- 三台服务器的内容一样;
如果三台服务器的内容不一样,我们可以根据用户请求来分发到不同的服务器,配置如下:
1 | 代理服务器 |
那么第二种三台服务器的内容一样的情况,后面说到负载均衡再说这种情况怎么处理。
5. 反向代理调优
反向代理值 Buffer(缓冲)和 Cache(缓存)。
- 相同点:
- 两种方式都是用来提供 IO 吞吐效率,都是用来提升 Nginx 代理的性能;
- 不同点:
- 缓冲主要用来解决不同设备之间数据传递速度不一致导致的性能低的问题,缓冲中的数据一旦此次操作完成后,就可以删除;
- 缓存主要是备份,将被代理服务器的数据缓存一份到代理服务器,这样的话,客户端再次获取相同数据的时候,就只需要从代理服务器上获取,效率较高,缓存中的数据可以重复使用,只有满足特定条件才会删除。
5.1 缓冲控制
proxy_buffering: 该指令用来开启或者关闭代理服务器的缓冲区,如果关闭缓冲,那么当 Nginx 一收到后端的反馈就同时传给客户端。
语法 | proxy_buffering on|off |
---|---|
默认值 | proxy_buffering on; |
位置 | http、server、location |
这个参数用来控制是否打开后端响应内容的缓冲区,如果这个设置为off
,那么proxy_buffers
和proxy_busy_buffers_size
这两个指令将会失效。 但是无论proxy_buffering
是否开启,对proxy_buffer_size
都是生效的。
proxy_buffering
开启的情况下,Nignx 会把后端返回的内容先放到缓冲区当中,然后再返回给客户端(边收边传,不是全部接收完再传给客户端)。 临时文件由proxy_max_temp_file_size
和proxy_temp_file_write_size
这两个指令决定的。如果响应内容无法放在内存里边,那么部分内容会被写到磁盘上。
如果proxy_buffering
关闭,那么 Nginx 会立即把从后端收到的响应内容传送给客户端,每次取的大小为proxy_buffer_size
的大小,这样效率肯定会比较低。
Nginx不会去尝试计算被代理服务器整个响应内容的大小,Nginx 能从服务器接受的最大数据,是由指令proxy_buffer_size
指定的。
proxy_buffering
启用时,要提防使用的代理缓冲区太大。这可能会吃掉你的内存,限制代理能够支持的最大并发连接数。- 对于基于长轮询(long-polling)的 Comet 应用来说,关闭
proxy_buffering
是重要的,不然异步响应将被缓存导致 Comet 无法工作。
proxy_buffers: 该指令用来指定单个连接从代理服务器读取响应的缓存区的个数和大小。
语法 | proxy_buffers number size; |
---|---|
默认值 | proxy_buffers 8 4k|8K(与系统平台有关) |
位置 | http、server、location |
- number: 缓冲区的个数;
- size: 每个缓冲区的大小,缓冲区的总大小就是
number*size
;
Nginx 从被代理的后端服务器取得的响应内容,放置到缓冲区,默认情况下,一个缓冲区的大小等于内存页面大小,可能是 4K 也可能是 8K,这取决于平台。
若某些请求的响应过大,则超过proxy_buffers
的部分将被缓冲到硬盘(缓冲目录由proxy_temp_path
指令指定),当然这将会使读取响应的速度减慢,影响用户体验。可以使用proxy_max_temp_file_size
指令关闭磁盘缓冲。
proxy_buffer_size: 该指令用来设置从被代理服务器获取的第一部分响应数据的大小。保持与 proxy_buffers 中的 size 一致即可,当然也可以更小。
语法 | proxy_buffer_size size; |
---|---|
默认值 | proxy_buffer_size 4k|8k(与系统平台有关) |
位置 | http、server、location |
proxy_busy_buffers_size: 该指令用来限制同时处于 BUSY 状态的缓冲总大小。
语法 | proxy_busy_buffers_size size; |
---|---|
默认值 | proxy_busy_buffers_size 8k|16K |
位置 | http、server、location |
proxy_busy_buffers_size
不是独立的空间,他是proxy_buffers
和proxy_buffer_size
的一部分。Nginx 会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据(这部分的大小是由proxy_busy_buffers_size
来控制的,建议为proxy_buffers
中单个缓冲区大小的 2 倍),然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。
proxy_max_temp_file_size | proxy_temp_file_write_size
语法 | proxy_max_temp_file_size size; |
---|---|
默认值 | proxy_max_temp_file_size 1024m; |
位置 | http、server、location |
语法 | proxy_temp_file_write_size size; |
---|---|
默认值 | proxy_temp_file_write_size 8k|16k; |
位置 | http、server、location |
临时文件由proxy_max_temp_file_size
和proxy_temp_file_write_size
这两个指令决定。 proxy_temp_file_write_size
是一次访问能写入的临时文件的大小,默认是proxy_buffer_size
和proxy_buffers
中设置的缓冲区大小的 2 倍,Linux下一般是 8k。
proxy_max_temp_file_size
指定当响应内容大于proxy_buffers
指定的缓冲区时,写入硬盘的临时文件的大小。如果超过了这个值,Nginx 将与代理服务器同步的传递内容,而不再缓冲到硬盘。设置为 0 时, 则直接关闭硬盘缓冲。
proxy_temp_path: 当缓冲区存满后,仍未被 Nginx 服务器完全接受,响应数据就会被临时存放在磁盘文件上,该指令设置文件路径。
语法 | proxy_temp_path path; |
---|---|
默认值 | proxy_temp_path proxy_temp; |
位置 | http、server、location |
注意 path 最多设置三层。
5.1.1 总结
所有的 proxy buffer 参数是作用到每一个请求的。每一个请求会安按照参数的配置获得自己的 buffer。proxy buffer 不是 global 而是 per request 的。
proxy_buffering
是为了开启 response buffering of the proxied server,开启后proxy_buffers
和proxy_busy_buffers_size
参数才会起作用。
无论proxy_buffering
是否开启,proxy_buffer_size
(main buffer)都是工作的,proxy_buffer_size
所设置的buffer_size
的作用是用来存储 upstream 端 response 的 header。
在proxy_buffering
开启的情况下,Nginx 将会尽可能的读取所有的 upstream 端传输的数据到 buffer,直到proxy_buffers
设置的所有 buffer 们被写满或者数据被读取完(EOF)。此时 Nginx 开始向客户端传输数据,会同时传输这一整串 buffer 们。同时如果 response 的内容很大的话,Nginx 会接收并把他们写入到 temp_file 里去。大小由proxy_max_temp_file_size
控制。如果 busy 的 buffer 传输完了会从 temp_file 里面接着读数据,直到传输完毕。
一旦proxy_buffers
设置的 buffer 被写入,直到 buffer 里面的数据被完整的传输完(传输到客户端),这个 buffer 将会一直处在 busy 状态,我们不能对这个 buffer 进行任何别的操作。所有处在 busy 状态的 buffer size 加起来不能超过proxy_busy_buffers_size
,所以proxy_busy_buffers_size
是用来控制同时传输到客户端的 buffer 数量的。
5.1.2 配置示例
- 通用网站的配置:
1 | proxy_buffer_size 4k; # 设置代理服务器(Nginx)保存用户头信息的缓冲区大小 |
- docker registry 的配置,这个每次传输至少都是 9M 以上的内容,缓冲区配置大;
1 | proxy_buffering on; |
5.2 缓存和过期控制
上面的配置是将所有请求都转发给后端应用。为避免静态请求给后端应用带来的过大负载,我们可以将 Nginx 配置为缓存那些不变的响应数据。这就意味着 Nginx 不会向后端转发那些请求。
下面示例,将*.html
,*.gif
等文件缓存 30 分钟。
1 | http { |
这里,我们将请求缓存到/tmp/cache
,并定义了其大小限制为 1G。同时只允许缓存有效的返回数据,例如:
1 | proxy_cache_valid 200 301 302 30m; |
所有响应信息的返回代码不是HTTP (200|301|302) OK
的都不会被缓存。
对于例如 workpress 的应用,需要处理 cookies 和缓存的过期时间,通过只缓存静态资源来避免其带来的问题。
参考文献
【1】https://www.bilibili.com/video/BV1ov41187bq?p=85&vd_source=e66fcf471e44c7ac45f918fd1f1e7a77
【2】https://blog.csdn.net/hknaruto/article/details/117289891
【3】https://zhuanlan.zhihu.com/p/95852366
【4】https://www.pianshen.com/article/2410401164/
【5】https://blog.csdn.net/woshaguayi/article/details/117367087