结论

如果你用lighttpd1.5(以下lighttpd均指1.5)做静态文件服务器,或者你虽然用lighttpd处理php请求,但是用到$PHYSICAL作为mod_proxy_core的条件, 且某个时候你的单机流量很低(几个/s), 或许你也有类似的问题,但是影响程度或许不会引起你的注意!
1.Lighttpd的mod_proxy_core不建议用$PHYSICAL作为条件;
2.Lighttpd的stat cache机制没有节省任何开销;
3.Lighttpd子线程和主线程通过管道+epoll的通信机制,存在event丢失问题;

现象

用户反馈凌晨的时候访问百度某页面,某些模块的数据出不来;

其它依赖于我们的前端接口的产品线反馈访问时间有时候超过1s;

我们自己的QA环境偶尔也会出现请求超过1s的问题;

因此我们打开lighttpd的日志的%D配置,打印ms级别的处理时间,发现晚上1点到凌晨8点有很多处理时间超过1s的请求,500ms以上的也有很多,并且流量越低, 比例越大;

1点-8点是流量低峰时期,流量越低,性能越差

这个现象每到高峰时期就正常了,因为是流量低峰才会出现这样的慢请求,占总比例非常之少,对整体的性能和稳定性影响极小,所以性能和稳定性监控报表中没有发现这个问题。

追查过程

由于这个页面对性能要求相对比较严格,虽然性能和稳定性衡量数据已经非常好, 但是这个问题一直是一个阴影,不解决终归不爽,所以开始了下面的追查过程:

由于处理路径是lighttpd->php-cgi->框架+逻辑, 首先的怀疑是框架+逻辑问题,但是通过查看php的处理时间,流量低峰高峰都非常正常,极少超过100ms,所以排除是框架+逻辑问题;那究竟是php-cgi的问题还是lighttpd本身的问题呢?为了排除php-cgi的问题,我们尝试了从线下复现这个问题,看访问静态文件是否也有类似的问题。但是悲剧的是线下就是复现不了这个问题。那再对比和线上环境的不同,会不会是先要经过一段时间的大流量,然后再小流量才会出现这个问题呢?于是用ab 30qps压了2个小时后停止,然后再手动访问试了一下,果然如此!通过访问静态文件,发现静态文件也是如此,处理时间超过1s, 因此基本排除php-cgi本身的原因,问题应该是出在lighttpd本身

通过这个线下实验,我们还发现了如下规律:

1.前期用ab压的时间不定,有时候压2个小时后然后低流量访问还不会出现这个问题,有一定的随机性;

2.手动低流量访问的时候,并不是每次都慢,对同一个url, 紧接着的两次访问(访问第一次后马上访问第二次),第一次会慢,但第二次会很快,然后再过个1-2s钟再访问第三次,又会很慢;

3.手动请求的时候,如果慢,总是慢1s, 但是线上有慢1s的,也有不是慢1s的,最多1s;

4.重启lighttpd后,所有请求会恢复正常,需要重新压;

于是产生两个最大的疑问, lighttpd在公司使用这么广,处理静态资源和php请求的都有用到, 别的产品线为什么不报? 为什么是1s? 对于第一个疑问,觉得可能是因为这种情况影响的平均性能非常少,可能其他产品线不会这么敏感,或者是流量低峰的单机请求量也很高,没有频繁的触发这个问题,这个时候还对比了其它产品线的lighttpd.conf, 这个时候是没有发现有什么问题的。于是就从第二个问题开始着手追查:为什么是1s?

带着问题,开始读lighttpd的源代码了。。。

该页面lighttpd event-handler用的是linux-sysepoll;

首先发现lighttpd代码中有各个地方和1s有关的代码:

源文件server.c

第一个1000ms是lighttpd的epoll的超时时间,也就是如果没有任何句柄有事件发生,epoll最多等待1000ms后即会返回,如果有事件发生,epoll会马上返回有事件发生的所有句柄,然后lighttpd会处理joblist中已经准备好的connection,重新进入状态机;第二个1s是lighttpd有个一个trigger机制,每隔1s会触发一次SIGALRM, 然后lighttpd处理超时的请求,清理stat_cache等;因此,通过修改这两个1s, 发现当改成fdevent_poll(srv->ev, 500);  后,慢请求都变成500了, 所以慢请求是因为的那个connection已经放在joblist, 但是没有成功触发epoll返回, epoll只有等待超时后返回,该connection才会被处理,这也就解释了为什么流量高峰的时候没有这个问题,因为高峰的时候epoll返回得相当频繁,也可以解释为什么线上的慢请求慢100,200ms的都有,但是最多不超过1s了,线下手工访问的时候总是慢1s, 这也是因为每秒的请求量的原因, 这其实也是类似epoll这种异步事件处理模型所带来的通病,用延迟换吞吐量;

