注:运行环境CentOS 6+
 
背景
     在对启动了20个worker的nginx进行压力测试的时候发现:如果把配置文件中event配置块中的accept_mutex开关打开(1.11.3版本之前默认开),就会出现worker压力不均,少量的worker的cpu利用率达到了98%,大部分的worker的压力只有1%左右;如果把accept_mutex关掉,所有的worker的压力差别就不大,而且QPS会有大幅提升;
 
分析过程
  1. nginx的 1(master)+N(worker) 多进程模型:master在启动过程中负责读取nginx.conf中配置的监听端口,然后加入到一个cycle->listening数组中。
  2. init_cycle函数中会调用init_module函数,init_module函数会调用所有注册模块的module_init函数完成相关模块所需资源的申请以及其他一些工作;其中event模块的module_init函数申请一块共享内存用于存储accept_mutex锁信息以及连接数信息
    1. //////////// nginx/src/event/ngx_event.c ///////
    2.  
    3. shm.size = size;
    4. shm.name.len = sizeof("nginx_shared_zone") - 1;
    5. shm.name.data = (u_char *) "nginx_shared_zone";
    6. shm.log = cycle->log;
    7.  
    8. if (ngx_shm_alloc(&shm) != NGX_OK) {
    9. return NGX_ERROR;
    10. }
    11.  
    12. shared = shm.addr;
    13.  
    14. ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
    15. ngx_accept_mutex.spin = (ngx_uint_t) -1;
    16.  
    17. if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,
    18. cycle->lock_file.data)
    19. != NGX_OK)
    20. {
    21. return NGX_ERROR;
    22. }
     所有的worker进程均是由master进程通过fork() 函数启动的,所以所有的worker进程也就继承了master进程所有打开的文件描述符(包括之前创建的共享内存的fd)以及变量数据(这其中就包括之前创建的accept_mutex锁)。worker启动的过程中会调用各个模块的process_init函数,其中event模块的process_init函数中就会将master配置好的listening数组加入到epoll监听的events中,这样初始阶段所有的worker的epoll监听列表中都包含listening数组中的fd。

  1. //////////// nginx/src/event/ngx_event.c ///////
  2.  
  3. /* for each listening socket */
  4.  
  5. ls = cycle->listening.elts;
  6. for (i = 0; i < cycle->listening.nelts; i++) {
  7.  
  8. #if (NGX_HAVE_REUSEPORT)
  9. if (ls[i].reuseport && ls[i].worker != ngx_worker) {
  10. continue;
  11. }
  12. #endif
  13.  
  14. c = ngx_get_connection(ls[i].fd, cycle->log);
  15.  
  16. if (c == NULL) {
  17. return NGX_ERROR;
  18. }
  19.  
  20. c->type = ls[i].type;
  21. c->log = &ls[i].log;
  22.  
  23. c->listening = &ls[i];
  24. ls[i].connection = c;
  25.  
  26. rev = c->read;
  27.  
  28. rev->log = c->log;
  29. rev->accept = 1;
  30. ...............
     当各个worker实际运行时,就会执行ngx_process_events_and_timers函数,这个函数会获取accept_mutex锁,同时所有的事件也会增加一个NGX_POST_EVENTS标识,其他获取不到的锁的worker就会将listening数组中fd从epoll监听列表中移除,这样下次listenling数组发生新的连接时,只有持有accept_mutex的worker才会响应。

  1. //////////// nginx/src/event/ngx_event.c ///////
  2.  
  3. if (ngx_use_accept_mutex) {
  4. if (ngx_accept_disabled > 0) {
  5. ngx_accept_disabled--;
  6.  
  7. } else {
  8. if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
  9. return;
  10. }
  11.  
  12. if (ngx_accept_mutex_held) {
  13. flags |= NGX_POST_EVENTS; //增加NGX_POST_EVENTS标识
  14.  
  15. } else {
  16. if (timer == NGX_TIMER_INFINITE
  17. || timer > ngx_accept_mutex_delay)
  18. {
  19. timer = ngx_accept_mutex_delay;
  20. }
  21. }
  22. }
  23. }
  24.  
  25. ////////////////////////
  26. ngx_int_t
  27. ngx_trylock_accept_mutex(ngx_cycle_t *cycle)
  28. {
  29. if (ngx_shmtx_trylock(&ngx_accept_mutex)) {
  30.  
  31. ngx_log_debug0(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  32. "accept mutex locked");
  33.  
  34. if (ngx_accept_mutex_held && ngx_accept_events == 0) {
  35. return NGX_OK;
  36. }
  37.  
  38. if (ngx_enable_accept_events(cycle) == NGX_ERROR) { //将cycle->listening加入到当前worker进程的epoll
  39. ngx_shmtx_unlock(&ngx_accept_mutex);
  40. return NGX_ERROR;
  41. }
  42.  
  43. ngx_accept_events = 0;
  44. ngx_accept_mutex_held = 1;
  45.  
  46. return NGX_OK;
  47. }
  48.  
  49. ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  50. "accept mutex lock failed: %ui", ngx_accept_mutex_held);
  51.  
  52. if (ngx_accept_mutex_held) {
  53. if (ngx_disable_accept_events(cycle, 0) == NGX_ERROR) { //将cycle->listening从当前worker进程的epoll移除
  54. return NGX_ERROR;
  55. }
  56.  
  57. ngx_accept_mutex_held = 0;
  58. }
  59.  
  60. return NGX_OK;
  61. }
 
     对于持有accept_mutex锁的worker进程,会将epoll返回的fd放到队列中,尽可能的从epoll中接受新连接。当不持有accept_mutex 锁时在对队列中的这些fd进行处理。

  1. ///////////////// nginx/src/event/modules/ngx_epoll_module.c //////////
  2.  
  3. if (flags & NGX_POST_EVENTS) {
  4. queue = rev->accept ? &ngx_posted_accept_events
  5. : &ngx_posted_events;
  6.  
  7. ngx_post_event(rev, queue);
  8.  
  9. } else {
  10. rev->handler(rev);
  11. }
 
     worker每次处理从epoll中读取新连接结束后,就会调用accept处理新建连接,并调用新连接的异步回掉函数进行处理,在处理新建连接的同时会实时统计1/8 * worker_connection - free_connecttion 的差值ngx_accept_disabled 。处理accept新连接结束后,就会释放accept_mutex锁,然后在去处理一些普通的连接请求。worker的下次cycle,会首先判断ngx_accept_disabled的值,如果大于0说明该worker当前处理连接数大于总连接数的7/8,不再参与accept_mutex的竞争。

  1. //////////// nginx/src/event/ngx_event.c ///////////
  2.  
  3. (void) ngx_process_events(cycle, timer, flags);
  4.  
  5. delta = ngx_current_msec - delta;
  6.  
  7. ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
  8. "timer delta: %M", delta);
  9.  
  10. ngx_event_process_posted(cycle, &ngx_posted_accept_events);
  11.  
  12. if (ngx_accept_mutex_held) {
  13. ngx_shmtx_unlock(&ngx_accept_mutex);
  14. }
  15.  
  16. if (delta) {
  17. ngx_event_expire_timers();
  18. }
  19.  
  20. ngx_event_process_posted(cycle, &ngx_posted_events);
 
