Nginx 代理转发

Nginx 现在的代理转发功能已经日趋完善, 可以用来进行大规范的数据负载转发, 比较知名的方案就是 Nginx-Tomcat/Nginx-Jar
的转发负载架构.

常见的 Java 高并发负载也是通过 Nginx 挂起代理转发到不同 Jar 服务进行处理.

Nginx 有两种转发方式:

  • HTTP转发: 只针对 HTTP 数据转发, 可以通过设置代理头转发源客户端数据.
  • Stream转发: 针对 TCP/UDP 的数据流转发, 目前转发的时候无法识别源IP.

如果仅仅作为流量不需要做任何日志和IP判断可以采用 Stream 转发, 剩下只要不涉及这种情况完全不推荐( 应该采用专门的
HAProxy 转发数据 )

Stream转发

这里说明 Stream 转发两种方式: TCP/UDP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 注意 Stream 转发不会涉及 HTTP 模块, 所以不需要在 HTTP 之中编写
http{
.......
}

# 这里直接和 http 模块一样, 创建出新的模块
stream {
# 这里可以追加其他默认配置, 比如重设日志输出格式
log_format proxy '$remote_addr [$time_local] '
'$protocol $status $bytes_sent $bytes_received '
'$session_time "$upstream_addr" '
'"$upstream_bytes_sent" "$upstream_bytes_received" "$upstream_connect_time"';

# 设置默认的日志访问
access_log /var/log/nginx/proxy.log proxy;

# 直接手动引入配置, 这里会默认加载目录下所有 xxxx.conf 后缀的配置文件
include /etc/nginx/stream.d/*.conf;
}

TCP转发

这里就是文件配置, 用于加载主文件获取加载目录下面的配置, 这里演示如何配置 TCP 和 UDP 的转发, 首先是 TCP 转发:

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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# 这里假设采用转发 SSH 的 TCP 协议来演示转发端口数据.
# sudo vim /etc/nginx/stream.d/ssh.conf
upstream ssh_proxy {
# 注意这里有几种并发请求分配算法设置, 不含NGINX商业版本算法.
# `backup` 这个是备份服务, 当所有的服务器宕机无法访问的时候就会转发到该服务.

# 默认轮流算法, 请求会轮流切换分配访问的服务器
server server1.example.com:22;
server server2.example.com:22;
server server4.example.com:22 backup;

# 权重分配算法, 按照权重来选择权重最高的服务器
server server1.example.com:22 weight=3;
server server2.example.com:22 weight=5;

# 客户端IP的哈希算法, 比较老版本暂时不推荐( 不支持 IPv6 和版本不支持 )
ip_hash;
server server1.example.com:22;
server server2.example.com:22;

# 自动分配最少请求服务器, 这种方式最简单方便
least_conn;
server server1.example.com:22;
server server2.example.com:22;

# 手动哈希分配算法, 对指定的某项客户端配置进行哈希分配服务器( 这里采用访问IP地址 )
# `consistent` 参数可有可无如果设置则默认采用一致性哈希, 能够更快命中某些缓存服务( Memcache/Redis )
hash $remote_addr consistent;
server server1.example.com:22;
server server2.example.com:22;

# 一致性哈希分配, 这里是简化版手动哈希分配( 根据 HTTP-GET 地址哈希比较选择 )
consistent_hash $request_uri;
server server1.example.com:22;
server server2.example.com:22;

# 负载检测分配, 这个方法采用后台服务器转发响应时间作为依赖, 并非可信的完全负载判断.
server server1.example.com:22;
server server2.example.com:22;
fair;

# 这里支持的服务器方式可以支持以下方式
# 支持最大错误次数( max_fails=3 ), 每次最大错误的超时时间( fail_timeout=30s )
# 错误判断用于给服务器识别异常从而转发下一级的服务器( 超时/无法访问等情况 )
server server1.example.com:22 max_fails=3 fail_timeout=30s; # 域名服务器
server 127.0.0.1:22 max_fails=3 fail_timeout=30s; # IP服务器
server unix:/tmp/ssh.sock max_fails=3 fail_timeout=30s; # UnixDomain服务器
}

# TCP 挂起服务器连接池
server {
# 设定监听端口, 可以采用多配置项目, 这里说明几个关键配置
# `reuseport`, 开启了端口快速复用缓解 TIME_WAIT 回收端口请求资源过慢导致请求
# `udp`, 声明该代理的是 UDP 服务
# 以上都是比较简单的配置项, 日常基本上就这些参数配置需要注意
listen 10022 reuseport;

# 启用 TCP 的 NODELAY 特性
# 网络默认 TCP 启用 `Nagle` 算法, 该算法通过减少传输的数据包( 读写缓冲区集中发放 )
#
# `Nagle` 算法的好处: 减少传输的数据包数量, 将传输数据集中一起统一发送, 从而有效提高网络利用率.
# 但是坏处也是明显, 数据并不会实时传输具有数据传输延时的问题( 因为数据必须等到写缓冲区满足条件发送 )
#
# `TCP_NODELAY` 禁用 `Nagle` 算法的好处: 直接实时发送对应数据包, 能够有效降低数据传输的延时
# 坏处则是会频繁实时发送数据包, 网络会频繁处于活跃状态导致网络利用率低下.
#
# 当然如果想要实时传输且提高网络利用率, 则可以采用编写自定义UDP传输协议处理
tcp_nodelay on;

# 设定连接的超时时间( CONNECT 状态 )
proxy_connect_timeout 3s;

# 设定代理的请求超时时间( WAIT 状态 )
proxy_timeout 15s;

# 调用指定的后端服务池
proxy_pass ssh_proxy;
}

# 也可以直接设置转发配置
server {
listen 20022;

# 直接简单的转发配置
proxy_pass 127.0.0.1:22;
}

这里配置方式基本上是常见配置, 剩下可以参考官方文档来配置.

UDP转发

UDP 转发配置基本上也差不多, 所以直接采用简单的配置说明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 这里假设采用转发 DNS 的 UDP 协议来演示转发端口数据.
# sudo vim /etc/nginx/stream.d/dns.conf
upstream dns_proxy {
# 自动分配最少请求服务器, 这种方式最简单方便
least_conn;
server dns1.example.com:53;
server dns2.example.com:53;
}

# 转发 DNS 服务
server {
# 设置监听端口和端口重用/UDP转发声明
listen 53 reuseport udp;

# 直接简单的转发配置
proxy_pass dns_proxy;
}

UDP 基本上配置和 TCP 差不多.

HTTP转发

这种方式转发比较常规, 被广泛应用在 Web 应用高并发处理, 能够有效起到负载分流功能( 这种又称 反向代理 ):

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
50
51
52
53
54
55
56
57
58
59
60
# 注意, 这里只在 http 配置块配置
http{
......

# 这里一般都是加载 /etc/nginx/conf.d/xxx.conf 之中配置
# 创建代理服务池服务
upstream tomcat_service {
# 采用自动分流功能
least_conn;

# 假设内网多服务器分流, 设置最大错误和错误时间
server 192.168.1.100:8080 max_fails=2 fail_timeout=15s;
server 192.168.1.102:8080 max_fails=2 fail_timeout=15s;
server 192.168.1.104:8080 max_fails=2 fail_timeout=15s;
server 192.168.1.108:8080 max_fails=2 fail_timeout=15s;

# 追加双机宕机备份
server 192.168.1.110:8080 max_fails=2 fail_timeout=15s backup;
server 192.168.1.112:8080 max_fails=2 fail_timeout=15s backup;
}

# 挂起 http 服务器监听
server {
# 设置监听端口
listen 80;
# 设置服务器访问域名
server_name www.example.com;
......

# 这里需要设置 localhost 块
location / {
# 声明代理转发给服务器池
proxy_pass http://tomcat_service;

# 设置是否启用服务重定向, 默认 off 不启用代理转发
proxy_redirect off;

# 将客户端请求的域名转发给代理的服务器, 让接受代理的服务器可以识别出客户端访问的域名
# 如果采用多端口请求的服务, 需要追加端口信息 `$server_port`, 如下配置:
# proxy_set_header Host $host:$server_port;
proxy_set_header Host $host;

# 将域名追加请求协议头
# `$proxy_add_x_forwarded_for` 该变量会将代理机和客户端IP一起合并( 逗号区分 )发放.
# `$remote_addr` 则是直接把客户端IP写入而不会加上代理IP
# 两者按需选择
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-real-ip $remote_addr;

# 设置 CONNECT(连接) 状态超时时间( 默认单位: 秒,60s )
proxy_connect_timeout 60s;

# 设置 SEND(请求发送) 状态超时时间( 默认单位: 秒,60s )
proxy_send_timeout 60s;

# 设置 READ(请求发送) 状态超时时间( 默认单位: 秒,60s )
proxy_read_timeout 60s;
}
}
}

WebSocket转发

WebSocket 转发则比较特别, 虽然是长连接请求但是内部配置是在 Http 配置块.

Nginx 代理转发要求 1.3+ 版本

配置示例如下:

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
# 注意, 这里只在 http 配置块配置
http{
......

# 这里一般都是加载 /etc/nginx/conf.d/xxx.conf 之中配置
# 创建代理服务池服务
upstream websocket_service {
# 采用自动分流功能
least_conn;

# 假设内网多服务器分流, 设置最大错误和错误时间
server 192.168.1.100:10800 max_fails=2 fail_timeout=15s;
server 192.168.1.102:10800 max_fails=2 fail_timeout=15s;
}

# 挂起 http 服务器监听
server {
# 设置监听端口
listen 1080;
# 设置服务器访问域名
server_name ws.example.com;
......

# 这里需要设置 localhost 块
location / {
# 声明代理转发给服务器池
proxy_pass http://websocket_service;

# 将客户端请求的域名转发给代理的服务器, 让接受代理的服务器可以识别出客户端访问的域名
proxy_set_header Host $host:$server_port;

# 将域名追加请求协议头
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-real-ip $remote_addr;

# 以上都是常规的配置, 之后就是专属的 WebSocket 配置
# 以下三行可以直接简单将 http 请求升级成 ws 请求.
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
}
}