问题进一步,那为什么重启lighttpd后,就算流量低也没问题呢,所以进一步看代码,通过把lighttpd所有debug日志打开,发现这个问题和lighttpd­的stat cache机制有关, 为了避免反复的调用stat来获取文件信息,lighttpd用了一个全局的hash表保存了每个物理路径的所对应文件的stat结果,这个机制和server. stat-cache-engine这个配置有关,我们用的默认配置“simple”, cache结果会缓存1s, 如果没有命中或者失效,lighttpd会把这个stat任务放在一个队列里面, 然后告诉状态机HANDLER_WAIT_FOR_EVENT, 暂时退出状态机,由另外几个线程来异步处理这个stat任务,处理完这个任务后,会重新把这个任务关联的connection加到joblist_queue中,然后通过管道通知主线程,让epoll返回, 相关代码如下:

源文件joblist.c

上面的代码可以看出, lighttpd是通过判断一个全局的变量srv->did_wakeup,如果是0,就把它改成1,然后往这个管道发生一个空格字符串,触发主线程的epoll返回,如果这个变量不是0,就不会通知主进程。

下面的代码是主线程epoll返回后,和这个管道句柄对于的处理函数,可以看出主线程又把srv->did_wakeup初始化成0了, 这样下次还会wakeup主线程;

源文件server.c

这就引发一个思考,如果因为某种原因srv->did_wakeup被修改成1了,但是主线程由于某种原因没有收到这个write事件,导致srv->did_wakeup没有被改成0,那不是后面都不会通过管道通知主线程了,为了证明这个假设,我加了下trace代码,发现确实是这样的,设置srv->did_wakeup =1 做了2456次,但是设置srv->did_wakeup = 0只做了2455次,只差一次,并且后续都没有做这个操作了,另外还发现子线程每次write管道都是成功的,但是最后一次主线程没有收到这个事件,至于为什么没有收到,就没有继续查了。

但是,还是有个疑问,我访问的php请求,lighttpd应该把请求路径发给php-fpm,自己应该不关心物理路径的啊,就不用搞什么stat cache吧,这个时候想起了当时为了解决某扩展能够正确获取到PATH_INFO的问题,把mod_proxy_core的条件配置从$HTTP["url"] =~ “\.php$”改成了$PHYSICAL["existing-path"] =~ “\.php$”。马上修改配置,再测试,问题果然没有了, 通过查看lighttpd代码,发现如果配置成$PHYSICAL这种形式,会导致lighttpd去stat这个物理文件,这个操作在mod_proxy_core之前执行,如果用$HTTP[“url”]就不会引发这个问题,到此,一切都清楚了,我看到的其它老的产品线都是配的$HTTP[“url”], 只有少数的几个产品线不是用的$HTTP[“url”],也只是单机流量非常低的情况才会出现这个问题,很难会让人觉察到!

另外,在追查问题的过程中还发现lighttpd stat_cache机制的两个问题,第一个问题就是处理stat任务的子线程,在stat之后,并没有更新这个stat cache的状态为FINISHED, 下次来查的时候还是没有命中cache, 等于是白干了。如下代码所示:

源文件stat_cache.c

第二个问题是就是命中了stat cache, 其实还是需要调用stat判断改cache有没有过期, 所以觉得stat cache本身这个机制也是白搞了,比较没有节省stat的开销,还多搞了,如下代码所示:

源文件stat_cache.c

遗留问题

子线程和主线程通过管道+epoll的机制来通信,为什么会有一定的概率失败呢?write管道其实是成功的,由于精力有限,这个问题没有继续追查;

管道其实是成功的,由于精力有限,这个问题没有继续追查;

对lighttpd配置的建议

如果利用mod_proxy_core做php处理,还是用$HTTP[“url”]做条件吧,例如:

