为什么80%的码农都做不了架构师?>>>   

线程池出来时让人眼前一亮,随笔写了一篇 nginx另一个性能杀手锏-线程池。想让更多人关注这个例好。现在想来有点偏,改为'nginx开发杀手锏-线程池'更贴切点。

来自大神的指点
agentzh:你这是滥用 nginx 的线程池。线程池的引入是为了处理不得不阻塞的 I/O,比如文件 I/O. 将之用于本可以 100% 非阻塞的网络 I/O 是巨大的退步,让 nginx 退化为 Apache worker fpm 了,靠 OS 线程来拼并发,彻底告别 C10K 了。nginx 官方介绍此特性的博客也是拿 disk I/O 来举例的。

1、开发nginx的挑战
虽然nginx的源码非常精致,但是不得不说开发nginx很有挑战性,越想更大程度上定制自己的模块,越需要对nginx的每个细节了解颇深。而且跟第三方服务通讯是非常需要的业务,你必须熟悉upstream,需要关注如何构造请求,如何解析响应,处理错误等。而且大部分情况下异步第三方服务处理的库不够丰富,反之同步的库足够丰富稳定,一般开源的服务器都提供c api开发库,比如mysql, redis, mongodb等等。

2、线程池的出现
nginx的异步处理方式让它的性能发挥到极致,但如果折衷下稍微在性能上退一点,但获得更大方便的开发,这是很值得研究和投入的。线程池可以让你按同步方式去实现你的业务,当然线程池可以应用的地方更多,但是开发这块是我们要关注的。因此,线程池是对那些熟悉c,又不需要非常熟悉nginx源码的开发者是个极大利好消息。

3、内部实现
现在切入源码分析,看下其内部是如何实现的。首先,您可以定义多个线程池,每个线程池有自己的名称,在nginx启动时将根据线程池配置信息处理线程。
src/core/ngx_thread_pool.c

static ngx_int_t
ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
{
    ......
    tpp = tcf->pools.elts;
    for (i = 0; i < tcf->pools.nelts; i++) {
        if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) {
            return NGX_ERROR;

}    
    }
    return NGX_OK;
}

static ngx_int_t
ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)
{
    ......
    for (n = 0; n < tp->threads; n++) {
        err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);
        ......
    }
    ......
    return NGX_OK;
}

static void *
ngx_thread_pool_cycle(void *data)
{
    ......
    for ( ;; ) {
        ......
        ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log);
        ......
        task->handler(task->ctx, tp->log); /* 执行任务处理,可以是阻塞操作 */
        .....
        (void) ngx_notify(ngx_thread_pool_handler); /* 让主线程得到回调 */
    }
}

在nginx工作进程启动时,被创建的线程也进入预备状态,这个很类似epoll_wait,但线程是在条件满足时进入wait的代码流程的。所以nginx已经将线程的外部流程做了足够的工作,让你可以只关注跑在线程里的开发。接下来了解下线程开发需要知道的结构体。每个线程池有一个任务队列,挂着你添加的任务,任务的结构体为:

struct ngx_thread_task_s {       
    ngx_thread_task_t   *next;   
    ngx_uint_t           id;
    void                *ctx;    
    void               (*handler)(void *data, ngx_log_t *log);
    ngx_event_t          event;  
};

其实非常的简单,你只需要关注两个 handler和event:
handler是跑在线程里的处理,即上面的task->handler。
event是用于回调给主线程的,所以还需要注册一个event->handler,这个是nginx内部的eventfd机制处理的。

4、开发实践
以nginx-http-mysql-module为例解释下如何开发:

a、添加任务
tp = mlcf->thread_pool;
task = ngx_thread_task_alloc(r->pool, 0);
if (task == NULL) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

task->ctx = r;
task->handler = ngx_http_mysql_thread_handler;

task->event.data = r;
task->event.handler = ngx_http_mysql_thread_event_handler;

if (ngx_thread_task_post(tp, task) != NGX_OK) {
    return NGX_HTTP_INTERNAL_SERVER_ERROR;
}

b、处理线程任务
ctx = ngx_http_get_module_ctx(r, ngx_http_mysql_module);
rc = ngx_http_mysql_run(r);
if (rc == NGX_ERROR) {
    ctx->err = 1;
}

这里需要注意的是,在线程里执行的代码不要处理任务错误,将执行结果放到ctx即可。

c、处理回调
r = ev->data;
ctx = ngx_http_get_module_ctx(r, ngx_http_mysql_module);
if (ctx->err) {
    ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
    return;
}

rc = ngx_http_mysql_output(r);
ngx_http_finalize_request(r, rc);

开发非常简单吧,只需要了解简单的nginx处理,大部分业务开发可以利用第三方现成的api库。

5、想法需要更多碰撞

