4. 负载均衡

4.1. nginx扩展方式

  • 基于url对功能进行分发

  • 基于rr算法进行水平扩展

  • 基于ip地址映射到特定ip或者集群

4.2. 支持的反向代理协议

  • udp

  • tcp

  • memcached

  • scgi

  • uwsgi

  • grpc

  • http

  • websocket

4.3. 负载均衡基础指令

指定上游服务地址, 通过upstream指令指定, 内部通过server指令指定server。 server指定的地址, 可以是域名、ip、或者socket地址。不加端口默认是80端口。

官方文档: https://nginx.org/en/docs/http/ngx_http_upstream_module.html#upstream

server片段重要参数说明

  • weight: 权重,默认是1

  • max_conns: 最大活动连接数量,默认是0,也就是不限制。

  • max_fails: 最大失败次数,也就是在fail_timeout这个时间内,如果出现max_fails次数的话,对应的server就不会再次被选择。

  • fail_timeout: 默认是10s

  • backup: 备用, 如果所有的主都不可用才使用这个的。

  • down: 表示这个机器永远不要调度。

  • resolve: 解析域名到地址的。必须使用共享内存的。1.5.12之后才支持动态感知域名变化的。

4.4. 对上游服务使用长连接

降低ningx与上游服务器的建立关闭连接的消耗,提升吞吐量的同事降低延迟。

http1.1才支持, 需要加入如下设置。

proxy_http_version 1.1;
proxy_set_header Connection "";

主要参数说明

  • keepalive : 指定次数。

  • keepalive_requests: 一个连接最多使用的req

  • keepalive_timeout: 一个连接最多使用的时间

4.5. 轮训配置样例

这里我先使用一个nginx启动了server, 测试如下

[root@zhaojiedi-elk-2 nginx]# curl 127.0.0.1:8001
8001 s1
[root@zhaojiedi-elk-2 nginx]# curl 127.0.0.1:8002
8002 s2

配置样例

upstream rrups {
	server 127.0.0.1:8001 weight=2 max_conns=2 max_fails=2 fail_timeout=5;
	server 127.0.0.1:8002;
	keepalive 20;
}

server {
	listen 8084;
	server_name n-rrups.linuxpanda.tech;
	error_log myerror.log info;

	location /{
		proxy_pass http://rrups;
		proxy_http_version 1.1;
        	proxy_set_header Connection "";
	}
}

测试结果

[root@zhaojiedi-elk-2 conf]# curl http://n-rrups.linuxpanda.tech
8001 s1
[root@zhaojiedi-elk-2 conf]# curl http://n-rrups.linuxpanda.tech
8002 s2
[root@zhaojiedi-elk-2 conf]# curl http://n-rrups.linuxpanda.tech
8001 s1
[root@zhaojiedi-elk-2 conf]# curl http://n-rrups.linuxpanda.tech
8001 s1
[root@zhaojiedi-elk-2 conf]# curl http://n-rrups.linuxpanda.tech
8002 s2

启用了keepalive 我们看下tcpdump 抓包的结果

[root@zhaojiedi-elk-2 ~]# tcpdump -i lo port 8001 -n
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes

16:20:09.756199 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [S], seq 1693123386, win 43690, options [mss 65495,sackOK,TS val 299254666 ecr 0,nop,wscale 7], length 0
le 7], length 0
16:20:09.756220 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [.], ack 1, win 342, options [nop,nop,TS val 299254666 ecr 299254666], length 0
16:20:09.756262 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [P.], seq 1:70, ack 1, win 342, options [nop,nop,TS val 299254666 ecr 299254666], length 69
16:20:09.756266 IP 127.0.0.1.vcom-tunnel > 127.0.0.1.42492: Flags [.], ack 70, win 342, options [nop,nop,TS val 299254666 ecr 299254666], length 0
16:20:09.756357 IP 127.0.0.1.vcom-tunnel > 127.0.0.1.42492: Flags [P.], seq 1:171, ack 70, win 342, options [nop,nop,TS val 299254666 ecr 299254666], length 170
16:20:09.756361 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [.], ack 171, win 350, options [nop,nop,TS val 299254666 ecr 299254666], length 0
-----下面是第二次的。
16:20:35.599901 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [P.], seq 70:139, ack 171, win 350, options [nop,nop,TS val 299280510 ecr 299254666], length 69
16:20:35.600018 IP 127.0.0.1.vcom-tunnel > 127.0.0.1.42492: Flags [P.], seq 171:341, ack 139, win 342, options [nop,nop,TS val 299280510 ecr 299280510], length 170
16:20:35.600028 IP 127.0.0.1.42492 > 127.0.0.1.vcom-tunnel: Flags [.], ack 341, win 359, options [nop,nop,TS val 299280510 ecr 299280510], length 0

