linux开发专注者(坚持原创)linuxfocus.blog.chinaunix.net

多阅读,多思考,多实践,多分享!

  • 博客访问: 5278596
  • 博文数量: 238
  • 博客积分: 10424
  • 博客等级: 少将
  • 技术积分: 14202
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-14 12:45
  • 认证徽章:
个人简介

啦啦啦~~~

文章分类

全部博文(238)

文章存档

2015年(5)

2014年(2)

2013年(22)

2012年(67)

2011年(120)

2010年(22)

微信关注

IT168企业级官微



微信号:IT168qiye



系统架构师大会



微信号:SACC2013

订阅
热词专题

分类: LINUX

<p> 作者:gfree.wind@gmail.com<br /> 博客:blog.focus-linux.net linuxfocus.blog.chinaunix.net<br /> 微博:weibo.com/glinuxer<br /> QQ技术群:4367710 </p> <p> 本文记录一次作者定位解决问题的一个过程,与大家分享一些经验。 </p> <h2> 问题描述 </h2> <p> 笔者在阿里云服务器上,使用Nginx搭建了一个HTTP 正向代理,但是运行一段时间后,就无法接受新连接请求了。 </p> <h2> 解决过程 </h2> <h3> 测试定位问题 </h3> <p> 出现问题后,重要的不是立刻动手,而是先做初步的思考,然后再动手。 </p> <ol> <li> <p> 首先我排除是否为网络问题:检查了iptable等。同时思考,如果真的是网络问题,不应该运行一段时间后,才出现无法接受新连接的现象。<br /> 为了验证,当出现问题时,我又重新启动Nginx,发现又可以接收新的请求了。也就是说出现问题时,只需要重启Nginx就可以解决,那么自然不是网络因素。 </p> </li> <li> <p> 判定是否是Nginx本身的问题(不一定是指代码包括我写的配置文件):因为这个代理服务器是为了测试fastsocket项目的稳定性,所以 Nginx是加载了fastsocket优化服务的。这时,就需要最纯粹的Nginx环境。我去掉了fastsocket服务,然后再用同样的配置启动 Nginx。这时,就排除了Nginx本身的问题。那么,究竟是否是fastsocket的bug呢?<br /> 这里先做一个小广告: fastsocket是新浪主导的一个开源项目,其通过封装socket套接字调用,无需改动服务程序,即可大幅提升服务程序性能。作者也是其中的维护者之一。这里小小推广一下:<a href="https://github.com/fastos/fastsocket" title="fastsocket">https://github.com/fastos/fastsocket</a>。当使用fastsocket默认加载参数时,nginx运行一段时间就无法接受新连接请求了。 </p> </li> <li> <p> 定位fastsocket问题:fastsocket的大部分优化功能都是有功能开关的,默认会使用一些功能,同时可以在加载动态模块时,使用参数指定是否打开开关。这时,先做实验,从所有功能关闭开始,逐渐打开功能开关,最后定位到<code>enable_listen_spawn</code>功能打开时,就会出现问题。并多次做实验,确定这是一个必现的问题。<br /> 当确定可以重现后,我想这难道是一个fastsocket的bug吗?于是,先跟林晓峰同学说了一声,告诉他我的发现,毕竟fastsocket是他在sina时的工作,他最为熟悉代码。他说这可能是Nginx的配置使用了<code>accept_mutex</code>。我的配置文件虽然没有配置<code>accept_mutex</code>,但是没想到Nginx的<code>accept_mutex</code>是默认打开的。但是他忘了为什么会这样了?依稀记得是Nginx hang在了mutex中,具体原因记不清了。所以fastsocket的说明也是要求disable accept_mutex。<br /> 因为我一直以来有这还不错的求知欲,所以一定要搞清楚这个问题。同时我认为,如果真的是一启用<code>accept_mutex</code>,fastsocket和nginx就会有兼容问题,那也应该算是fastsocket的bug,应该将其解决掉。 </p> </li> <li> <p> 定位Nginx hang在什么位置:这个很简单,使用strace -p跟踪Nginx的每个worer进程。发现大部分worker进程是在不断的<code>epoll_wait</code>,而其中一个worker进程,始终停留在<code>epoll_wait</code>中。重试多次,每次都是停留在<code>epoll_wait</code>中。 </p> </li> </ol> <p> 现在已经确定了本次问题,当使用fastsocket的<code>enable_listen_spawn</code>功能时,也就是fastsocket自动为当前CPU创建本地的listen socket套接字时,就会出现问题。 </p> <h3> 解决问题 </h3> <p> 当定位到问题时,就需要一步一步的找到原因,查看为什么一个worker进程始终停留在<code>epoll_wait</code>中。这时候,其实思考还是要优于动手。先思考,再动手,动手之后,看到结果,再做进一步思考。 </p> <ol> <li> <p> 查看该worker进程停留在<code>epoll_wait</code>的什么位置:只能通过日志形式来判断hang在<code>epoll_wait</code>的哪个位置?这时,不能用内核普通的printk来打印日志,不然就会淹没于大量正常工作worker进程打印的日志中。我们需要根据pid来打印日志。<br /> 再做一个小广告:我做了一个内核小工具<a title="UnitPerf">git@github.com:gfreewind/unit_perf.git</a>。是用来定位内核代码的性能瓶颈工具,和一些辅助工具。大家觉得还可以的话,就给赞个星星。<br /> 它提供一个宏<code>UP_PID_INFO_LOG</code>用于打印指定PID的日志,pid可以通过proc来指定。<br /> 这样我在<code>epoll_wait</code>中增加了大量的日志。在Nginx启动后,通过proc指定就打印某个worker进程的日志。<br /> 最后发现<code>epoll_wait</code>是因为指定了无限等待时间,所以该worker进程一直在hang住。 </p> </li> <li> <p> Nginx让一个worker进程无限等待,这稍微颠覆了我对Nginx的认识。我认为Nginx一直都是使用无阻塞的系统调用,至少核心模块是这样处理的。那么为什么会出现这个现象呢?这时候,就需要思考,而不是动手了。<br /> 毫无疑问,<code>accept_mutex</code>是一个关键。它本身是用于均衡不同worker进程的负载。稍微阅读一点Nginx相关的 代码,就可以明白。在Nginx无法接收新连接请求时,一定是该轮到hang住的进程接收新连接请求。所以尽管其它进程没有hang住,但是它们是无法接 受新请求,而能够接收新请求的进程却hang住,这样就导致了问题的产生。 </p> </li> <li> <p> 为什么hang住的进程无法接收到新的请求呢?这时还是思考优先。首先要勾画标准的内核TCP连接的过程,然后对比启用fastsocket 后,TCP连接的过程。很可能是这两者之间的区别,造成了问题。尤其是启用了spawn socket时,与标准流程的不同。spawn socket时,实际上为每个cpu都创建了一个本地listen 套接字的hash表,与全局的listen表区分开。这样一方面访问全局hash表时需要的锁,另一方面也做到了将TCP会话做到本地,可以尽量命中 cache。 对于同一个CPU,由于有两个listen表的存在,所以在收到新的TCP连接请求时,必须先检查本地的listen表,然后再检查全局表。 根据这样的流程和现象,应该是所有的连接请求,都被发到其它的CPU,并且匹配中了其它CPU的本地listen表,所以全局表中的listen socket套接字一直没有被匹配到。<br /> 那么hang住的进程,既没有连接请求匹配本地listen表中的套接字,而全局表也一样,因为被请求都被其它CPU命中了本地的套接字。<br /> 所以问题更为明朗了,hang住的进程所在的CPU不能收到任何新连接请求。 </p> </li> <li> <p> 这时其实已经到了冲刺的时候了。开始的时候,我还想着,是否是fastsocket影响了数据包的分发,还想检查一下代码。但一想,还是先看看 RPS的设置吧——虽然我没有设置网卡的任何RPS。结果出乎我意料,原来阿里云ECS服务器默认就把网卡的RPS设置了,唯一的外网网卡的RPS设置为 了0000,所以只有CPU 0能收到新连接请求,而另外的CPU1收不到任何的连接请求,这就造成了运行在CPU1上的worker进程hang住。 </p> </li> </ol> <p> 最后我修改了该网卡的RPS设置,使其可以将数据包分发到不同的CPU上。这样在加载了fastsocket后,即使打开了<code>accept_mutex</code>,Nginx也可以正常工作了。 </p> <p> 本次过程,虽然最后发现只是服务器配置的问题,但整个儿过程还是收获不少。唯一的遗憾,是还没有定位Nginx对与<code>epoll_wait</code>的超时计算。开始的时候,都是500ms,后面因为什么因素变成了无限。这留到有时间的时候,再阅读Nginx源码吧。 </p> <br />
阅读(10088) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册