现在nginx的版本已经到了1.9.12,不过在1.9.0的时候,就开始了对TCP代理服务器的支持。
之前也有一些早期的做法,比如就有写了nginx_tcp_module模块的
https://github.com/yaoweibin/nginx_tcp_proxy_module,
以及nginx_stream_lua模块的
https://github.com/openresty/stream-lua-nginx-module本来就想着用这两个的,后来发现已经有支持了,就先来试试看看这个了。
默认的话,nginx是没有开启对tcp的支持的.如果要支持的话,需要用--with-stream去指定。
我这边只要需要三个IP,一个是nginx进程所在的机器,我这边用192.168.1.4表示,
另外两台就是TCP上游业务服务器,我们分别用192.168.1.5和192.168.1.21来表示,
基于这样的考虑是因为nginx充当tcp代理服务器后,自身也会占用一个端口。
并且从目前来说,貌似一个nginx进程也就只能listen一个端口。
我用的版本是nginx 1.9.9.
我在/usr/local/nginx/conf下面新建了nginx_stream_tcp.conf作为本次的测试。
worker_processes auto;
2 error_log /usr/local/nginx/logs/error.log info;
3 events {
4 worker_connections 1024;
5 }
6
7 stream {
8 upstream loadbalance {
9 # Specifies a load balancing method for a server group where client-server mapping is based on the hashed key value
10 hash $remote_addr consistent;
11 # sets the weight of the server, by default, 1.
12 server 192.168.1.21:8001 weight=5;
13 # by default, the parameter is set to 10 seconds.
14 server 192.168.1.5:9001 max_fails=3 fail_timeout=30s;
15
16
17 }
18
19 server {
20 # listen 8001.
21 listen 8001;
22 # Defines a timeout for establishing a connection with a proxied server.
23 proxy_connect_timeout 1s;
24 # Sets the timeout between two successive read or write operations on client or proxied server connections
25 proxy_timeout 3s;
26 # Sets the address of a proxied server
27 proxy_pass loadbalance;
28 }
29
30 }
这个大部分是官网支持的,所以其实我也没有修改太多,就设置了几个IP和端口。
在编译好nginx后,可以用/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx_stream_tcp.conf
就可以起来了,可以通过netstat -tlnp查看是否起来,大致显示这样的:
tcp 0 0 0.0.0.0:8001 0.0.0.0:* LISTEN 7744/nginx_stream_t
当然也可以通过telnet来查看。
接下来,写个简单的cs程序来验证下吧.
server端:
if __name__ == '__main__':
import socket
import commands
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind(('192.168.1.21', 8001))
sock.listen(5)
while True:
connection,address = sock.accept()
print "connected by",address
while 1:
buf = connection.recv(1024)
if buf != "" :
print "connection buffer", buf
connection.sendall(buf)
connection.close()
client端:
if __name__ == '__main__':
import socket
HOST='192.168.1.4'
PORT=8001
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((HOST, PORT))
while 1:
cmd = raw_input("please input cmd:")
sock.sendall(cmd)
data = sock.recv(1024)
print data
sock.close()
在client向192.168.1.4发起一个TCP连接时,接下来192.168.1.4会连接到192.168.1.21,
192.168.1.21的server端会显示, connected by ('192.168.1.4',58559)
后面的端口应该是随机的临时端口。
其实它的确起到了转发作用,这个nginx进程相当于一个通用网关,如果有两个client向nginx进程发起连接,一旦nginx那边调度到同一个上游TCP服务器,那么只需要从NGINX进程建立一个TCP连接到上游TCP服务器即可。
之前我自己设计网关的时候,不是依赖于用户的,而是通过静态配置来建立连接的,比如上游逻辑服务器跟网关有连接,那都是在启动服务器的时候,就建立起来了。
这样有个好处就是可以省事,不过也可以采用这种方式,有玩家连接进某个上游TCP服务器才让网关跟这个TCP服务器建立连接,否则无需处理。因为不少时候还是会有不少服务器处于空闲的.