线程池可以发挥更多作用,比如可以把连接放到线程池里。nginx的异步加lua的协程是个非常好的组合,现在有了线程池后,线程池加协程将是另一个选择。总而言之,如果在保证性能的情况下,让nginx开发变得非常简单,这是非常利好的消息。真诚欢迎更多关于这个话题的见解。

推荐阅读:
nginx另一个性能杀手锏-线程池
NGINX使用线程池提升性能9x倍

转载于:https://my.oschina.net/fqing/blog/472273

nginx源码分析之线程池相关推荐

  1. nginx源码分析之内存池与线程池丨nginx的多进程网络实现

    nginx源码分析之内存池与线程池 1. nginx的使用场景 2. nginx源码 内存池,线程池,日志 3. nginx的多进程网络实现 视频讲解如下,点击观看: [Linux后台开发系统]ngi ...

  2. 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化

    人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...

  3. 深入源码分析Java线程池的实现原理

    转载自   深入源码分析Java线程池的实现原理 程序的运行,其本质上,是对系统资源(CPU.内存.磁盘.网络等等)的使用.如何高效的使用这些资源是我们编程优化演进的一个方向.今天说的线程池就是一种对 ...

  4. Hbase Compaction 源码分析 - CompactSplitThread 线程池选择

    目录 CompactSplitThread requestCompactionInternal方法 selectCompaction方法 requestCompaction方法 其他相关文章 Hbas ...

  5. Libuv源码分析 —— 8. 线程池

    网络I/O 在 上一节 的学习中,我们已经搞明白了网络I/O的基本过程,并通过了解进程/线程间通信来熟悉这个流程.下面,让咱们学习线程池中的线程如何工作.并和主进程进行通信的吧! 线程池 Libuv ...

  6. 从源码分析创建线程池的4种方式

    摘要:从创建线程池的源码来深入分析究竟有哪些方式可以创建线程池. 本文分享自华为云社区<[高并发]从源码角度分析创建线程池究竟有哪些方式>,作者:冰 河 . 在Java的高并发领域,线程池 ...

  7. nginx源码分析之内存池实现原理

    建议看本文档时结合nginx源码: 1.1   什么是内存池?为什么要引入内存池? 内存池实质上是接替OS进行内存管理,应用程序申请内存时不再与OS打交道,而是从内存池中申请内存或者释放内存到内存池, ...

  8. Nginx源码分析——ngx_pool_t内存池

    引言: C/C++下内存管理是让几乎每一个程序员头疼的问题,分配足够的内存.追踪内存的分配.在不需要的时候释放内存--这个任务相当复杂.而直接使用系统调用malloc/free.new/delete进 ...

  9. Nginx源码分析:核心数据结构ngx_cycle_t与内存池概述

    nginx源码分析 nginx-1.11.1 参考书籍<深入理解nginx模块开发与架构解析> 核心数据结构与内存池概述 在Nginx中的核心数据结构就是ngx_cycle_t结构,在初始 ...

  10. nginx源码分析—内存池结构ngx_pool_t及内存管理

    本博客( http://blog.csdn.net/livelylittlefish)贴出作者(阿波)相关研究.学习内容所做的笔记,欢迎广大朋友指正! Content 0.序 1.内存池结构 1.1 ...

最新文章

  1. Android开源项目分类汇总[转]
  2. Hive 基础-进阶
  3. JavaScript零散知识点总结
  4. 一个 http 请求的曲折经历
  5. 详解进程的虚拟内存,物理内存,共享内存
  6. xml内容过多装不下,怎么实现下滑功能(最简单的下滑功能实现)
  7. mysql触发器学习的一个小错误
  8. python的底层实现,Python封装底层实现原理详解(通俗易懂)
  9. 【图说Word】怎么在word的每一行前面和后面加上同一个字或字符?
  10. 基于JBox2d物理引擎和canvas的游戏开发实例
  11. centos7搭建单机kafka集群
  12. python3中input()方法报错traceback变量未定义的解决方法
  13. python中new方法详解及_Python中__new__与__init__方法的区别详解
  14. 计算机会计系统管理,会计电算化系统管理实验报告.doc
  15. 基于javacv的视频转码(升级版)
  16. 2022小旋风万能蜘蛛池9.02开心版/站长必备SEO+带教程
  17. Linux下C语言开发通讯录管理软件(一)
  18. java拍照控件焦距问题,监控摄像头镜头焦距计算方法
  19. 数字电路逻辑化简公式
  20. 并发编程--线程池原理

热门文章

  1. Java关流对流对象有什么影响_Java面试题全集(1.4)
  2. sublime 如何使用less_【图文】5分钟可以学会在vue里使用sass?
  3. c++ opencv mat_【CV实战】OpenCV—Hello world代码示例
  4. matlab中x.^2与x^2有什么区别?
  5. vivi开发笔记【专辑】
  6. 站内消息弹出层简单实现
  7. SpringBoot(九):fastjson、异常处理
  8. Android面试问题收集总结
  9. 关于djangorestframework
  10. 理解Python中的装饰器