$HTTP["url"] =~ “\.php” {

proxy-core.balancer = “static”

proxy-core.protocol = “fastcgi”

proxy-core.allow-x-sendfile = “enable”

proxy-core.backends = ( “unix:/home/super/php/var/php-cgi.sock” )

proxy-core.rewrite-request = (

“_pathinfo” => ( “(/[^\?]*)/index\.php(/[^\?]*)” => “$2″ ),

“_scriptname” => ( “(.*\.php)” => “$1″ )

)

注意,为了让php-cgi取到正确的PATH_IFNO, 请注意添加“_pathinfo” rewrite规则!

对lighttpd代码优化的建议

1.在每隔1s的trigger操作中,新增一个操作:将srv->did_wakeup重置为0,防止这个变量变成1以后永远便不会0的情况发生;
2.stat_cache_thread处理完stat_job之后,要更新源stat_cache_entry的状态为FINISHED, 否则就白搞了;
3.命中stat cache后,不用再通过stat判断该cache是不是最新的,因为最多缓存1s钟;

【本文首发于:搜索研发部官方博客】http://stblog.baidu-tech.com/?p=1444
【关注百度技术沙龙】

本文转自百度技术51CTO博客,原文链接:http://blog.51cto.com/baidutech/779536,如需转载请自行联系原作者

流量低峰也烦人-lighttpd耗时长问题追查相关推荐

  1. 推送通知_手机总是收到烦人的推送,教你几招,彻底摆脱无用通知

    相信许多人都有过这样的情况,在使用手机时总是会经常弹出一些无用的通知,就算已经关掉的APP还是会接收一些垃圾消息,让人烦躁不已.那么,怎样才能彻底摆脱这些不必要且招人烦的推送呢,小编今天就教你两招,让 ...

  2. 鸿蒙系统通知栏怎么清理,教你两招 彻底关掉手机通知栏烦人的无用通知

    相信很多朋友都跟我一样,在使用安卓手机时,遇到这样一个十分令人烦躁的问题:通知栏经常会弹出一堆无用的通知.有些自带的APP从来没用过,但各种通知推送从来没断过.而且每次清除后,过不了多长时间通知栏又是 ...

  3. 【R语言系列01】烦人的拼贴操作 详述 paste and paste0

    R语言系列01 烦人的拼贴操作 paste 与 paste0 相信很多人在一开始使用R语言的paste, paste0的时候,总是拿捏不准,感到有些迷糊. 本期文章中,我将记录对比一些操作以及表现,加 ...

  4. 网站太多太杂很烦人?macz小编告诉你如何在Chrome上屏蔽任何网站

    你们是不是经常在浏览网站的时候被一些无关紧要的网站吸引注意力,浪费宝贵的时间?今天macz小编为大家详细介绍一下如何在Chrome上屏蔽任何网站的方式方法. Chrome无疑是世界上最受欢迎的浏览器, ...

  5. 苹果android怎么设置关闭,iPhone手机系统更新提醒太烦人,终于知道怎样彻底关闭了!...

    因为最新的系统往往是为最新款的手机"量身定制"的,特别是iPhone 用户应该更有感触.但是不想升级又总是收到提醒,真的很烦人有木有!今天绿豆就教你彻底解决,iPhone和安卓都有 ...

  6. wordpress修复插件_关于WordPress及其修复方法的15个最烦人的事情

    wordpress修复插件 Like most things in life, WordPress also has it's fair share of annoying things. Deali ...

  7. 你的华为手机总是杀后台,很烦人!那是这4个设置没关闭吧

    如今使用华为手机的朋友也越来越多了,不过很多朋友在使用过程中,都遇到这样的一件事,那就是手机"杀后台"太厉害了. 有时候我们聊完天想切换回去看视频时,就发现视频后台就被清理了,很是 ...

  8. java--新猿来瞅瞅==那些烦人的符号

     Java 是一种广泛应用于多个行业的编程语言,具有广泛的应用范围.作为一名 Java 新手,了解代码中不同符号的含义可能会令人望而生畏. 任何一项知识的学习都不可能一蹴而就,因此也不要一时的挫折而全 ...

  9. 华为手机如何设置重要通知_系统通知太烦人?华为手机只要妥当设置,就能让状态栏很清爽...

    常常会听到身边的人怀念昔日的诺基亚,不仅是因为它可以用来砸核桃,还有一部分原因就是喜欢功能机时代那个简洁而又方便的系统. 现在的智能手机功能更丰富了,却也在无形之中给我们增加了负担,烦人的系统通知就是 ...

最新文章

  1. linux终端terminal个性化配置(转)
  2. Xamarin iOS开发实战中册 (内部资料)C#苹果应用开发
  3. 数据结构思维 第六章 树的遍历
  4. cnn识别mnist、Fashion-MNIST(pytorch)
  5. 使用Spring跟踪应用程序异常
  6. 数据中台精华问答 | 数据中台和传统数仓的区别是什么?
  7. 利用H5开发微信公众号
  8. cloudflare 关于tls 检测,发送未知message type字节
  9. java ssh 启动时间_java ssh项目启动异常说明
  10. Nginx虚拟主机别名的配置
  11. 客户成功案例 | 台湾杜邦:提升制程能力的法宝 — 先进的数据分析
  12. 多源信息融合与多视角学习
  13. 灵越7590BIOS升级到1.6版本无法回退
  14. 牛客-Java计算个人所得税
  15. centos7.x配置mysql初始密码
  16. 《有效的单元测试》第三章
  17. 权限管理需要哪几张表
  18. Could not transfer artifact XXX:XXX:pom:XX from/to镜像地址
  19. 公司新来了个00后卷王,一副毛头小子的样儿,哪想到...
  20. 腾讯与360之间引起的反思

热门文章

  1. OC中protocol、category和继承的区别
  2. 嵌入式成长轨迹54 【Zigbee项目】【CC2430基础实验】【系统睡眠工作状态】
  3. 强大Jquery插件,table排序之二
  4. 使用Altera综合工具Quartus II下载到FPGA时无法识别USB-Blaster问题
  5. C/C++之变长数组(VLA)和可伸缩型数组成员
  6. JAVA学习之路 不走弯路,就是捷径
  7. vue-router 手势滑动触发返回
  8. 每天坚持一个CSS——社会人
  9. LAMP架构之个人博客搭建
  10. 利用shell监控cpu、磁盘、内存使用率