nginx功能 - 流量分流

问题

我有2个问题一直没有想明白.

假设我做了一个项目, 开放了一些接口给用户使用. 程序源源不断的接收到请求. 然后开始处理数据.
某天. 我发现一个程序bug. 我需要修改程序. 然后需要重启.

此时会有2个问题:

  • 问题1. 重启过程中, 用户的请求怎么办, 直接丢失了吗
  • 问题2. 如果已经接收的请求. 程序还在处理中, 直接停服似乎不太妥当.

我能想到一个办法, 直接部署新旧2个程序. 然后使用 gateway , 前置拦截流量. 再按权重分配给2个程序.

但是有一天gateway程序也会重启. 很明显这不是一个好办法.

今天网上的一句话让我豁然开朗.

无论的你是单体应用还是微服务, 你都应该部署一个 nginx 拦在最前面.

  • 我直接使用 nginx 拦在最前面. 底下无论是直连程序还是再进入业务网关. 随便你程序如何修改, 如何升级. nginx无需重启. 只需加载配置. 用户请求我可以随心所欲的控制分配.

流量分流测试

nginx最大的优点就是配置方便. 支持热更新.

  • 环境如下

nginx监听端口为8081

假设我部署2个服务分别占用端口9999和9998

按比例分配流量

  • 程序A 接收90%流量
  • 程序B 接收10%流量

nginx配置如下.

1
2
3
4
5
6
7
8
9
10
11
12
upstream backend {
server 127.0.0.1:9999 weight=90; # 程序A的地址和端口 权重
server 127.0.0.1:9998 weight=10; # 服务B的地址和端口 权重
}

server {
listen 8081;
server_name 127.0.0.1;
location /yxhis {
proxy_pass http://backend/yxhis/;
}
}

执行命令重新载入配置 nginx -s reload

测试发现, 5次请求中果然有4次去了程序A, 1次去了程序B

流量全部打到程序B

修改配置

1
2
3
4
5
6
7
8
9
10
11
12
13
upstream backend {
# 直接注释程序A的端口
#server 127.0.0.1:9999 weight=90; # 程序A的地址和端口 权重
server 127.0.0.1:9998 weight=10; # 服务B的地址和端口 权重
}

server {
listen 8081;
server_name 127.0.0.1;
location /yxhis {
proxy_pass http://backend/yxhis/;
}
}

执行命令重新载入配置 nginx -s reload

然后发现, 5次请求中果然全部去了程序B

简直太方便了.

nginx.conf完整的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

worker_processes 1;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;

keepalive_timeout 65;


upstream backend {
server 127.0.0.1:9999 weight=90; # 旧服务的地址和端口
server 127.0.0.1:9998 weight=10; # 新服务的地址和端口
}

server {
listen 8081;
server_name 127.0.0.1;
location /yxhis {
proxy_pass http://backend/yxhis/;
}

# 测试前端
location /# {
root html;
index index.html index.htm;
proxy_pass http://127.0.0.1:9527/;
}

}

}


记录转发日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
worker_processes  1;

events {
worker_connections 1024;
}

http {
# 记录请求和响应的出入参
log_format upstremmain escape=json '【请求】$remote_addr 【时间】[$time_local] 【转发】$upstream_addr -$request'
'【入参】$request_body 【时长】$upstream_response_time';

access_log logs/access.log upstremmain;

include mime.types;
default_type application/octet-stream;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;

upstream backend {
server 127.0.0.1:9999 weight=50; # 旧服务的地址和端口
server 127.0.0.1:9998 weight=50; # 新服务的地址和端口
}

server {
listen 8081;
server_name 127.0.0.1;

location /yxhis {
proxy_pass http://backend/yxhis/;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy true;

# 记录请求的出入参
proxy_set_body $request_body;
}

# 测试前端
location /# {
root html;
index index.html index.htm;
proxy_pass http://127.0.0.1:9527/;
}
}
}