Nginx负载均衡和反向代理
负载均衡

负载均衡:Nginx 的负载均衡是保障服务可用性的重要手段。当服务异常或者处于扩容中无法提供服务时,可以让请求绕过这些节点。
Nginx 在 AKF 扩展立方体上的应用

- X:基于 Round-Robin 或者
least-connected算法分发请求 - Y:基于 URL 对功能进行分发
- X:将用户 IP 地址或者其他信息映射到某个特定的服务或者集群
支持多种协议的反向代理

- 四层代理:依靠 IP地址实现,通过
stream模块支持,将下游发过来的 TCP、UDP 请求转发出去 - 七层代理:依靠业务信息,例如 Header、Method、URI 之类的,可以将 HTTP 请求作为
memcached、scgi等协议的请求转发到上游
反向代理与缓存

缓存可以分为两类:
- 时间缓存:将上游应用服务器的响应缓存在 Nginx 服务器的磁盘上,当下次同样的请求到达 Nginx 时,则直接返回磁盘上的内容。
- 空间缓存:当 Nginx 访问上游服务器时,可以预取一些内容缓存到 Nginx 磁盘
指定上游服务地址的 upstream 与 server 指令
Nginx 中将负责与上游服务交互的模块统称为 upstream 模块,包括 stream、http_upstream。并且提供 rb 负载均衡算法。
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream
1 | # name 会交由后面的反向代理使用 |
功能:指定一组上游服务器地址,其中,地址可以是域名、IP 地址或者 unix socket 地址。可以在域名或者 IP 地址后加端口,如果不加端口,那么默认使用 80 端口。
通用参数:
backup:指定当前server为备份服务,仅当非备份server不可用时,请求才会转发到该serverdown:标识某台服务已经下线,不在服务(主要方便管理和维护)
1 | upstream backend { |
加权 Round-Robin 负载均衡算法
功能:在加权轮询的方式询问 server 指令指定的上游服务。默认集成在 Nginx 的 upstream 框架中。
指令:
weight:服务访问的权重,默认是 1max_conns:server的最大并发连接数,仅作用于单worker进程。默认是 0,表示没有限制max_fails:在fail_timeout时间段内,最大的失败次数。当达到最大失败时,会在fail_timeout秒内这台server不允许再次被选择fail_timeout:单位为秒,默认 10 秒,具有两个功能:- 指定一段时间内,也是
fail_timeout,最大的失败次数max_fails - 到达
max_fails后,该server不能访问的时间
- 指定一段时间内,也是
对上游服务使用 keepalive 长连接
功能:通过复用连接,降低 Nginx 与上游服务器建立、关闭连接的消耗,提升吞吐量的同时,降低时延(上游服务器数量有限,效果更好)
对上游连接的 http 头部增加设定:
1 | proxy_http_version 1.1; # http 1.0 协议不支持,为了防止用户发来的是 1.0 版本协议,设置覆盖 1.1 |
1 | # 配置 Nginx 到上游配置的服务器,每个工作进程最多保持多少个空闲的连接用于 keepalive 请求 |
指定上游服务域名解析
resolver 指令,解析上游服务域名,以及域名解析超时时间
1 | Syntax: resolver address ... [valid=time] [ipv4=on|off] [ipv6=on|off] [status_zone=zone]; |
基于客户端 IP 地址的 Hash 算法实现负载均衡
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash
ip_hash 功能:以客户端的 IP 地址(remote-addr)作为 hash 算法的关键字,映射到特定的上游服务器中。
- 对 IPv4 地址使用前 3 个字节作为关键字,对 IPv6 则使用完整地址(16个字节)
- 可以使用
round-robin算法的参数 - 可以基于
realip模块修改用于执行算法的 IP 地址
1 | Syntax: ip_hash; |
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash
hash 功能:通过指定关键字作为 hash key,基于 hash 算法映射到特定的上游服务器中。
- 关键字可以含有变量、字符串
- 可以使用
round-robin算法的参数
1 | Syntax: hash key [consistent]; |
1 | upstream backend { |
hash 算法的问题
当上游服务器异常、扩容、缩容时,hash 算法会引发大量路由变更,可能导致缓存大范围失效。
缩容后:
一致性 hash 算法
在一个环上获取放置节点,当扩容或者缩容,可以将部分流量迁移到其他流量,而不是所有流量。
扩容后:
1 | Syntax: hash key [consistent]; |
使用 consistent 开启一致性哈希算法。
优先选择连接最少的上游服务器
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#least_conn
从所有上游服务器中,找出当前并发连接数最少的一个,将请求转发到它。
- 如果出现多个最少连接服务器的连接数都是一样的,使用
round-robin算法。
1 | Syntax: least_conn; |
使用共享内存使负载均衡策略对所有 worker 进程生效
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#zone
分配出共享内存,将其他 upstream 模块定义的负载均衡策略数据、运行时每个上游服务的状态(连接数、权重、失败次数)存放在共享内存上,以对所有 nginx worker 进程生效。
1 | Syntax: zone name [size]; |
upstream 模块间的顺序
保障功能的正常运行
1 | ngx_module_t *ngx_modules[] = { |
upstream 模块提供的变量(不含 cache)
upstream_addr:上游服务器的 IP 地址,格式为可读的字符串,例如192.168.1.1:80upstream_connect_time:与上游服务建立连接消耗的时间,单位为 秒,精确到毫秒upstream_header_time:接收上游服务发回响应中http头部所消耗的时间(响应先返回header),单位为秒,精确到毫秒upstream_response_time:接收完整的上游服务响应所消耗的时间,单位为秒,精确到毫秒upstream_http_名称:从上游服务返回的响应头部的值upstream_bytes_received:从上游服务接收到的响应长度,单位为字节upstream_response_length:从上游服务返回的响应包体长度,单位为字节upstream_status:上游服务返回的 HTTP 响应中的状态码。如果未连接上,该变量值为 502upstream_cookie_名称:从上游服务发回的响应头 Set-Cookie 中取出的cookie值upstream_trailer_名称:从上游服务的响应尾部取到的值
反向代理
流程

- 当缓存未命中,与上游服务器发送请求时,是先生成请求
header和body,甚至会读取请求完整的body(是否读取请求完整的body依据proxy_request_buffering参数,默认打开,也就是缓存到磁盘上),然后根据负载均衡策略连接上游服务器(避免上游服务器并发不强时产生影响) - 上游服务发回响应是先发送
header,Nginx 先处理响应头部,再决定如何处理body。如果proxy_buffering on(默认),则接收完整的响应包体之后,再发送响应包体。
Proxy 模块
对上游服务使用 http/https 协议进行反响代理。默认编译进 Nginx
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass
1 | Syntax: proxy_pass URL; |
URL 参数规则
- URL 必须以
http://或者https://开头,接下来是域名、IP、unix socket地址或者upstream的名字(代表一个集群),前两者可以在域名或者 IP 后加端口。最后时可选的 URI - 当 URL 参数中携带 URL 与否,会导致发向上游请求的 URL 不同:
- 不携带 URL,则将客户端请求中的 URL 直接转发给上游
location后使用正则表达式、@名字时,应采用这种方式
- 携带 URL,则对用户请求中的 URL 做如下操作:
- 将
location参数中匹配上的一段替换为该 URI
- 将
- 不携带 URL,则将客户端请求中的 URL 直接转发给上游
- 该 URI 参数中可以携带变量
- 更复杂的 URL 替换,可以在
location内的配置添加rewrite break语句
1 | # 例如下面,在端口后面有内容,就是携带 URL,此时请求 /xxx/abc,则会请求到上游的 /uri/abc |
根据指令修改发往上游的请求
生成请求行
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_method
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_http_version
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_header
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_headers
1 | # 设置 method |
生成包体
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_pass_request_body
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_set_body
1 | # 是否将用户请求中的 body 传递给上游 |
Nginx 接收请求包体的方式
收完再转发还是边收边转发。
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_request_buffering
1 | Syntax: proxy_request_buffering on | off; |
启用或禁用客户端请求主体的缓冲。
当启用缓冲时,整个请求主体从客户端读取后再发送给代理服务器。
当禁用缓冲时,请求主体在接收到后立即发送给上游服务器。在这种情况下,如果 nginx 已经开始发送请求主体,则无法将请求传递给下一个服务器。当使用 HTTP/1.1 分块传输编码来发送原始请求主体时,除非为代理启用了 HTTP/1.1,否则不管指令值如何都会对请求主体进行缓冲。
on:接收完再转发,将body缓存在磁盘中- 客户端网速慢
- 上游服务并发处理能力低
- 适应高吞吐量场景(打开之后,非常依赖 Nginx 的高吞吐能力)
off:边接收边转发- 更及时的响应
- 降低 Nginx 读写磁盘的消耗
- 一旦开始发送内容,
proxy_next_upstream功能失败
客户端包体的接收
https://nginx.org/en/docs/http/ngx_http_core_module.html#client_body_buffer_size
1 | Syntax: client_body_buffer_size size; |
客户端发过来的请求中存在包体时,接收包体所分配的内存
- 若接收头部时已经接收完全部包体,则部分配(接收
header时,可能也接收到了一点包体) - 若剩余待接收包体的长度小于
client_body_buffer_size,则仅分配所需大小 - 分配
client_body_buffer_size大小内存接收包体- 关闭包体缓存时,该内存上内容及时发送给上游
- 打开包体缓存
- 该段大小内存用完时,写入临时文件,释放内存
最大包体长度限制
1 | Syntax: client_max_body_size size; |
仅对请求头部中含有 Content-Length 有效超出最大长度后,返回 413 错误。
临时文件路径格式
1 | # 设置在哪个目录下放置 body,Nginx 启动后,默认会创建这个目录 |
读取包体时的超时
1 | Syntax: client_body_timeout time; |
读取包体时超时,则返回 408 错误。
向上游服务建立连接
https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_connect_timeout
1 | Syntax: proxy_connect_timeout time; |
超时后,会向客户端生成 http 响应,响应码为 502(如果路由失效,不会等待 60s)
1 | Syntax: proxy_next_upstream on | off; |
当无法与代理服务器建立连接时,确定客户端连接是否将传递到下一个服务器。
上游连接启用 TCP keepalive
1 | Syntax: proxy_socket_keepalive on | off; |
TCP keepalive 是用于 TCP 连接,当没有数据传输时,会通过定时发送探测包,维护 TCP 连接。(由操作系统实现)

上游连接启用 HTTP keepalive
https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive
1 | Syntax: keepalive connections; |
使用了 HTTP 的 keepalive,大多数时候可以覆盖 TCP 的 keepalive。
修改 TCP 连接中的 local address
https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_bind
1 | Syntax: proxy_bind address [transparent] | off; |
- 可以使用变量:
proxy_bind $remote_addr;
- 可以使用不属于所在机器的 IP 地址:(需要有 root 权限)
proxy_bind $remote_addr transparent;
使用场景:
- 如果到达上游服务器有多个路由,
proxy_bind可以指定 IP,而不使用系统路由 - 透传 IP 地址
proxy_bind 其实是在修改 IP 报文中的 Source IP Address

当客户端关闭连接时
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_client_abort
1 | Syntax: proxy_ignore_client_abort on | off; |
当下游客户端出现失败,关闭客户端到 nginx 的连接,proxy 是否要忽略这个报错,是否一并关闭。
向上游发送 HTTP 请求
1 | Syntax: proxy_send_timeout time; |
Nginx 从接收到客户端请求,到生成请求,将请求发送给上游的超时时间。
接收上游的响应
接收上游的 HTTP 响应头部
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffer_size
1 | Syntax: proxy_buffer_size size; |
这个值也限制了 header 的大小,如果 header 中携带 cookie,超过了 proxy_buffer_size,那么 Nginx 无法正常处理这个请求。
出现这个问题时,Nginx 日志中会出现:error.log: upstream sent too big header
接收上游的 HTTP 包体
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_buffers
1 | Syntax: proxy_buffers number size; |
当指定大小的内存能存放上游的包体,则不会向磁盘中写入。
1 | Syntax: proxy_buffering on | off; |
启用或禁用来自代理服务器的响应缓冲。
- 当启用缓冲时,
nginx尽快从代理服务器接收响应,并将其保存到由proxy_buffer_size和proxy_buffers指令设置的缓冲区中。如果整个响应无法完全放入内存,则部分内容可以保存到磁盘上的临时文件中。写入临时文件受proxy_max_temp_file_size和proxy_temp_file_write_size指令控制。 - 当禁用缓冲时,响应会同步传递给客户端,即在接收到后立即传递。Nginx 不会尝试从代理服务器读取整个响应。Nginx 一次从服务器接收数据的最大大小由
proxy_buffer_size指令设置。也可以通过在X-Accel-Buffering响应头字段中传递yes或no来启用或禁用缓冲功能。此功能可通过使用proxy_ignore_headers指令进行禁用。
1 | Syntax: proxy_max_temp_file_size size; |
及时转发包体
1 | Syntax: proxy_busy_buffers_size size; |
虽然缓存响应,但是也希望更及时向客户端发送响应。此时收到 8k|16k(与机器相关)时,则转发给客户端。
接收上游时网络速度相关指令
1 | # 两次读取上游响应操作之间的超时时间 |
上游包体的持久化
1 | # 将转发的临时文件做持久化处理 |
启用将文件保存到磁盘。on 参数会将文件保存在与指令别名或根路径对应的路径中。off 参数会禁用文件保存功能。此外,可以使用带有变量的字符串来明确设置文件名:
处理上游的响应头部
加工响应内容
当接收到请求,必须经过 HTTP 过滤模块处理,来自上游模块的响应同理。

禁用上游响应头部的功能
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ignore_headers
1 | Syntax: proxy_ignore_headers field ...; |
- 功能:某些响应头部可以改变 Nginx 的行为,使用
proxy_ignore_headers可以禁止它们生效 - 可以禁用功能的头部: “X-Accel-Redirect”, “X-Accel-Expires”, “X-Accel-Limit-Rate” (1.1.6), “X-Accel-Buffering” (1.1.6), “X-Accel-Charset” (1.1.6), “Expires”, “Cache-Control”, “Set-Cookie” (0.8.44), and “Vary” (1.7.7).

转发上游的响应
1 | Syntax: proxy_hide_header field; |
功能:对上游响应中的某些头部,设置不向客户端转发
默认不转发的响应头部:“Date”, “Server”, “X-Pad”, and “X-Accel-…”

1 | yntax: proxy_pass_header field; |
功能:对于已经被 proxy_hide_header 的头部,设置向客户端转发
修改返回的 Set-Cookie 头部
1 | # 修改 cookie 中的域名 |
返回修改的 Location 头部
1 | Syntax: proxy_redirect default; |
替换上游返回的头部中的 Location
上游出现失败时的容错方案
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_next_upstream
当第一台上游服务器返回错误给 Nginx 时,Nginx 的一些容错方案
1 | Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...; |
前提:没有向客户端发送任何内容,如果向客户端发送字节了,代表这个上游已经生效,此时无法选择新的上游服务
配置:(当上游返回以下情况时)
error:例如一些网络错误timeout:请求超时invalid_header:上游返回的header不合法http_:具体的响应码non_idempotent:通常,使用非幂等方法(POST、LOCK、PATCH)的请求不会被传递到下一个服务器,如果已经向上游服务器发送了请求(1.9.13);显式启用此选项允许重试这些请求;off:关闭将请求传递给下一个上游服务器
1 | # 限制请求可以传递到下一个服务器的时间。值为0会关闭此限制。 |
用 error_page 拦截上游失败响应
当上游响应的响应码大于等于 300 时,应将响应返回客户端还是按 error_page 指令处理
1 | Syntax: proxy_intercept_errors on | off; |
对上游服务使用 SSL
双向认证时的指令实例

对下游使用证书
https://nginx.org/en/docs/http/ngx_http_ssl_module.html#ssl_certificate
1 | Syntax: ssl_certificate file; |
验证下游证书
1 | Syntax: ssl_verify_client on | off | optional | optional_no_ca; |
对上游使用证书
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_ssl_certificate
1 | Syntax: proxy_ssl_certificate file; |
验证上游使用证书
1 | Syntax: proxy_ssl_trusted_certificate file; |
ssl 模块提供的变量


创建证书的示例

浏览器缓存与 Nginx 缓存
互联网中,使用缓存可以大大提升访问效率。
- 浏览器缓存
- 优点
- 使用有效缓存时,没有网络消耗,速度最快
- 即使由网络消耗,但对失效缓存使用 304 响应做到网络流量消耗最小化
- 缺点
- 仅提升一个用户的体验(也就是用浏览器这一个用户的体验)
- 优点
- Nginx 缓存
- 优点
- 提升所有用户的体验(作为入口,可以提升所有访问这个入口的用户的体验)
- 相比浏览器缓存,有效降低上游服务的负载
- 通过 304 响应减少 Nginx 与上游服务间的流量消耗
- 缺点
- 用户仍然保持网络消耗
- 优点
- 同时使用浏览器缓存与 Nginx 缓存
浏览器缓存

Etag 头部

https://nginx.org/en/docs/http/ngx_http_core_module.html#etag
1 | Syntax: etag on | off; |
启用或禁用静态资源的“ETag”响应头字段的自动生成。(会使用时间的16进制加上返回的字节数)
生成规则
1 | ngx_sprintf(etag->value.data, "\"%xT-%xO\"", r->headers_out.last_modified_time,r->headers_out.content_length_n) |
If-None-Match

If-Modified-Since 头部

not_modified 过滤模块
功能:用户端拥有缓存,但不确认缓存是否过期,于是在请求中传入 If-Modified-Since 或者 If-None-Match 头部,该模块通过将其值与响应中的 Last-Modified 值相比较,决定是通过 200 返回全部内容,还是仅返回 304 Not Modified 头部,表示浏览器仍然使用之前的缓存。
使用前提:原返回响应码为 200
https://nginx.org/en/docs/http/ngx_http_headers_module.html#expires
1 | Syntax: expires [modified] time; |


https://nginx.org/en/docs/http/ngx_http_core_module.html#if_modified_since
1 | Syntax: if_modified_since off | exact | before; |
off:忽略请求中的if_modified_since头部exact:精确匹配if_modified_since头部与last_modified的值before:若if_modified_since大于等于last_modified的值,则返回 304
If-Match

If-Unmodified-Since

Nginx 缓存
定义存放缓存的载体
配置上游服务器响应的缓存
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache
1 | # 定义共享内存 |


例如
1 | proxy_cache_path /data/nginx/cache keys_zone=cache_zone:10m; |
缓存的关键字
1 | Syntax: proxy_cache_key string; |
缓存什么样的响应
1 | Syntax: proxy_cache_valid [code ...] time; |
- 对不同的响应码缓存不等的时长
- 例如:
code 404 5m
- 例如:
- 只标识时间
- 进对以下缓存码缓存
- 200
- 301
- 203
- 进对以下缓存码缓存
- 通过响应头部控制缓存时长
X-Accel-Expires,单位 秒- 为 0 时标识禁止 Nginx 缓存内容
- 通过 @ 设置缓存到一天中的某一时刻
- 响应头若含有 Set-Cookie 则不缓存
- 响应头含有
Vary: *则不缓存
哪些内容不使用缓存
参数为真,响应不存入缓存(例如有些数据存放在 cookie 中)
1 | Syntax: proxy_no_cache string ...; |
参数为真,不使用缓存内容(即使响应存入缓存,但是不使用)
1 | Syntax: proxy_cache_bypass string ...; |
变更 HEAD 方法
可以将 HEAD 方法变更为 GET 方法
1 | Syntax: proxy_cache_convert_head on | off; |
启用或禁用将 HEAD 方法转换为 GET 以进行缓存。当禁用转换时,缓存键应配置为包括 $request_method。
upstream_cache_status 变量
标识这个缓存的状态

对客户端请求的缓存处理流程

proxy_cache_methods
https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_methods
1 | Syntax: proxy_cache_methods GET | HEAD | POST ...; |
接收上游响应的缓存处理流程
X-Accel-Expires 头部

从上游服务定义缓存多长时间:
0表示不缓存当前响应@前缀标识缓存到当前的某个时间
Vary 头部

Set-Cookie 头部

若 Set-Cookie 头部没有被 proxy_ignore_headers 设置忽略,则不对响应进行缓存。

减轻缓存失效时上游服务的压力
当缓存大量失效或者重启服务,会导致缓存穿透
第一种方式:合并回源请求(也叫单飞、请求收束)

1 | Syntax: proxy_cache_lock on | off; |
同一时间,仅第一个请求发向上游,其他请求等待第一个响应返回或者超时后,使用缓存响应客户端
1 | Syntax: proxy_cache_lock_timeout time; |
等嗲第一个请求返回响应的最大时间,到达后直接向上游发送请求,但不缓存响应
1 | Syntax: proxy_cache_lock_age time; |
上一个请求返回响应的超时时间,到达后再放行一个请求发向上游
第二种方式:使用 stale 陈旧的缓存(也称作降级)

1 | # 配置为 updating 表示使用旧缓存 |
proxy_cache_use_stale 指令:定义陈旧缓存的用法

缓存有问题的响应
1 | Syntax: proxy_cache_background_update on | off; |
当使用 proxy_cache_use_stale 允许使用过期响应时,将同步生成一个子请求,通过访问上游服务更新过期的缓存
1 | Syntax: proxy_cache_revalidate on | off; |
更新缓存时,使用 If-Modified-Since 和 If-None-Match 作为请求头部,预期内容未发生变更时,通过 304 来减少传输内容。
这个过程,跟浏览器和 Nginx 交互的流程一样

及时清除缓存
原生缓存会通过定时器决定缓存是否失效,第三方模块提供缓存立即失效的方法
模块:
- 第三方模块:https://github.com/FRiCKLE/ngx_cache_purge
功能:
- 接收到指定 HTTP 请求后立刻清除缓存
1 | syntax: proxy_cache_purge on|off|<method> [from all|<ip> [.. <ip>]] |
其他协议的反向代理
七层反向代理对照:构造请求内容

建立连接并发送请求

接收上游响应

转发响应

SSL

缓存类指令


独有配置

memcached 反向代理
应用级的反向代理
https://nginx.org/en/docs/http/ngx_http_memcached_module.html
功能:
- 将 HTTP 请求转换为
memcached协议中的get请求,转发请求至上游 memcached 服务 get命令:get <key>*\r\n(协议简单,只能转成get请求)- 控制命令:
<command name> <key> <flags> <exptime> <bytes> [noreply]\r\n - 通过设置
memcached_key变量构造key键
模块默认编译进 Nginx
指令:

websocket 反向代理
https://nginx.org/en/docs/http/websocket.html
让服务器主动向浏览器推送请求
例如:
1 | location /chat/ { |
协议升级

websocket 协议帧


websocket 协议和扩展

用分片提升缓存效率
当上游服务器响应的内容很大,可以通过分片提升缓存效率
https://nginx.org/en/docs/http/ngx_http_slice_module.html
1 | Syntax: slice size; |
功能:通过 range 协议将大文件分解为多个小文件,更好的用缓存为客户端的 range 协议服务
默认没有编译到 Nginx
运行流程:

将文件以分块的形式构建多个请求,将多个请求的分片缓存下来。
1 | location / { |
通过 open file cache 提升性能
https://nginx.org/en/docs/http/ngx_http_core_module.html#open_file_cache
1 | Syntax: open_file_cache off; |
缓存哪些元信息

缓存文件句柄,下次使用时,可以不再重新打开文件。
1 | Syntax: open_file_cache_errors on | off; |
HTTP2 协议
主要特性
- 传输数据量的大幅减少
- 以二进制方式传输
- 标头压缩(主要针对
cookie)
- 多路复用及相关功能
- 消息优先级
- 服务器消息推送
- 并行推送
核心概念

- 连接 Connection:1个 TCP 连接,包含一个或者多个 Stream
- 数据流 Stream:一个双向通讯数据流,包含多条 Message
- 消息 Message:对应 HTTP1 中的请求或者响应,包含一条或者多条 Frame
- 数据帧 Frame:最小单位,以二进制压缩格式存放 HTTP1 中的内容
协议分层

多路复用

传输中无序,接收时组装

数据流优先级

- 每个数据流有优先级( 1-256)
- 数据流间可以有依赖关系
标头压缩

Frame 格式


服务器推送 PUSH

http2
https://nginx.org/en/docs/http/ngx_http_v2_module.html
默认没有编译进 Nginx
功能:对客户端使用 http2 协议提供基本可能
前提:开启 TLS/SSL 协议
使用方法:listen 443 ssl http2;
Nginx 推送资源

1 | # 例如 如果上游头部添加 Link: style.css,则 Nginx 会将 style.css 文件推送给客户端 |
测试 Nginx http2 协议的客户端工具:https://github.com/nghttp2/nghttp2/releases
并发请求控制
1 | # 最大并行推送数 |
超时控制
1 | Syntax: http2_recv_timeout time; |
连接最大处理请求数
1 | Syntax: http2_max_requests number; |
设置响应包体的分片大小
1 | Syntax: http2_chunk_size size; |
缓冲区大小设置
1 | Syntax: http2_recv_buffer_size size; |
grpc 反向代理
协议:https://grpc.io/
模块:https://nginx.org/en/docs/http/ngx_http_grpc_module.html
默认编译进 Nginx 中,依赖 ngx_http_v2_module 模块
指令对照表

对照 SSL 部分

stream 模块
四层网络功能,相比七层网络而言,四层简单很多。
与七层一样,四层也支持代理,四层网络代理是代理 TCP/UDP 请求。
stream 模块处理请求的 7 个阶段

模块:https://nginx.org/en/docs/stream/ngx_stream_core_module.html
1 | Syntax: stream { ... } |
可以看到,四层反向代理和七层反向代理在配置上的区别是:
四层反向代理的配置在 main 下的 stream 配置块中;
七层反向代理的配置在 main 下的 http 配置块中;
传输层相关的变量



Nginx 系统变量

content 阶段
return 模块
1 | Syntax: return value; |
proxy_protocol 协议
网络中流量经过网关和防火墙可能会丢失真实的客户端地址,此时需要有额外协议携带真实地址。

v1 协议中,填写的是:源IP 目标IP 源端口 目标端口
读取 proxy_protocol 协议的超时控制
1 | Syntax: proxy_protocol_timeout timeout; |
stream 处理 proxy_protocol 流程

首先查看 Nginx 配置中是否携带 listen proxy_protocol
post_accept 阶段
realip 模块
https://nginx.org/en/docs/stream/ngx_stream_realip_module.html
功能:通过 proxy_protocol 协议获取出客户端真实地址,并写入 remote_addr 及 remote_port 变量。同时使用 realip_remote_addr 和 realip_remote_port 保留 TCP 连接中获得的原始地址
默认没有编译到 Nginx 中
1 | Syntax: set_real_ip_from address | CIDR | unix:; |
设置可行地址,从可行地址中读取对端地址。
preaccess 阶段的 limit_conn 模块
https://nginx.org/en/docs/stream/ngx_stream_limit_conn_module.html
功能:限制客户端的并发连接数,使用变量自定义限制依据,基于共享内存所有 worker 进程同时生效。
默认编译进 Nginx
1 | Syntax: limit_conn_zone key zone=name:size; |
access 阶段的 access 模块
https://nginx.org/en/docs/stream/ngx_stream_access_module.html
根据客户端地址(realip 模块可以修改地址)决定连接的访问权限。
默认编译进 Nginx 模块
1 | Syntax: allow address | CIDR | unix: | all; |
log 阶段的 stream_log 模块
https://nginx.org/en/docs/stream/ngx_stream_log_module.html
1 | Syntax: access_log path format [buffer=size] [gzip[=level]] [flush=time] [if=condition]; |
stream 模块 TLS/SSL 应用场景

绿色代表网络安全的连接。
Nginx 可以单独为客户端和上游服务提供 TLS/SSL 协议,也可以将与客户端之间 TLS/SSL 协议连接转换为到上游的裸 TCP 连接。同样的,也可以将与客户端的 TCP 协议转换为到上游服务的 TLS/SSL 协议。
stream 中的 ssl
https://nginx.org/en/docs/stream/ngx_stream_ssl_module.html
使 stream 反向代理对下游支持 TLS/SSL 协议。
默认不编译进 Nginx
指令对比 HTTP 模块
steam 中的 SSL 与 HTTP 模块中基本一致。
配置基本参数

提升性能

验证客户端证书

ssl 模块提供的变量


可以基于 stream 四层反向代理的 stream_ssl_module 模块解析 TLS 协议

ssl_preread 模块
https://nginx.org/en/docs/stream/ngx_stream_ssl_preread_module.html
解析下游 TLS 证书中信息,以变量方式赋能其他模块。
提供变量:

preread 阶段:ssl_preread 模块
https://nginx.org/en/docs/stream/ngx_stream_core_module.html#preread_buffer_size
1 | Syntax: preread_buffer_size size; |

通过 ssl_preread 模块,可以获取到下游服务请求的域名,通过域名可以对应代理到不同的上游服务。
反向代理 stream_proxy 模块
https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html
功能:
- 提供 TCP/UDP 协议的反向代理
- 支持与上游的连接使用 TLS/SSL 协议
- 支持与上游的连接使用
proxy protocol协议
proxy 模块对上下游的限速指令

限制读取上游服务数据的速度
https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_download_rate
1 | Syntax: proxy_download_rate rate; |
限制读取客户端数据的速度
1 | Syntax: proxy_upload_rate rate; |
stream 反向代理指令

stream ssl 指令与 http proxy 模块对照

UDP 反向代理
在实时视频会议、音频会议的场景中,使用 UDP 的效果更好。
理论

https://nginx.org/en/docs/stream/ngx_stream_proxy_module.html#proxy_requests
1 | Syntax: proxy_requests number; |
- 指定一次会话
session中最多从客户端接受到多少报文就结束session- 仅会话结束才会记录
access日志 - 同一个会话中,Nginx 使用同一端口连接上游服务(也就是上图中的 端口C)
- 设置为 0 表示不限制,每次请求都会记录
access地址
- 仅会话结束才会记录
1 | Syntax: proxy_responses number; |
- 指定对应一个请求报文,上游应返回多少个响应报文(常见的是 1,也就是一个请求一个响应,但是很多时候也不是)
- 与
proxy_timeout结合使用,控制上游服务是否不可用
- 与
透传 IP 地址
请求通过网络设备后,很多时候可能会丢失原 IP 地址,解决方案有以下几种:
proxy_protocol协议:数据载荷前端带有源IP地址和端口- 修改 IP 报文:IP 报文头里面有原始 IP 地址
- 步骤:
- 修改 IP 报文中的源地址
- 修改路由规则
- 方案:
- IP 地址透传,经由 Nginx 转发上游返回的报文给客户端(TCP/UDP)
- DSR:上游直接发送报文给客户端(仅 UDP)
- 步骤:
修改源 IP 和路由

操作:

DSR 方案
方案1:修改 TCP 中的源 IP 和源端口,修改路由,返回经由 Nginx 出口

方案二:修改 TCP 中的源 IP 和源端口,在上游服务进程中,修改源地址为网关,可去掉路由。

