一个TCP连接池的自我修养-如mysql-client、http-client、redis-client

问题:单服单天过亿请求中,出现0.0001%的500怎么回事?

  • 日志中不定时出现 mysql gone awayconnect timeout, reset by beer等错,原因不明。
  • access-log同样不定时出现5xx错。
  • 感觉MySQL运行正常,没压力,没慢查,sql使用主键查询–不知道问题在哪?
  • 连接MySQL的代码使用keepalive。

这个时候,你是否怀疑过是连接库的问题?

一个TCP连接池的自我修养

  • keepalive特性,idle-connection可设定。
  • one retry 特性。
  • max-connection 设定。
  • read/write timeout 设定。

max-connection 特性是必需的

如果是mysql连接库,max-connection不设上限,要不就是造成后端的mysql连接过多,拖跨mysql。
如果是http连接库,就会造成服务应用大量并发请求在等待,不论阻塞、非阻塞同样会。即使像go语言,这样原生性非阻塞、轻并发语言。大量的等等请求,一样会有耗尽内存的风险。换是python、php一个线程就以M为单位内存计算的,并发更低。
所以必需带有合理的 setMaxConnection() 接口。

非常重要的容错

当连接池满载时,一个新线程请求一个新连接时,采用什么策略?
等待?
还是直接返回try-later?

read/write timeout 也是必需的

任何的TCP连接,都可能出现的读/写超时,对上层来说就是出错了,如果timeout很长,即直接阻塞了此线程或微线程。大量阻塞同样地引发内存风险,也不存在语言上的差别。所以,合理的timeout值,特别是read timeout,经常地出现一种,对方服务器处理慢,不能及时返回结果,而client端断开后,对方继续完成了工作,而client方以为没完成,而重发请求,如果没防重入,会造成数据不一致。 为什么重发?参考one-retry特性说明。

keepalive+ idle-connection 作为可选特性

追求高性能,TCP的keepalive,连接重用是必需的。
而idle-connection作为闲时保持最低的连接数量,当上层业务要使用时马上可用。
还有一种做法是 connection-keep-timeout, 即每个连接闲下来后,多长时间后自动关闭。
MySQL中就可以使用 set timeout=n; 显式设置每个连接的timeout值,否则使用服务端默认统一值。

one retry 特性,在keepalive打开时,必需加上

one-retry特性,并不是每个程序员都注意并知道的一个常规特性。
而此特性正是解决 我们项目中 0.0001% 5xx 问题 的关键。
一旦开启idle-connection特性后, TCP客端端会保持一定数量在连接在连接池中,上层要使用时返回一个连接。
而像MySQL的通信协议中是有Ping指令的,就是用于测试连接是否有效。所以当返回一个连接时,如果是MySQL,就可以用Ping测试一下,确保返回给上层的是可用连接。
原因是连接池中的连接不一定有效,即使你代码中监听了socket的close事件,但由于服务端可能使用了LVS这样的虚拟IP,很可能出现服务端单方面断开了TCP连接,客户端是没法感知(注:这里并不是说使用LVS就会有这种现象,只能用于举例说明,服务端可能使用了客户端无法感知的连接层)。除此以后,TCP断开时4次握手不完全,也是很正常的现象。
为了上层能获得一个可用连接池,没有ping协议的HTTP协议怎么办?
可以使用one-retry特性:
上层发送一个请求调用,由 连接池 执行, 如出现 reset by beermysql was gone away 情况的,可以新建一次连接,重发一次请求 。我把这种行为称为one-retry, 上层不必感知。若重试的请求,也出来同样的错误,就应该向上层返回或异常了–视语言而定。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章