结论
  1. 上述分析的主要是accept_mutex打开的情况。对于不打开的情况,比较简单,所有worker的epoll都会监听listening数组中的所有fd,所以一旦有新连接过来,就会出现worker“抢夺资源“的情况。对于分布式的大量短链接来讲,打开accept_mutex选项较好,避免了worker争夺资源造成的上下文切换以及try_lock的锁开销。但是对于传输大量数据的tcp长链接来讲,打开accept_mutex就会导致压力集中在某几个worker上,特别是将worker_connection值设置过大的时候,影响更加明显。因此对于accept_mutex开关的使用,根据实际情况考虑,不可一概而论。
  2. 根据分析我们的压测程序,发现是采用的长tcp连接的方式,然后调用http请求;而且worker_connection也比较大,这样就出现了accept_mutex打开worker负载不均造成QPS下降的问题。
  3. 目前新版的Linux内核中增加了EPOLLEXCLUSIVE选项,nginx从1.11.3版本之后也增加了对NGX_EXCLUSIVE_EVENT选项的支持,这样就可以避免多worker的epoll出现的惊群效应,从此之后accept_mutex从默认的on变成了默认off。

accept_mutex与性能的关系 (nginx)的更多相关文章

  1. paip.提升性能---mysql 优化cpu多核以及lan性能的关系.

    paip.提升性能---mysql 优化cpu多核以及lan性能的关系. 作者Attilax  艾龙,  EMAIL:1466519819@qq.com 来源:attilax的专栏 地址:http:/ ...

  2. 浅析I/O处理过程与存储性能的关系

    浅析I/O处理过程与存储性能的关系 https://community.emc.com/docs/DOC-28653 性能”这个词可以说伴随着整个IT行业的发展,每次新的技术出现,从硬件到软件大多数情 ...

  3. 高性能web服务器(热死你)Resin Linux的安装、配置、部署,性能远超Nginx支持Java、PHP等

    高性能web服务器(热死你)Resin Linux的安装.配置.部署,性能远超Nginx支持Java.PHP等 一.    安装resin 1.  下载resin: 下载地址:http://cauch ...

  4. API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd API 网关出现的原因

    API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd http://www.infoq.com/cn/articles/compa ...

  5. NGINX比Apache的性能高是因为NGINX由C语言开发,而Apache由C++开发

    事实上,NGINX比Apache的性能高是因为NGINX由C语言开发,而Apache由C++开发.因此,NGINX效率大概是Apache的10倍左右

  6. web性能优化——代理(nginx)

    简介 一个很好的原则是调优时每次只个性一个配置.如果对配置的个性不能提高性能的话,改回默认值 优化必须要通过性能测试.不能意淫,需要前后对比,真实说明问题. 场景 优化nginx. 确保每次请求控制一 ...

  7. API Gateway性能比较:NGINX vs. ZUUL vs.Cloud Gateway vs. Linkerd[译]

      2018-03-04 15:07 联发科的反思 前几天拜读了 OpsGenie 公司(一家致力于 Dev & Ops 的公司)的资深工程师 Turgay elik 博士写的一篇文章(链接在 ...

  8. API网关性能比较:NGINX vs. ZUUL vs. Spring Cloud Gateway vs. Linkerd(转)

    前几天拜读了 OpsGenie 公司(一家致力于 Dev & Ops 的公司)的资深工程师 Turgay Çelik 博士写的一篇文章(链接在文末),文中介绍了他们最初也是采用 Nginx 作 ...

  9. HTTP/2 服务器推送(Server Push)教程(HTTP/2 协议的主要目的是提高网页性能,配置Nginx和Apache)

    HTTP/2 协议的主要目的是提高网页性能. 头信息(header)原来是直接传输文本,现在是压缩后传输.原来是同一个 TCP 连接里面,上一个回应(response)发送完了,服务器才能发送下一个, ...

随机推荐

  1. 数据库优化案例——————某市中心医院HIS系统

    记得在自己学习数据库知识的时候特别喜欢看案例,因为优化的手段是容易掌握的,但是整体的优化思想是很难学会的.这也是为什么自己特别喜欢看案例,今天也开始分享自己做的优化案例. 最近一直很忙,博客产出也少的 ...

  2. Python高手之路【一】初识python

    Python简介 1:Python的创始人 Python (英国发音:/ˈpaɪθən/ 美国发音:/ˈpaɪθɑːn/), 是一种解释型.面向对象.动态数据类型的高级程序设计语言,由荷兰人Guido ...

  3. 用scikit-learn学习DBSCAN聚类

    在DBSCAN密度聚类算法中,我们对DBSCAN聚类算法的原理做了总结,本文就对如何用scikit-learn来学习DBSCAN聚类做一个总结,重点讲述参数的意义和需要调参的参数. 1. scikit ...

  4. Asp.Net WebApi核心对象解析(上篇)

    生活需要自己慢慢去体验和思考,对于知识也是如此.匆匆忙忙的生活,让人不知道自己一天到晚都在干些什么,似乎每天都在忙,但又好似不知道自己到底在忙些什么.不过也无所谓,只要我们知道最后想要什么就行.不管怎 ...

  5. css样式之border-radius

    border-radius 属性设置边框的园角 可能的值:像素,百分比 扩展延伸 html代码 <div></div> css代码 div { height: 200px; w ...

  6. RMS Server打开或关闭日志记录

    原文: https://technet.microsoft.com/zh-cn/library/cc732758 在 Active Directory Rights Management Servic ...

  7. Android—简单的仿QQ聊天界面

    最近仿照QQ聊天做了一个类似界面,先看下界面组成(画面不太美凑合凑合呗,,,,):

  8. 敏捷转型历程 - Sprint4 回顾会

    我: Tech Leader 团队:团队成员分布在两个城市,我所在的城市包括我有4个成员,另外一个城市包括SM有7个成员.另外由于我们的BA离职了,我暂代IT 的PO 职位.PM和我在一个城市,但他不 ...

  9. Hadoop伪分布式集群环境搭建

    本教程讲述在单机环境下搭建Hadoop伪分布式集群环境,帮助初学者方便学习Hadoop相关知识. 首先安装Hadoop之前需要准备安装环境. 安装Centos6.5(64位).(操作系统再次不做过多描 ...

  10. 练习JavaScript判断上传文件后缀名

    <script type = text/javascript> function jiance(filename) { var pic = ["jpg","p ...