TCP长连接及连接管理探讨

TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。 TCP生命周期分为三个阶段: 建立连接、传输数据、关闭连接。 根据使用方法不同,可认为TCP连接分长连接和短连接。 如果每次通信后就close掉连接,那TCP就相当于短连接。 如果请求结束后保持TCP连接的状态不关闭,那TCP就是长连接。

一、为什么使用长连接

  • TCP连接的建立需要3次握手,断开需要4次挥手。 使用长连接可以减少连接的建立次数,减少CPU及内存的使用

  • 实现pipelining模式

  • 实现服务端push数据给客户端

  • 减少TCP请求,减少网络堵塞

  • 减少后续请求的响应时间

  • 减少了三次握手和四次挥手的时间

单线程1000次请求数据对比:

  • TCP 短连接用时:4.6s

  • TCP 长连接用时:2.5s

二、长连接会有什么问题

维护成本高,随着服务的运行,建立的连接会越来越多,最终可能导致服务器不堪重负。因此如何管理连接和清理死连接(不活跃)就显得尤为重要。

(1)心跳检测

  • TCP 层:KeepAlive机制

  • 应用层:心跳

TCP keepalive机制

  • tcp keepalive time:在链路上没有数据传送的情况下,tcp keepalive time秒后触发tcp心跳

  • tcp keepalive intvl:心跳每个tcp keepalive intvl秒发送一次

  • tcp keepalive probes:发送tcp keepalive probes次都无响应后将断开连接

KeepAlive本质

通过抓包数据来看,是 TCP 发送一个数据长度为0的空包,ack最后一个包的序号,这样对应用层无影响。

默认情况下,是关闭 TCP 的KeepAlive机制的。

go打开KeepAlive的方法:

(2)连接管理

目前主要有2种连接管理方式:

  • 基于 ip:port 的连接池

    ip:port为key,缓存一组连接

  • 基于 seq 的连接池管理

    需要应用层协议支持,客户端在请求包中加入seq,收到返回包后根据seq找到相应的回调方法。

连接池

bingo服务端使用的tcp层keepalive机制检查连接状态,同时服务端支持pipeline的方式处理请求。因此在客户端可以使用连接池来复用 TCP 长连接,避免客户端创建大量的连接。

常见实现方式:

  • map+chan

  • map+list

使用 map + chan的方式实现

  • conn pool:负责管理conn set

  • conn set:一个ip:port拥有一个conn set,负责申请连接、释放连接的具体实现,使用chann缓存连接

2个参数&2个方法

  • maxIdleSize:一个ip对应的最大空闲连接数。等于channel的大小

  • maxIdleTime:最大空闲时间,连接超过这个时间可认为是不活跃连接,可以直接关闭

  • Alloc:申请一个连接

  • Reclaim:把连接返回连接池,连接在使用过程中若发生error,则应该close掉连接,不应该把该连接返回连接池

申请连接时,会优先从连接池中获取链接,如果获取失败或尝试次数达到上限后才会创建新连接。

数据对比

(1)不使用连接池

  • 单协程请求总数:100

  • 请求间隔时间:0~50ms

  • 协程数:500

  • 成功:51636

  • 失败:3364

  • 端口占用:50000

(2)使用连接池

  • 单协程请求总数: 500

  • 请求间隔时间: 0~50ms

  • 协程数: 50

  • 成功: 50000

  • 失败: 0

  • 端口占用: 5~10

优点:

1.代码实现比较简单。

2.上层协议无关,任何协议都可以接入连接池。

缺点:

  1. 高并发下占用过多的fd。

  2. 如果请求间隔时间为0,占用端口数=协程数。

基于seq的连接池管理

这种方式通常用于异步rpc调用

客户端会缓存seq->channel(存放返回包、用于回调),

客户端发包在包头中填写seq值,服务端会将seq字段原封不动的带回来,

然后通过seq查找到对应的channel,将数据放进去。等待数据的协程就可以接着处理请求了。

这种方式管理连接,通常发送请求包和接受返回包是不同的进程(写成),通过map

来共享数据,完成回调等操作。

jungle中返回包处理代码:

交互流程

如果conn本身是协程(进程)安全的,在alloc连接时,连接池可以不把此连接清除掉。这种情况下连接池的主要作用是根据策略做连接的负载均衡。

优点:

  1. 连接复用,理论上一个ip+port可以只对应一个连接。

  2. 基于seq复用,节省fd。

缺点:

  1. 实现复杂,异常状态多,超时、连接断开不可用等处理逻辑复杂。

  2. 需要应用层协议支持。

TCP长连接相对短连接,传输速度快,可是实现一些高级功能,如:server可以主动发送数据给client。但是需要服务端维护连接,增加系统的复杂性。保持的连接也会占用和多系统资源,带来更多的风险。因此具体选择哪种技术,还需要根据具体情况做权衡。