百万连接之路

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介:
  前段时间接到某项目中关于虚拟机所在宿主机上最大支撑连接数的 测试需求。应用场景类似于在物理机上运行着多个虚拟机,这些虚拟机对外提供服务,来自于任何地方的客户端都可能向这些应用服务发起连接和请求。也许单个虚拟机并发的连接数十分有限,但对提供虚拟机服务的物理机或宿主机来说连接数就可能达到十万、几十万甚至百万。在这样的情况下,宿主机是否能够稳定运行呢?同时项目方也提出了明确的测试目标,支撑300万连接,这就需要我们着手进行测试验证了。
  首先,我们先做一个假设,即这300万连接里同时只有少数活动连接,测试场景可以简化为保持300万长连接的测试。明确了这一点,我们继续分析测试可能出现的瓶颈点,是CPU、内存还是网络?借鉴以往长连接或者消息推送类服务的测试经验,由于保持长连接并不需要消耗过多CPU和网络资源,只是占有系统的内存,我们决定把关注点主要集中在内存使用上,测试过程中也确实出现了一些内存相关的问题,这是后话了。
  我们首先需要准备一个测试环境,一个可以支撑足够连接的应用服务,大量的客户端。很幸运我们可以借用杭研前台技术中心基于node.js开发的开源游戏引擎pomelo作为服务端程序。使用 java网络API开发了一个简单的客户端程序。客户端向服务器发起连接请求,成功后把连接对象保存在内存中。至此万事具备,只欠执行测试了。
  不急,我们先建立100万连接试试。没过多久第一个拦路虎就出现了,在客户端 日志里出现了java.net.NoRouteToHostException异常,将它作为关键字google一把,原来是/proc/sys/net/ipv4/ip_local_port_range配置的区间太小,端口耗尽导致,配置修改如下
pomelo@debian:~/socktest$ cat /proc/sys/net/ipv4/ip_local_port_range
2048 65500
  可见单个客户端ip只能建立6万多连接,所以我们需要大约50个独立ip发起300万的连接,为简单起见我们使用50台虚拟机运行客户端,而没有采用单机多ip的方案。
  继续测试,再次被异常打断,java.net.SocketException: Too many open files。这个有经验的同学都应该了解,该修改ulimit了。改完继续,可奇怪的是运行了一段时间后,又被同样的异常中断了,不是修改得很大了吗?怎么又出了呢?确认修改有没有生效,/etc/security/limits.conf文件是否保存,不会是这么狗血的问题吧。确认已经保存,检查了客户端进程的limit,/proc/pid/limits,发现open files竟然只有4096。百思不得其解。最后还是SA发现了问题的原因,原来是open files默认最大值1024*1024,我们修改时设置过大导致其溢出了,最后在/etc/security/limits.conf中添加如下两行搞定
* hard nofile 1048576
* soft nofile 1048576
  至此我们终于完成了100万连接的阶段性胜利,不能松懈, 一鼓作气拿下300万大关。等等,有人可能会问,服务端的open files也是配置1048576,就100万多点,肯定不能支撑300万连接吧。是的,open files是针对单一进程的限制,但我们跑的服务是多进程程序,所以不用担心。另外,open files的最大值也能通过配置/proc/sys/fs/nr_open参数修改,这样就能摆脱1048576的上限了;而系统中所有进程打开的文件数确实是需要配置的,通过fs.file-max修改。
  接着我们遇到了这次测试中最可疑的问题之一。当建立完200万连接以后,我们kill掉了一个服务进程,没过多久,就发现部分运行客户端的虚拟机不能ssh登陆了。通过vnc连接上后发现虚拟机CPU几乎跑满了,dmesg中存在Out of socket memory这样的错误信息。此处省略一千字某SA大神的问题定位过程,本质上是由于服务端和客户端之间存在4万连接,服务进程挂掉后客户端机器收到大量FIN包导致网络相关内存溢出,而客户端机器上存在一个使用curl的定时任务,网络内存溢出引发其某个bug,进而引发CPU跑满。因此调大net.ipv4.tcp_mem配置,注意该参数的单位是页,而不是字节。
  眼看着300万连接的目标已经近在咫尺,可问题再次不请自来了。在漫长的等待后,我们用ss –s确认最终建立的连接数,但这个数值却始终停留在280万附近,照例的打开dmesg查看,发现了一大堆错误信息,如下为开始的一段
