阿里云数据库工程师也收到了客户的反馈,于是我们开始深入排查,持续跟踪解决这个问题。最终,就在9月份,这个问题得到了有效解决。下面我们使用Redis的标准版本架构来描述这个问题(注意,即使在非云环境下这个问题仍然存在)。
图1 Redis标准版双副本切换流程
在Redis标准版架构中,开源SDK通过域名解析获取VIP地址,与Ali-LB建立连接,然后与Redis Master建立连接(图中1’和1连接对应)。
当Master因意外故障直接宕机时,有概率不会产生RST。
HA组件检测到Master宕机,调用Ali-LB switch_rs接口将后端连接从Master切换到Replica。
切换完成后,Ali-LB不会主动释放前端旧的客户端连接。客户端发送给Ali-LB的数据包会因为后端不可用而默认被丢弃,因此客户端会持续超时。这时候如果建立新的连接(比如4',会建立到新的Master的连接),不会有问题,但是Lettuce客户端超时后不会重新建立连接,所以有是旧连接的问题。
直到Ali-LB的est_timeout(默认900s)达到,Ali-LB会回复RST断开连接,然后客户端恢复。注意:对于某些网卡故障、网络分区故障等情况,概率上不会生成RST。大多数停机时,操作系统在退出前都会向客户端发送RST,因此只有在切换或停机后才不会出现此问题;在正常切换的情况下,由于Master可以服务,因此在步骤3中,HA组件会主动向旧Master发送客户端kill命令,让客户端发起重连恢复。
二、问题分析
首先,这是Lettuce 客户端的设计缺陷。原因稍后在与其他客户的对比分析中阐述。
其次,这是Ali-LB的一个不成熟的机制(切换后保持沉默,不关闭与Client的连接),所以所有使用Ali-LB的数据库产品都会遇到它,包括RDS MySQL等。
由于900s的不可用对Tair影响很大,比如用户有10000个QPS,那么900s涉及到1000万左右的QPS,所以我们是第一个推动这个问题的解决方案。
2.1 为什么 Jedis 和 Redisson 客户端没有问题?
Jedis是连接池模式。底层超时后,当前连接将被销毁。下次重新建立连接时,它将连接到新的交换节点并恢复。
Jedis连接池模式
尝试{ jedis=jedisPool.getResource(); //查询前获取连接//jedis.xxx //执行操作查询} catch (Exception e) { e.printStackTrace(); //超时、命令错误等} finally { if (jedis !=null) { //这里关闭,如果连接正常则返回连接池//如果连接异常则销毁连接jedis 。关闭(); }}
Redisson本身支持每隔一段时间向服务器发送ping来确定它是否还活着。如果连接失败,则会发起重连。
Redisson的PingConnectionInterval参数
//PingConnectionInterval: 是向服务器发送PING 数据包的时间间隔(以毫秒为单位)。在此连接上,如果失败,请重新连接。默认为30000config.useSingleServer().setAddress(uri).setPingConnectionInterval(1000);RedissonClient connect=Redisson.create(config) ;
2.2 能否通过配置 TCP 的 KeepAlive 来保活?
结论是不行,因为TCP Retransmission Package的优先级高于KeepAlive,即如果是活动连接,出现这个问题时,会先启动TCP Retran,具体取决于tcp_retries23参数(默认15次,需要924.6秒)。
图2 主动连接黑洞问题流程图
T1:客户端发送set key value给Ali-LBT2:Ali-LB回复okT3:客户端发送get key给Ali-LB,但此时后端发生切换。之后阿里-LB无任何响应,客户端性能超时。 T4:开始第一次tcp retranT5:开始第二次tcp retranT6:此时仍然是tcp retran,但是因为到达了Ali-LB est_timeout时间,所以Ali-LB回复RST,客户端恢复。如果Ali-LB始终不回复RST,重传完成后TCP会主动断开重连,也可以恢复。
三、问题解决
3.1 紧急止血
由于没有其他有效的方法,我们只能将est_timeout调整为120s(不能更小,否则正常静默连接会被断开),这意味着用户最多将受到损害135s(120s) + 15s检测,请注意:不可用后,必须检测到才可以发起切换)。
官网文档不建议用户使用Lettuce。
3.2 客户端侧修复
尝试一:为 Lettuce 添加 PingConnectionInterval
我们在上面的分析中提到,如果客户端想要解决这个问题,就需要在应用层实现一个活动判断机制。简而言之,客户端将活动确定数据间接插入到与服务器的连接中。包,请注意,这里使用的连接必须是客户端和服务器之间已有的连接,不能是单独的新连接。否则就会误判,因为问题出在连接维度上的黑洞。如果使用新的连接来判断,那么服务端会返回正常的结果。
这种修复方法比较复杂。由于Lettuce支持Command Listener,因此他认为Command超时后用户可以自行关闭连接。 Redis本身有一些Block命令,例如xread和brpop。此时连接已挂起,无法进行探索。
沟通后,我们拒绝用户通过Command Listener的方式自行关闭连接,因为修改比较复杂。这意味着每个用户都必须更改代码才能安全地使用Lettuce,成本会非常高。但是,此解决方案无法使用block 命令。需要解决的问题确实存在,所以暂时搁置了。
尝试二:使用 TCP_USER_TIMEOUT
TCP USER TIMEOUT 是RFC 54288 中规定的TCP 选项,用于扩展TCP RFC 7939 协议本身中的“User Timeout”参数(原始协议不允许参数大小配置)。用于控制已发送但尚未被ACK的数据包的生存时间。如果超过这个时间,连接将被强制关闭。可以用来解决上述KeepAlive无法解决的Retran优先级高的问题。以下是KeepAlive与Retran和TCP USER TIMEOUT配合使用的情况。
打开TCP_USER_TIMEOUT
bootstrap.option(EpollChannelOption.TCP_USER_TIMEOUT, tcpUserTimeout);
Lettuce修复SNAPSHOT版本依赖的版本
依赖项groupIdio.lettuce/groupId artifactIdlettuce-core/artifactId version6.3.0.BUILD-SNAPSHOT/version/依赖项依赖项groupIdio.netty/groupId artifactIdnetty-transport-native-epoll/artifactId version4.1.65.Final/version classifierlinux-x86_64/classifier/依赖性
3.3 Ali-LB 的修复方案
Ali-LB推出了Connection Draining功能来解决这个问题。 Connection Draining 表示连接排空,用于正常关闭。
正常关闭意味着后端服务器通常可用。如下图所示,一个阿里-LB后面有4台服务器。执行缩放操作以删除服务器4。对于将发送到该服务器的请求4 和6(在同一连接上),在配置的排空时间(0-900 秒)内,服务器4 仍然会响应该请求,并且不会断开连接,直至达到排水时间。注意:清空后,新的链接将不再调度到Server4,因此后续的7、8、9等请求将不再发送到Server 4,这也是清空的前提。
因此,一旦启用Draining,客户端最晚会在达到Draining时间后收到Ali-LB的RST。
图3. 连接排水图
与est_timeout机制相比,Connection Draining的优点是减少误判,尽其所能。
表1. est_timeout 与连接耗尽
阿里-LB团队推出Connection Draining后,配合验证,将故障时间从120秒缩短至30秒,符合Redis产品的SLA。已经发布到全网,也解决了其他Redis融合连接SDK和整个数据库产品的问题。连接黑洞问题。
四、总结
从客户端:您可以将Lettuce升级到最新的6.3.0版本并打开TCP_USER_TIMEOUT参数。在阿里云上,不需要修改代码。 Ali-LB的Connection Draining会主动避免这个问题(无需用户升级,阿里云会主动逐步改变)。
这个问题从发现到修复花了大约2年的时间,终于解决了。路途漫长而艰难,但终点即将来临!
参考阅读
[01] https://github.com/lettuce-io/lettuce-core
[02]https://github.com/redis/redis
[03] tcp_重试2
https://www.kernel.org/doc/Documentation/networking/ip-sysctl.txt
[04]《TCP中已有SO_KEEPALIVE选项,为什么还要在应用层加入心跳包机制?》
https://www.zhihu.com/question/40602902/answer/209148428
[05]https://github.com/lettuce-io/lettuce-core/issues/1437
[06] https://github.com/lettuce-io/lettuce-core/issues/2082
[07] https://github.com/yangbodong22011/lettuce-core/commit/23bafbb9255c87ed96a6476c260b299f852ee88a
[08] TCP_用户_超时
https://www.rfc-editor.org/rfc/rfc5482.html
标题:为什么生菜会导致更长的停机时间?
链接:https://www.zhangqiushi.com/news/sypc/15140.html
版权:文章转载自网络,如有侵权,请联系删除!
用户评论
我最近玩了Lettuce,感觉它的稳定性确实有待提高。
有11位网友表示赞同!
Lettuce在游戏中出现了好多次意外断线,真是让游戏体验大打折扣。
有10位网友表示赞同!
每次用Lettuce打比赛时都像是在坐过山车,因为经常出现故障。
有7位网友表示赞同!
我试过多次联系客服解决Lettuce的故障问题,但效果不佳。
有5位网友表示赞同!
尽管广告中的Lettuce看起来完美无缺,但实际上它总是出错。
有10位网友表示赞同!
Lettuce让我最头疼的事情就是它的频繁闪退和网络连接问题。
有16位网友表示赞同!
在玩Lettuce的时候,我几乎每局游戏都会遇到连接中断的情况。
有5位网友表示赞同!
我不喜欢在Lettuce上花时间等待系统恢复稳定,感觉浪费了宝贵的游戏时间。
有12位网友表示赞同!
Lettuce的优化问题让玩家无法享受流畅的游戏过程,很让人失望。
有15位网友表示赞同!
玩Lettuce的时候总担心下一秒它会崩溃,这让人非常不安。
有20位网友表示赞同!
我试着调整设置来解决Lettuce的一些技术问题,但似乎没有任何改善。
有19位网友表示赞同!
对于那些对稳定性有一定要求的玩家来说,Lettuce可能会有些难以接受。
有11位网友表示赞同!
虽然Lettuce有独特的游戏特点,但频繁的故障让这变成了一个缺点。
有19位网友表示赞同!
AJ在使用Lettuce的时候总是抱怨性能不佳,导致无法获得好的游戏体验。
有10位网友表示赞同!
Lettuce的运维团队似乎需要更多关注来提升系统稳定性。
有7位网友表示赞同!
期待未来的更新能够解决目前Lettuce中存在的那些持续性的技术问题。
有19位网友表示赞同!
很多玩家包括我个人都对Lettuce的一些故障表示了强烈不满,希望开发商能重视这个问题。
有18位网友表示赞同!
在与其他玩家讨论时,大家都一致认为Lettuce的稳定性是最大的痛点。
有19位网友表示赞同!
Lettuce的游戏内错误报告系统似乎没有有效解决问题,需要改进。
有6位网友表示赞同!