转载文章:https://www.cnblogs.com/devilwind/p/6865750.html
问题说明:生产环境redis(哨兵模式 一主两从)使用过程中发现有一台从机sentinel端口的连接持续增长,每天增加44条established状态的连接(共四台应用服务器,每台服务器每天增加11条,每130分钟左右增加一条,相当规律)
疑问:另一台从机和主机没有这样的问题,同时开发环境、dat测试环境、uat测试环境、准生产、压测环境等同样的redis运用,所有配置都一样,却没有这样的问题。
redis使用场景:使用commons-pool2-2.4.2.jar中的JedisSentinelPool初始化连接池,jedis-2.9.0.jar进行操作。
问题处理1:一开始遇到此问题,网上查找资料,介绍说是jedis包的版本低导致(系统一开始使用的是jedis-2.4.2.jar),将jar升级至2.9.0后发布生产进行验证,结果问题没有解决(没办法啊,其余环境不能复现,其余环境升级jar后观测两周后没有问题才升级的生产环境)。 【继续努力中…………………………】
问题处理2:终于在上面转载地址中找到了和我遇见的问题相似度达95%的解决方法,以下全为转载内容,格式就不调整了(具体待验证…………)
一、问题现象
二、问题排查过程 1、根据连接状态进行推断
2、检查业务系统的redis配置和程序逻辑
3、从网络寻找线索
4、重新思考产生连接的根源
public void run() { running.set(true); while (running.get()) { j = new Jedis(host, port); try { j.subscribe(new JedisPubSub() { @Override public void onMessage(String channel, String message) { log.fine("Sentinel " + host + ":" + port + " published: " + message + "."); String[] switchMasterMsg = message.split(" "); if (switchMasterMsg.length > 3) { if (masterName.equals(switchMasterMsg[0])) { initPool(toHostAndPort(Arrays.asList(switchMasterMsg[3], switchMasterMsg[4]))); } else { log.fine("Ignoring message on +switch-master for master name " + switchMasterMsg[0] + ", our master name is " + masterName); } } else { log.severe("Invalid message received on Sentinel " + host + ":" + port + " on channel +switch-master: " + message); } } }, "+switch-master"); } catch (JedisConnectionException e) { if (running.get()) { log.severe("Lost connection to Sentinel at " + host + ":" + port + ". Sleeping 5000ms and retrying."); try { Thread.sleep(subscribeRetryWaitTimeMillis); } catch (InterruptedException e1) { e1.printStackTrace(); } } else { log.fine("Unsubscribing from Sentinel at " + host + ":" + port); } } } } 正常情况下 j.subscribe会产生阻塞,而出现异常时会重新创建连接并且打印日志“Lost connection to Sentinel at……”
一般在系统中设置时间间隔都会是个整数,2小时11分15秒显得有点怪异,然后将时间换算成秒,看是否为一个常用的或有规律的整数,2小时=7200秒,突然想到操作系统的keepalive设置中有一个7200秒,查一下操作系统默认的设置,参数如下: net.ipv4.tcp_keepalive_time = 7200 默认 tcp空闲时间 net.ipv4.tcp_keepalive_intvl =75 默认心跳检测时间间隔 net.ipv4.tcp_keepalive_probes = 9 默认检测次数 如果完成9次心跳,仍然发现连接无效的时间为:7200+9*75=2小时11分15秒,由此,基本上可以判断,客户端的连接是被操作系统回收的,结合前面的分析,连接被回收时并未向服务端发送关闭的报文。
5、与运维进行沟通防火墙问题
6、持续观察测试环境的日志
7、初步结论
8、继续思考服务断连接不被释放的问题
以上因素使得防火墙每30分钟将该连接的会话信息清除,从而导致客户端操作系统检测到心跳失败,随后操作系统清除了客户端连接,使得客户端的连接数能正常释放,随后客户端的jedis收到异常后重新创建连接,而服务端的keepalive并未执行,以上过程不断循环,导致服务端established状态的连接不断增加并得不到释放。 |
问题处理3:参见上述处理办法2中的处理方式与运维沟通了服务器上的配置,很遗憾和处理办法2中的不一致,怎么办、怎么办,快疯了……革命尚未成功,同志们还得努力啊。最终,在团队的共同努力下给解决了:前面有提到,用的是一主两从的redis哨兵模式,但是出问题的只有一台从机,另一台从机却没有问题,很奇怪对不对,的确也是哈。线上用的服务器都是虚拟机,那好,就将没问题的那台从机克隆下来,去替换有问题的那一台从机,再来观测连接数是否增长(事实证明,OK了),具体步骤:
1、联系运维,沟通此方案,并协助处理。
2、克隆没问题的从机,停掉有问题的从机(不会影响正产的服务)
3、将克隆的从机的ip改为原有问题从机的ip,并将原redis的配置文件拷贝克隆的虚拟机上(如果原从机上有别的服务还得一一还原并启动,幸好我们这台机子上就redis和一个zookeeper的配置,好还原)
4、启动克隆的虚拟机,启动redis-server、redis-sentinel。
5、观测连接数(哈哈哈哈哈,连接数没有增涨了,问题解决了,也解答了先前的疑问,至于原虚拟机为啥有问题呢,好吧,没去深究了,涉及网络、操作系统,太深奥了……………………)。