[531503.634391] TCP: too many of orphaned sockets
[531503.634412] TCP: too many of orphaned sockets
[531503.634432] TCP: too many of orphaned sockets
[531503.634451] TCP: too many of orphaned sockets
[531508.704084] net_ratelimit: 255864 callbacks suppressed
[531508.704088] Out of socket memory
[531508.704233] Out of socket memory
[531508.704245] Out of socket memory
  简单的调大net.ipv4.tcp_max_orphans参数,问题依旧。查看服务端日志,发现报错
  FATAL ERROR: CALL_AND_RETRY_2 Allocation failed - process out of memory
  查看服务进程数,确认有部分进程已不存在了,结合以上两种日志不难猜到问题的根源,是由于服务进程挂掉,瞬间出现大量孤儿连接,进而导致网络内存溢出引起。但服务进程为什么会莫名其妙地挂掉呢?咨询了相关开发人员,由于pomelo是基于node项目的,需要通过编译选项调整内存上限,修改重新编译后测试,进程还是出现了内存溢出的情况,但明白了问题的本质,我们通过增加node进程数量,以减小单个进程的内存占用,可以绕过该问题。而node进程的内存限制问题还需要后续的确认。
  到此为止,终于完成了300万连接的目标,可喜可贺。简单总结一下,连接数测试不同于常规的 性能测试,不太关注TPS和响应时间等指标,主要是通过dmesg和日志中出现的异常信息定位问题,进而调整系统相应参数,这些参数大多与网络、句柄数及内存有关。不仅仅是测试阶段,日常运维这类系统也应该时刻关注这些。

最新内容请见作者的GitHub页:http://qaseven.github.io/

相关文章
|
3月前
|
网络协议 Ubuntu
百万并发连接的实践测试01
百万并发连接的实践测试01
百万并发连接的实践测试02
百万并发连接的实践测试02
|
6月前
|
存储 消息中间件 Java
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的高可靠消息服务设计实现
在深入研究了 **“【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的API网关设计实现”** 设计实现后,我们意识到,尽管API网关为服务商提供了高效的数据获取手段,但实时数据的获取仍然是一个亟待解决的问题。
98 1
【亿级数据专题】「高并发架构」盘点本年度探索对外服务的百万请求量的高可靠消息服务设计实现
|
存储 关系型数据库 MySQL
百万数据怎么入库mysql mysql百万级数据
      1、连接数据库的问题:建立连接和关闭连接的次数太多,导致IO访问次数太频繁。        2、应该使用批量插入和批量修改的方法,而不是有一条数据就进行插入,这样会导致访问数据库的实际特别的慢。
|
6月前
|
存储 缓存 安全
2.2.1服务器百万并发实现
2.2.1服务器百万并发实现
|
6月前
|
存储 网络协议 Linux
百万并发服务器
百万并发服务器
46 0
|
存储
服务器百万并发的原理与实现
服务器百万并发的原理与实现
177 0
|
缓存 NoSQL 关系型数据库
性能第三讲:百万级QPS,支撑淘宝双11需要哪些技术
性能第三讲:百万级QPS,支撑淘宝双11需要哪些技术
1200 0
|
存储 运维 负载均衡
邓荣伟:稳定支撑每秒百万笔支付请求,支付宝数据库架构的过去、现在与未来
8 月 10 日,2022 OceanBase 年度发布会在京沪深三地同时召开,支付宝资深数据库专家邓荣伟在会上分享了《从“小”到“大”,支付宝分布式升级之路》的主题演讲,为我们带来了支付宝的架构演进以及上线 OceanBase 的故事。
514 0
邓荣伟:稳定支撑每秒百万笔支付请求,支付宝数据库架构的过去、现在与未来
|
消息中间件 存储 负载均衡
多数据中心的百万级消息服务实战
多数据中心的百万级消息服务实战
249 0
多数据中心的百万级消息服务实战