周六生产服务器显示redis服务器不可用,错误信息为:
状态不可用。等待后台检查器恢复后才能使用。流意外结束;预期类型“状态”
如下图所示,下图中的6300就是我们的redis服务器运行的端口。
这是我第一次遇到这种问题。我想可能是redis挂了,所以就用telnet ip + port。发现运行正常,然后就想到进入redis查看当前连接状态。乍一看,竟然有1903条之多。
然后我想可能是代码创建的redis连接过多导致的,于是检查了代码。
发现redis创建只有这个地方才有,而且也是注册服务的时候执行的。即应用程序启动时执行一次。然后搜索整个项目,发现其他地方都没有调用redis初始化。
我不愿意放弃。 redis中是不是每次读写数据都会创建一个连接?和经常读书、写作有关系吗?我总觉得不行,所以我立即创建了一个测试代码来测试一下。
本地搭建了redis环境。测试前,首先检查连接数。目前只有1个,即当前cmd连接客户端。这是正常的。
开始测试并运行程序。该代码创建一个连接对象并测试总共 1000 次写入和 1000 次读取。
无论我怎么测试连接,都是6个,也就是说我们的程序最多创建5个连接,当然主要是线程池的原因。
所以这段代码的基本存储和读取一定是没有问题的。
但代码并没有完全废弃,因为生产服务器通过docker运行了大约6个应用程序。它们都连接到同一个redis。会不会是其他应用程序导致的?
然后我想通过redis连接列表中的任意端口直接查询对应的进程信息,就可以知道是哪些应用程序。
Linux中通过查询网络端口号来显示进程信息。
netstat -atunlp | netstat -atunlp | grep 60852
首先查看这个端口对应的IP。例如,这里的第一个是172.17.0.1。熟悉docker的同学应该知道,这个IP就是docker网关IP。我们容器中的程序通过这个网关IP与我们的宿主主机进行通信。我们可以通过ifconfig找到docker的网关IP。第二个172.17.0.3:6379是redis的容器IP。
这样看来,确实无法查出对应容器中的哪个程序与我们连接。
最愚蠢的方法之一就是一一进入容器。即docker exec –it test /bin/bash,然后检查当前容器的网络连接。这非常繁琐,需要安装很多组件来执行一系列命令。
另一种方法是 lsof 命令。如果不可用,则需要安装它。我们可以通过进程找到所有的网络连接。
比如我们刚刚发现我们的进程主要是docker,它的pid是582251。
lsof -i |grep 582251 或 lsof -i -p 582251
结果如下图所示。右侧实际上出现了一个特定的IP。这个IP就是docker容器的具体IP地址。
现在所有 IP 和端口都已知,我们可以下载命令执行结果。
首先找到你的每个容器对应的IP。
docker inform name |grep IPAddress //name 容器名称或id
找到每个IP后,根据刚刚下载的所有网络连接信息进行统计,看看哪个IP的连接数最多。联系最多的那个肯定有问题。
然后我找到了这个IP对应的容器部署程序,然后查看了redis的配置。发现线程池设置为200。
另外,通过github,我发现CSRedisCore还有一个预热机制,就是preheat。其默认值为 5 个预热连接。
我们的线程池设置为200加5个连接,有自己的预热机制。不知道会不会产生200*5=1000。有时间我会仔细研究一下源码。目前这只是一个猜测。
我现在已经将redis修改为poolsize=5,preheat=false。有5个线程池,预热机制关闭。
修改我们的连接配置并重新启动应用服务器和redis服务器(为了彻底清除已建立的连接)后,我们发现连接数减少了,但不是很多。后来查询发现是redis空闲时间过长,导致连接池维护了过多的连接而没有被释放。
我们将超时设置为30s
执行CONFIG SET timeout 30(单位是秒,该方法只是临时修改,对当前操作有效,记得修改redis配置文件才能长期生效)
然后看连接数,一下子减少了很多。
总结:
1、Redis连接数大幅增加。首先,我们从自己的应用程序中寻找问题。比如我发现连接池设置太大,再加上默认的预热机制等等。另外尽量看看创建连接时代码层面会不会被触发多次。如果是这样,就必须予以纠正。现在实例是通过注入创建的,具体取决于该地方是否被多次调用。
2.修改redis服务器配置,例如连接空闲超时。包括,你还可以检查最大连接数,默认值。
本网站每日更新互联网创业教程,一年会员只需98,全站资源免费下载点击查看会员权益