tcp连接迁移

意义

搜了一下好像并没有文章讲这件事,网上的tcp迁移貌似讲的都是网关,跟upstream直连偷偷换连接(偷改四元组,类似负载均衡);;不过我说的可能跟其他人看到标题时候想的也不一样,实际主要说的是热更新tcp长链不断。

mosn

主要也是看了mosn(蚂蚁版本envoy)源码,这块挺有意思的。
实际上我也没有完全看明白它的实现,里面代码封装太多层(抽象,也不好调试)。比方说,conntion迁移的时候是和reader buffer的数据一起过去了,如何避免中间时间差的read buffer;我只是翻懂了大致的思路,然后自己写出来一个(写法上有一些差异)。

热更新

关于热更新:类似nginx或者go http服务的热更新,一般都比较熟知大致流程;
新进程继承listen port(子进程继承),并且接受新的http请求;
旧进程上面已存在的http请求正常运行至结束,旧进程在所有http请求关闭后自动退出;
所以新旧进程短时间可能会同时存在运行。

可以拿nginx和python来做个试验。

nginx_python

第一步:可以看到Nginx监听了80端口,没有任何连接过来
第二步:利用python的requests库发http请求,注意调用session()是为了保持tcp暂时不断
第三步:可以看见tcp已经保持不断,随后nginx -s reload平滑重启nginx,发现旧进程上面的长链tcp服务器端主动断了

tcp长链不断

新旧进程间如何保证tcp长链不断?
主要利用Linux可以发送 control message 格式,来传递fd(包括listener/tcp conntion 都是可以的)
需要注意一些细节,例如read buffer传过去的时机;又或者因为现在的tcp是双工的,所以对于writeconntion是不需要重复传输conntion的,readconntion已经发送过一遍了。

reply

我写了一个极小demo,留作记录
我的程序会指定监听端口,当客户端连接上来的时候,无论发送过来,只有看到'#',就会把字符串截取加上指定的前缀吐出去

举个栗子

// 程序编译、运行  
go build -o main  
./main -l :8081 -p hello 
// 然后就不用看上面服务端的日志,主要看下面客户端返回

// 客户端使用nc连接并发送数据  
nc 127.0.0.1 8081  
// 发送
123#456#789  
// 收到
hello reply : 123#  
hello reply : 456#  


因为789后面没有'#'号,所以程序并没有把 789 返回

// 这个时候启动新的进程
./main -p world

//并且把`789`后续的`#`补上发出去,你会发现nc收到了 
world reply: 789#

// 最终旧进程平滑关闭


全流程下来 nc 与服务端的连接没有断链(可以任意中途netstat -anp查看连接),tcp保持长链无感

reply.png

附件

代码下载地址

附一份代码的流程图,防止忘了

cm2.png