项目使用到了基于Netty
的长连接,最近有一次活动,线上存在大流量,活动开始后的一段时间,各个指标正常,但是一段时间后就出现了部分用户无法连接上 WebSocket
,需要重试多次才能连接。
首先来看看项目背景:
Netty
作为Websocket
的底层,Netty
的性能不需要质疑。Nginx
作为代理,将Websocket
升级到wss
,Nginx
的性能也不需要质疑。所以整体来说就是:流量先打到了Nginx
再通过他转发到了Netty
的Websocket
。
第一步查看了Java
应用的内存占用和CPU
占用,未发现异常。再查看 JVM
的 GC
情况,发现也是正常的,没有任何疑点。
所以我暂时排除了Java
应用导致的问题,也没有采用没有重启服务的万金油方案。
此时移动端和前端都提供了一个信息:建立Websocket
长连接的时候,服务端返回了500
或者50X
。
50X
该问题通常是由于连接被客户端或者服务端主动断开导致的。在当时情况下,移动端和前端都应该是没有问题的,那么问题就应该后端,后端关键组件是Nginx
和Java
应用,排除了Java
应用,那么就剩下Nginx
了。
通过如下指令查看Nginx
的日志
tail -f /log/access.log
发现确实存在GET
请求就出现了502
的错误。
结合当时情况来看,流量确实是有些大,但是我不信久经沙场的Nginx
这么脆弱,所以估计是配置问题。
打开 Nginx
配置,发现有如下关键配置
worker_processes 1;
events {
worker_connections 1024;
}
http {
include mime.types;
....
}
可以发现这个 worker_connections
为1024
,这个配置代表的含义的:单个进程能够建立的最大连接数。很明显,在大流量下1024
肯定是不够的,所以该参数完全可以修改的更大。
该参数也并随便多大,受限于单个进程能够打开的最大文件数和系统内存:
Linux
所有的资源都是文件,单个进程能够打开的最大文件数受制于系统的设置,可以通过ulimit -n
获取,该参数通常是65535
。328
字节,所以可以计算下:65535*328/1024/1024=20.5MB
综上,经过修改后的 Nginx 配置如下:
# 2 个 进程
worker_processes 2;
# 能够打开的最大文件数
worker_rlimit_nofile 65535;
events {
# 单个进程允许的客户端最大连接数
worker_connections 65535;
}
然后重新或者重新加载 Nginx 配置文件后,通过如下指令观察了一段Nginx
在处理Websocket
升级的日志后,发现一切正常了。
tail -f xxx/accees.log
这里在记录下这个过程中用到的一些命令:
Nginx
进程可以打开的最大文件句柄数# 该指令会有 2 个输出
ps -ef | grep nginx
# 获取文件句柄数
cat /proc/$pId/limits
# 然后查看`Max open files` 的数量
Nginx
进程当前打开的文件数lsof -p $pId |wc -l
Nginx
部署在docker
中,如何进入docker
docker exec -it nginx bash
# 查看进程能够打开的最大进程数
ulimit -n
# 查看系统能够打开的最大文件数
cat /proc/sys/fs/file-max
#设置进程
vim /etc/security/limits.conf
# 加入配置
* soft nofile 65535
* hard nofile 65535
# 设置系统能够打开的最大文件数
vim /etc/sysctl.conf
# 加入配置
fs.file-max=6553560
# 最后重启
reboot now
解决问题,一定要学会分析问题的源头到底在哪里,不要遇到问题就只会傻傻的重启服务来解决。