可以看到第一次是三次握手了, 但是是没有F的falgs的,也就是关闭连接的, 接下里curl的直接传输了。

4.6. 反向代理的hash算法

对于ipv4地址使用前面3个字节作为关键字,对ipv6使用完整的地址。通过ip_hash 进行配置。

当然也可以通过hash key 方式配置非ip方式的。

官方文档: https://nginx.org/en/docs/http/ngx_http_upstream_module.html#ip_hash

配置ip_hash

upstream rrups {
	#ip_hash;
	hash user_$arg_username;
	server 127.0.0.1:8001 weight=2 max_conns=200 max_fails=2 fail_timeout=5;
	server 127.0.0.1:8002;
}

server {
	listen 8084;
	server_name n-hash.linuxpanda.tech;
	error_log myerror.log info;

	location /{
			set_real_ip_from  10.157.0.0/16;
			set_real_ip_from  10.21.0.0/16;
			real_ip_recursive on;
			real_ip_header    X-Forwarded-For;

			proxy_pass http://rrups;
			proxy_http_version 1.1;
        		proxy_set_header Connection "";
	}
}

验证1 ip_hash

[root@zhaojiedi-elk-2 nginx]# curl http://n-hash.linuxpanda.tech:8084 -H "X-Forwarded-For: 1.1.1.1"
8001 s1
[root@zhaojiedi-elk-2 nginx]# curl http://n-hash.linuxpanda.tech:8084 -H "X-Forwarded-For: 1.1.1.1"
8001 s1
[root@zhaojiedi-elk-2 nginx]# curl http://n-hash.linuxpanda.tech:8084 -H "X-Forwarded-For: 1.1.1.1"
8001 s1
[root@zhaojiedi-elk-2 nginx]# curl http://n-hash.linuxpanda.tech:8084 -H "X-Forwarded-For: 1.1.1.1"
8001 s1
[root@zhaojiedi-elk-2 nginx]# curl http://n-hash.linuxpanda.tech:8084 -H "X-Forwarded-For: 1.1.1.1"
8001 s1

验证1 hash key

[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=abc
8001 s1
[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=abc
8001 s1
[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=abc
8001 s1
[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=deaaa
8002 s2
[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=deaaa
8002 s2
[zhaojiedi_dxm@instance-pu4usb9a ~]$ curl http://n-hash.linuxpanda.tech:8084?username=deaaa
8002 s2

4.7. 一致性hash算法

通过hash key consistent 即可。

4.8. 连接做少的上游服务器

4.9. upsteam共享内存

使用共享内存可以将upstream定义的策略数据,状态数据放到共享内存中,对所有worker进程生效。

4.10. upsteam模块提供的变量

  • upstream_addr: 地址

  • upstream_connect_time: 建立了解消耗时间

  • upstream_header_time: 接收上游的头部需要的时间

  • upstream_response_time: 完整耗时

  • upstream_bytes_received: 响应长度

  • upstream_response_length: 包体长度

  • upstream_status: 上游的状态吗, 如果没有连接上,502

  • upstream_cookie_名称: 上游返回的set_cookie取出的值。

4.11. http反向代理的处理流程

../_images/nginx25.png

4.12. proxy模块的基本使用

这个模块是支持http和https协议2种类型的代理的, proxy 后面的url必须以http或者https开头, 接下来是域名、ip 、socket或者upstream名字。前面2个是可以添加端口的。

如果url参数带不带uri差异是比较大的。

  • 不携带uri: 直接转发给上游

  • 携带uri: 将location匹配的部分进行替换。

提前准备一个后端

server {
        listen 8001 ;
        location / {
                return 200 '8001 s1\r\n uri=$uri \r\n';
        }
}
upstream r1 {
	server 127.0.0.1:8001;
}

server {
	listen 8084;
	server_name n-proxy.linuxpanda.tech;
	error_log myerror.log info;

	location /a/b/c {
		#proxy_pass http://r1;
		proxy_pass http://r1/panda;
		proxy_http_version 1.1;
        	proxy_set_header Connection "";
	}
}

验证一下

# 不带uri方式的。
[root@zhaojiedi-elk-2 conf]# curl http://n-proxy.linuxpanda.tech:8084/a/b/c/d/e/f
8001 s1
uri=/a/b/c/d/e/f
[root@zhaojiedi-elk-2 conf]# curl http://n-proxy.linuxpanda.tech:8084/a/b/c/def
8001 s1
uri=/a/b/c/def

# 带uri方式的。
[root@zhaojiedi-elk-2 conf]# curl http://n-proxy.linuxpanda.tech:8084/a/b/c/def
8001 s1
uri=/panda/def
[root@zhaojiedi-elk-2 conf]# curl http://n-proxy.linuxpanda.tech:8084/a/b/c/d/e/f
8001 s1
uri=/panda/d/e/f

4.13. 修改向后端的请求行

  • proxy_method: 修改请求方法

  • proxy_http_version: 修改协议版本。

  • proxy_set_header field value: 修改请求头部。

  • proxy_pass_request_header: 用户的header是否发送。

  • proxy_pass_request_body: body是否发送

  • proxy_set_body: 自定义body方式。

4.14. 接收客户端请求的包体

  • proxy_request_buffering 控制收完在转发还是边收边转发。

  • client_max_body_size: 仅仅对头部含有Content-Length有效超过最大长度后,返回413错误。

  • client_body_temp_path: 临时文件存放目录,这个是存储body信息的。

  • client_body_in_file_only : 请求的包体是否保留的文件中,on必须写的, clean: 处理完毕后删除, off: 非常小,buffer_size 如果够的话,就不写了。

  • client_body_timeout : 两次读取body的最大时延,返回418错误。

4.15. 向上游建立连接

  • proxy_connect_timeout 建立连接,如果超时,502

  • proxy_next_upstream : 特定错误后,可以换一个机器进行调度响应。

  • proxy_socket_keepalive: 保持连接。

  • keepalive: 控制数量

  • keepalive_requests: 控制连接复用次数

  • proxy_bind: 修改源地址。如果非本机地址,需要transparent的参数辅助的。

  • proxy_ignore_client_abort: 客户端和nginx如果取消了, nginx和后端的连接是否断开。

  • proxy_send_timeout : 发送请求的超时时间。

4.16. 接收上游的响应头部

  • proxy_buffer_size 是用来存放对应响应头部的,如果比较大,errlog会出现upstream send too big header。

  • proxy_buffering : 接收完整的响应包体,这个基本同proxy_request_buffering.

4.17. 接收上游的包体

  • proxy_buffers 这个用于存放上游的http包体大小,8 8k参数是如果8k足够就只分配1个8k的, 不够继续分配,最多8个8k。

  • proxy_buffering: 禁用的话,会收到上游响应后立刻同步给客户端响应。如果开启,放到内存,内存放不下,写临时文件。

  • proxy_max_temp_file_size: 写入磁盘的文件的最大值。

  • proxy_temp_file_write_size: 每次写入字节限制。

  • proxy_temp_path: 指定临时文件目录和存储级别。

  • proxy_busy_buffers_size: 及时转发包体

4.18. 接受上游网络速度相关指令

  • proxy_read_timeout: 2次读取超时时间。

  • proxy_limit_rate: 读取上游的速率。

4.19. 包体持久化

  • proxy_store_access: 定义临时文件持久化, 权限设置。

  • proxy_store: 可以使用变量控制存放位置。

4.20. 加工响应头部

  • proxy_ignore_header: 禁用特定字段。

  • proxy_hide_header: 不转发某些头部。

  • porxy_pass_header: 允许哪些头部发送。

  • proxy_cookie_domain: 修改域名

  • proxy_cookie_path: 修改path

  • proxy_redirect: 重定向url

4.21. 上游返回错误的处理办法

  • proxy_next_upstream: 在没有想客户端发送任何内容的时候,就错误的才重新选择新的机器。

  • proxy_next_upstream_timeout: 重试的时间

  • proxy_next_upstream_tries: 重试的次数。

  • proxy_intercept_erros: 是否拦截上游失败响应,如果上游响应>=300的视乎,启用这个选项的话, error_page就会生效了。

准备工作

[root@zhaojiedi-elk-2 nginx]# cat /root/nginx/conf/sites/s1.conf
server {
    listen 8001 ;
    location / {
        return 200 '8001 s1\r\n uri=$uri \r\n';
    }
}
[root@zhaojiedi-elk-2 nginx]# cat /root/nginx/conf/sites/s2.conf
server {
    listen 8002;
    location / {
        return 200 '8002 s2 \r\n';
    }
}
[root@zhaojiedi-elk-2 nginx]# cat /root/nginx/conf/sites/s3.conf
server {
    listen 8003;
    location / {
        return 502 ;
    }
}

openrestry配置如下

upstream ups {
	server 127.0.0.1:8001;
	server 127.0.0.1:8002;
	server 127.0.0.1:8003;
}
log_format ups 'porxy_host="$proxy_host" proxy_port="$proxy_port" upstream_addr="$upstream_addr" upstream_status="$upstream_status"';

server {
	listen 8084;
	server_name n-proxyc.linuxpanda.tech;
	error_log myerror.log info;

	root html;
	error_page 502 500 /a.txt;
	access_log logs/access.log ups ;

	location / {
		
		proxy_pass http://ups;
		proxy_next_upstream error timeout http_502; 
	}
}

验证效果

[root@zhaojiedi-elk-2 nginx]# curl http://n-proxyc.linuxpanda.tech:8084/
8001 s1
uri=/
[root@zhaojiedi-elk-2 nginx]# curl http://n-proxyc.linuxpanda.tech:8084/
8002 s2
# 803是返回502的, 但是我们配置了next的,会调度到别的实例然后拿到结果。
[root@zhaojiedi-elk-2 nginx]# curl http://n-proxyc.linuxpanda.tech:8084/
8001 s1
uri=/
[root@zhaojiedi-elk-2 nginx]# tail -n 10 logs/access.log
porxy_host="ups" proxy_port="80" upstream_addr="127.0.0.1:8001" upstream_status="200"
porxy_host="ups" proxy_port="80" upstream_addr="127.0.0.1:8002" upstream_status="200"
porxy_host="ups" proxy_port="80" upstream_addr="127.0.0.1:8003, 127.0.0.1:8001" upstream_status="502, 200"

4.22. ssl使用场景和双向认证

../_images/nginx26.png

官方文档: https://nginx.org/en/docs/http/ngx_http_ssl_module.html#directives

几个主要连接相关变量

  • ssl_client_serial: 连接上的客户端序列号。

  • ssl_early_data: 在tls3使用early data 且握手为完成返回1 ,否则返回空。

  • ssl_client_verify: 验证失败返回failed:原因,成功返回Success.

  • ssl_session_reused: 如果session复用,返回r,否则为. 。

准备相关证书

# 这里不多弄具体证书创建问题, 需要几个文件,
ca的pem文件
n-ssl1.linuxpanda.tech.key n-ssl1.linuxpanda.tech.crt
n-ssl2.linuxpanda.tech.key n-ssl2.linuxpanda.tech.crt

# 具体操作可以参考: https://www.linuxpanda.tech/en/latest/%E6%9D%82%E9%A1%B9/CA%E7%9A%84%E6%90%AD%E5%BB%BA.html
# 或者: https://www.cnblogs.com/zhaojiedi1992/p/zhaojiedi_linux_011_ca.html

nginx配置

server {
	listen 8011 ssl;
	server_name n-ssl1.linuxpanda.tech;
	
	ssl_certificate     ssl/n-ssl1.linuxpanda.tech.crt;
        ssl_certificate_key ssl/n-ssl1.linuxpanda.tech.key;

	ssl_verify_client on;
	ssl_client_certificate ssl/cacert.pem;

	location / {
		return 200 'ssl_early_data=$ssl_early_data ; ssl_client_verify=$ssl_client_verify; \r\n';
	}
}

openrestry配置

server {
	listen 8011 ssl;
	server_name n-ssl1.linuxpanda.tech;
	
	ssl_certificate     ssl/n-ssl1.linuxpanda.tech.crt;
        ssl_certificate_key ssl/n-ssl1.linuxpanda.tech.key;

	ssl_verify_client on;
	ssl_client_certificate ssl/cacert.pem;

	location / {
		return 200 'ssl_early_data=$ssl_early_data ; ssl_client_verify=$ssl_client_verify; \r\n';
	}
}

验证测试

[root@zhaojiedi-elk-2 sites]# curl http://n-ssl2.linuxpanda.tech:8012/
ssl_early_data= ; ssl_client_verify=SUCCESS;

4.23. 浏览器缓存与nginx缓存

浏览器缓存

优点: 没有网络消耗,速度最快,即使有网络消耗也比较小。 缺点: 仅仅提升一个用户体验。

nginx缓存:

优点: 提升所有用户体验,有效降低后端负载,通过304减少与上游的流量消耗。 缺点: 用户仍然保持网络消耗

建议同时使用浏览器缓存和nginx缓存。

../_images/nginx28.png

4.24. etag

根据文件的修改时间和大小生成。

4.25. if-none-match

它的原理是这样的,当浏览器请求服务器的某项资源(A)时, 服务器根据A算出一个哈希值(3f80f-1b6-3e1cb03b)并通过 ETag 返回给浏览器, 浏览器把”3f80f-1b6-3e1cb03b” 和 A 同时缓存在本地,当下次再次向服务器请求A时,会通过类似 If-None-Match: “3f80f-1b6-3e1cb03b” 的请求头把ETag发送给服务器,服务器再次计算A的哈希值并和浏览器返回的值做比较,如果发现A发生了变化就把A返回给浏览器(200),如果发现A没有变化就给浏览器返回一个304未修改。 这样通过控制浏览器端的缓存,可以节省服务器的带宽,因为服务器不需要每次都把全量数据返回给客户端。

具体参考: https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/If-None-Match

4.26. if-modified-since头部

如果没有超过日期,就返回304,超过就返回200,具体内容就返回。

Warning

如何和if-None-Match一同使用的时候,这个since是不生效的,除非服务器不支持Match的。

4.27. nginx判定缓存是否过期

expire指令:

  • max: 最大的值。cache-control基本10年。

  • off,不添加或者修改过期和cache-control字段。

  • epoch 不设置缓存

  • time 设置具体时间, @18h30m

准备几个文件

[root@zhaojiedi-elk-2 html]# for i in $(seq 1 7) ; do mkdir dir$i;  echo "this is dir$i" >> dir$i/index.html; done
[root@zhaojiedi-elk-2 html]# tree
.
├── 50x.html
├── dir1
│   └── index.html
├── dir2
│   └── index.html
├── dir3
│   └── index.html
├── dir4
│   └── index.html
├── dir5
│   └── index.html
├── dir6
│   └── index.html
├── dir7
│   └── index.html
└── index.html

nginx配置样例

server {
	listen 8084;
	server_name n-cache.linuxpanda.tech;
	error_log myerror.log info;

	root html;

	location /dir1 {
		expires max ;	
	}
	location /dir2 {
		expires @15h30m;
	}

	location /dir3 {
		expires modified +24h;
	}
	location /dir4 {
		expires 0;
	}
	location /dir5 {
		expires -1;
	}
	location /dir6 {
		expires	 epoch;
	}
}

这里对集中情况进行验证

# 案例1,max, 可以看到结果max-age是10年的
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir1/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:49:14 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Thu, 31 Dec 2037 23:55:55 GMT
Cache-Control: max-age=315360000
Accept-Ranges: bytes

#案例2 我当前是11:50的,我设置15h30m 具体这个时间大概220分钟=(3小时 40分钟)
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir2/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:50:00 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Tue, 11 Jan 2022 07:30:00 GMT
Cache-Control: max-age=13200
Accept-Ranges: bytes

#案例3 根据modify修改时间,加一个时间。
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir3/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:52:56 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Wed, 12 Jan 2022 03:45:37 GMT
Cache-Control: max-age=85961
Accept-Ranges: bytes

[root@zhaojiedi-elk-2 conf]# ll ../html/dir3/index.html
-rw-r--r-- 1 root root 13 Jan 11 11:45 ../html/dir3/index.html

# 案例4 max-age=0
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir4/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:53:43 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Tue, 11 Jan 2022 03:53:43 GMT
Cache-Control: max-age=0
Accept-Ranges: bytes
# 案例5 设置负数表示no-cache
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir5/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:54:39 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Tue, 11 Jan 2022 03:54:38 GMT
Cache-Control: no-cache
Accept-Ranges: bytes
# 案例6
[root@zhaojiedi-elk-2 conf]# curl http://n-cache.linuxpanda.tech:8084/dir6/index.html -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 03:55:34 GMT
Content-Type: text/html
Content-Length: 13
Last-Modified: Tue, 11 Jan 2022 03:45:37 GMT
Connection: keep-alive
ETag: "61dcfd61-d"
Expires: Thu, 01 Jan 1970 00:00:01 GMT
Cache-Control: no-cache
Accept-Ranges: bytes

4.28. not_modified

客户端拥有缓存,但是不确定缓存是否过期,请求闯入if-none-match或者if-modified-since 头部, 该模块通过将改值与响应中的last-modified值进行比较, 决定是否返回200还是仅仅302 notmodified头部。

../_images/nginx29.png

4.29. nginx缓存:定义存放的载体

  • proxy_cache zone: 定义使用哪个zone

  • proxy_cache_path: 定义zone

proxy_cache_path指令详细参数

  • path: 定义目录

  • level: 目录级别

  • use_temp_path: 定义临时目录

  • keys_zone: name定义共享内存名字,size是共享内存大小 1MB大约可以存放8k key

  • inactive: 在特定时间没有被访问就会被淘汰掉, 默认10min

  • max_size: 设置最大的缓存文件大小,超出后按照lru淘汰。

  • manager_files: 淘汰的最大文件数量 默认100

  • manager_sleep: 淘汰一次后的休眠时间

  • manager_threshold: 执行一次的最大耗时,默认50ms

  • loader_files: 载入磁盘文件到共享内存的批处理文件数量,默认100

  • loader_sleep: 执行一次缓存文件到共享内存后,进程的休眠时间。

  • loader_threshold: 每次载入文件的最大耗时,默认50ms

4.30. 定义缓存的key

proxy_cache_key : 可以使用变量,定义缓存的key

4.31. 缓存什么请求

proxy_cache_valid:

4.32. 不缓存什么内存

  • proxy_no_cache : 参数为真的时候,响应不存入缓存

  • porxy_cache_bypass: 参数为真,不使用缓存内容。

4.33. upstrream_cache_status变量

  • MISS 未命中

  • HIT 命中缓存

  • EXPIRED 缓存已经过期

  • STALE 命中陈旧缓存

  • UPDATING 内容陈旧单正在更新

  • REVALIDATED nginx验证陈旧内容依然有效

  • BYPASS 响应是从原始服务器获得的。

nginx后端配置

server {
	listen 8004;
	location / {
	#	add_header Vary * ;
	#	add_header X-Accel-Expires 3 ;
		alias html/;
	}
}

openrestry 配置

proxy_cache_path /root/openresty/nginx/cache levels=2:2 keys_zone=two:10m loader_threshold=300 
                     loader_files=200 max_size=200m inactive=1m;

server {
	listen 8084;
	server_name n-cachec.linuxpanda.tech;
	error_log myerror.log debug;

	root html;

	location / {
		# 这里为了可以在响应中看到是否命中,添加个header看看
		proxy_cache two;
		proxy_cache_valid 200 1m;
		add_header X-Cache-Status $upstream_cache_status;

		proxy_cache_key $scheme$uri ;
		proxy_pass http://localhost:8004;
	}
}

验证结果

curl http://n-cachec.linuxpanda.tech:8084/index.html  -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 07:43:32 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Last-Modified: Mon, 29 Nov 2021 07:50:00 GMT
ETag: "61a48628-264"
X-Cache-Status: MISS
Accept-Ranges: bytes

[root@zhaojiedi-elk-2 conf]# curl http://n-cachec.linuxpanda.tech:8084/index.html  -IL
HTTP/1.1 200 OK
Server: openresty/1.19.9.1 (no pool)
Date: Tue, 11 Jan 2022 07:43:35 GMT
Content-Type: text/html
Content-Length: 612
Connection: keep-alive
Last-Modified: Mon, 29 Nov 2021 07:50:00 GMT
ETag: "61a48628-264"
X-Cache-Status: HIT
Accept-Ranges: bytes

[root@zhaojiedi-elk-2 sites]# !tree
tree /root/openresty/nginx/cache/
/root/openresty/nginx/cache/
└── 0a
    └── 0d
        └── 3fc7ad9511bc116105001aa24d260d0a

2 directories, 1 file
[root@zhaojiedi-elk-2 sites]# cat /root/openresty/nginx/cache/0a/0d/3fc7ad9511bc116105001aa24d260d0a

4.34. 缓存流程 发起请求部分

../_images/nginx30.png

4.35. 缓存流程 接手上游响应

../_images/nginx31.png

4.36. 如何减轻上游压力

  1. 合并回源请求 proxy_cache_lock on 同一个时间只能有1个请求。 proxy_cache_lock_timeout: 等待第一个多久, proxy_cache_lock_age: 上一个请求的超时时间。

  2. 使用久缓存返回 proxy_cache_use_stale updating proxy_cache_backgroupd_update on 。

  3. proxy——cache_revalidate on 在请求上游的时候会带header if-modified_since if-none-match等,没有变化就是304减少传输。

4.37. 及时清理缓存

官方的开源是不支持的,plus nginx是支持的, 这里有个模块,可以编译进来。 https://github.com/FRiCKLE/ngx_cache_purge

4.38. 七层反向代理对照表

4.39. redis反向代理

将http请求转换为redis协议中的get请求转发到上游memcached服务中。

配置如下

upstream redis1 {
	server 127.0.0.1:6379;
}
server {
	listen 8084;
	server_name n-redis.linuxpanda.tech;
	error_log myerror.log info;

	root html;
 # GET /get?key=some_key
 location = /get {
     set_unescape_uri $key $arg_key;  # this requires ngx_set_misc
     redis2_query get $key;
     redis2_pass redis1:6379;
 }

 # GET /set?key=one&val=first%20value
 location = /set {
     set_unescape_uri $key $arg_key;  # this requires ngx_set_misc
     set_unescape_uri $val $arg_val;  # this requires ngx_set_misc
     redis2_query set $key $val;
     redis2_pass redis1:6379;
 }
}

样例验证

[root@zhaojiedi-elk-2 sites]# curl 'http://n-redis.linuxpanda.tech:8084/set?key=k1&val=v1'
+OK
[root@zhaojiedi-elk-2 sites]# curl 'http://n-redis.linuxpanda.tech:8084/get?key=k1'
$2
v1

4.40. nginx实现websocket代理

proxy模块提供 官方参考 https://nginx.org/en/docs/http/websocket.html

核心配置片段

proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";

配置

server {
	listen 8085;
	server_name n-websocket.linuxpanda.tech n-ws.linuxpanda.tech;
	error_log myerror.log info;
	access_log logs/ws.log ;

	location / {
		proxy_http_version 1.1;
    		proxy_set_header Upgrade $http_upgrade;
    		proxy_set_header Connection "upgrade";
		proxy_pass http://121.40.165.18:8800;
	}
}

验证

../_images/nginx32.png

如果配置证书的话, 就是wss协议了。 这个样例仅仅支持ws协议。

4.41. 使用分片提升效率

处理大文件的时候,每次都请求全部大小是很消耗资源的事情, 通过range方式配合slice方式即可请求特定部分。

准备一个大文件

dd if=/dev/zero of=big.avi bs=1M count=88

配置一个上游服务

就是简单配置的静态集群的。

配置openrestry

proxy_cache_path /root/openresty/nginx/cache levels=2:2 keys_zone=five:10m loader_threshold=300 
                     loader_files=200 max_size=200m inactive=1m;

server {
	listen 8084;
	server_name n-slice.linuxpanda.tech;
	error_log myerror.log debug;

	root html;

	location / {
		# 这里为了可以在响应中看到是否命中,添加个header看看
		proxy_cache five;
		proxy_cache_valid 200 206 1m;
		add_header X-Cache-Status $upstream_cache_status;

		proxy_set_header  Range $slice_range;
		proxy_cache_key $scheme$uri$slice_range ;
		proxy_pass http://localhost:8005;
	}
}

验证下

[root@zhaojiedi-elk-2 nginx]# curl http://n-slice.linuxpanda.tech:8084/big.avi -r 100-200
[root@zhaojiedi-elk-2 nginx]# tail -n 2 /root/openresty/nginx/logs/access.log
10.157.89.215 - - [12/Jan/2022:10:47:08 +0800] "GET /big.avi HTTP/1.1" 502 173 "-" "curl/7.29.0" "-"request_filename=/root/openresty/nginx/html/big.avi document_root=/root/openresty/nginx/html realpath_root=/root/openresty/nginx/html
10.157.89.215 - - [12/Jan/2022:10:47:55 +0800] "GET /big.avi HTTP/1.1" 206 101 "-" "curl/7.29.0" "-"request_filename=/root/openresty/nginx/html/big.avi document_root=/root/openresty/nginx/html realpath_root=/root/openresty/nginx/html
[root@zhaojiedi-elk-2 nginx]# tail -n 2 /root/nginx/logs/access.log
127.0.0.1 - - [11/Jan/2022:15:47:39 +0800] "GET /index.html HTTP/1.0" 200 612 "-" "curl/7.29.0"
127.0.0.1 - - [12/Jan/2022:10:47:55 +0800] "GET /big.avi HTTP/1.0" 200 92274688 "-" "curl/7.29.0"

4.42. 缓存优化-文件打开

  • open_file_cache: 打开文件缓存的个数,超过的个数使用lru算法。

  • open_file_cache_valid: 文件缓存的有效时间。

  • open_file_cache_errors: 是否缓存文件查询的错误信息。默认off.

  • open_file_cache_min_uses: 具体没有看明白文档。

文件缓存都缓存什么内容

  • 文件句柄

  • 文件修改时间

  • 文件大小

  • 文件查询时候的错误信息(比如文件不能存在,或者没有权限)

  • 目录是否存在

配置如下

server {
	listen 8084;
	server_name n-openfilecache.linuxpanda.tech;

	root html;

	location / {
		open_file_cache max=10 inactive=300s;
		open_file_cache_min_uses 1; 
		open_file_cache_valid 300s; 
		open_file_cache_errors on;	
	}
}

验证

# 这里找到对应nginx的work进程,看看这个具体进程的系统调用
[root@zhaojiedi-elk-2 ~]# strace -p 14160
strace: Process 14160 attached
# 默认卡在wait这个地方,等待连接请求
epoll_wait(13,[{EPOLLIN, {u32=44802384, u64=44802384}}], 512, -1) = 1
# 收到一个请求
accept4(11, {sa_family=AF_INET, sin_port=htons(40254), sin_addr=inet_addr("10.157.89.215")}, [112->16], SOCK_CLOEXEC|SOCK_NONBLOCK) = 4
epoll_ctl(13, EPOLL_CTL_ADD, 4, {EPOLLIN|EPOLLRDHUP|EPOLLET, {u32=44803080, u64=44803080}}) = 0
epoll_wait(13, [{EPOLLIN, {u32=44803080, u64=44803080}}], 512, 60000) = 1
# 接受头部
recvfrom(4, "GET / HTTP/1.1\r\nUser-Agent: curl"..., 1024, 0, NULL, NULL) = 100
# 写响应
writev(4, [{iov_base="HTTP/1.1 200 OK\r\nServer: openres"..., iov_len=255}], 1) = 255
# 发送文件body
sendfile(4, 5, [0] => [1129], 1129)     = 1129
lstat("/root", {st_mode=S_IFDIR|0550, st_size=4096, ...}) = 0
lstat("/root/openresty", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/root/openresty/nginx", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
lstat("/root/openresty/nginx/html", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
write(3, "10.157.89.215 - - [12/Jan/2022:1"..., 232) = 232
setsockopt(4, SOL_TCP, TCP_NODELAY, [1], 4) = 0
epoll_wait(13, [{EPOLLIN|EPOLLRDHUP, {u32=44803080, u64=44803080}}], 512, 65000) = 1
recvfrom(4, "", 1024, 0, NULL, NULL)    = 0
close(4)                                = 0
epoll_wait(13,

4.43. http2主要特性

传输数据量大幅减少

二进制数据传输 标头压缩

多路复用

消息优先级

服务器消息推送

并行推送

4.44. http2核心概念

  • 连接: 1个tcp连接

  • 流:一个双向流

  • 消息: 对应http的请求或者响应

  • 数据帧: 最小单位,以二进制压缩格存放http1中的内容。

4.45. 协议分层和多路复用

../_images/nginx33.png

传输是无序的,接收的时候组装。

优先级1-255

4.46. http2推送文件

  • http2_push_preload 跟进header指定, 上游服务器可以生成header.

  • http2_push 推送哪个url

  • http2_push_max_number: 推送最大数

  • http2_recv_timeout: 如果超过这个时间没有收到请求就关闭

  • http2_idle_timeout: 超过这个时间,就关闭连接

  • http2_max_concurrent_pusher: 最大推送个数

  • http2_max-concurrent_streams: 最大并发流个数

  • http2_max_field_size: header最大大小

  • http2_max_requests: 一个连接最大请求个数

4.47. nginx反向代理grpc

stream模块处理的7个阶段

  • POST_ACCEPT

  • PREACCESS

  • ACCESSS

  • SSL

  • PREFEAD

  • CONTENT

  • LOG

4.48. nginx代理4层样例

4.49. proxy_protocol 协议

v1协议:

proxy tcp4 ip1 ip2 port1 port2 rn

v2协议:

签名+ version_command + 地址族+ 长度

4.50. udp反向代理