一、概况

Nginx可以开启多个进程,每个进程拥有最大上限128个子线程以及一定的可用连接数。最大客户端连接数等于进程数与连接数的乘积,连接是在主进程中初始化的,一开始所有连接处于空闲状态。每一个客户端请求进来以后会通过事件处理机制,在Linux是Epoll,在FreeBSD下是KQueue放到空闲的连接里。

如果设置了线程数,那么被填充的连接会在子线程中处理,否则会在主线程中依次处理。

如果解析出是动态脚本请求,会根据fast-cgi的设置访问php-cgi进程,php进程数量的多少依据php-fpm.conf中max_children的设置。

因此Nginx的动态请求能力不仅仅依靠Nginx本身的设置,还要调试php-fpm。

从源代码级别上看nginx由以下几个元素组成:

1. worker(进程)

2. thread(线程)

3. connection(连接)

4. event(事件)

5. module(模块)

6. pool(内存池)

7. cycle(全局设置)

8. log(日志)

二、MAIN函数

整个程序从main()开始算,代码更详细的内容,可以查看两外一篇文章:http://www.cnblogs.com/liqiu/p/3500357.html

   ngx_max_module = ;

    for (i = ; ngx_modules[i]; i++) {

        ngx_modules[i]->index = ngx_max_module++;

    }

这几句比较关键,对加载的模块点一下数,看有多少个。ngx_modules并不是在原代码中被赋值的,你先执行一下./configure命令生成用于编译的make环境。在根目录会多出来一个文件夹objs,找到ngx_modules.c文件,默认情况下nginx会加载大约40个模块,的确不少,如果你不需要那个模块尽量还是去掉好一些。

接下来比较重要的函数是 ngx_init_cycle(),这个函数初始化系统的配置以及网络连接等,如果是多进程方式加载的会继续调用ngx_master_process_cycle(),这是main函数中调用的最关键的两个函数。

三、模块初始化

ngx_init_cycle()实际上是个复杂的初始化函数,首先是加载各子模块的配置信息、并初始化各组成模块。任何模块都有两个重要接口组成,一个是create_conf,一个是init_conf。分别是创建配置和初始化配置信息。模块按照先后顺序依次初始化,大概是这样的:

&ngx_core_module,

&ngx_errlog_module,

&ngx_conf_module,

&ngx_events_module,

&ngx_event_core_module,

&ngx_epoll_module,

&ngx_http_module,

&ngx_http_core_module,

&ngx_http_log_module,

首先是内核模块、错误日志、配置模块、事件模块、时间内核模块、EPOLL模块、http模块、http内核模块、http日志模块,剩下的模块都算不上关键。

epoll是比较关键的核心模块之一,nginx兼容多种IO控制模型,memecached用的是libevent不如nginx彻头彻尾是自己实现的。

四、socket初始化

在ngx_init_cycle()中对模块初始化完毕后,调用ngx_open_listening_sockets()函数对socket进行了初始化。在listen上80端口以后,调用模块的另外一个重要接口init_module对各模块进行初始化。并不是每个模块都对init_module接口进行了定义,在比较重要的模块中仅有 ngx_http_log_module 对这个接口进行了定义。

五、进程初始化

ngx_init_cycle()返回后,主要的工作都是在ngx_master_process_cycle()函数中继续进行的。ngx_master_process_cycle()函数中的重要过程是调用ngx_start_worker_processes()生成多个子进程,一般nginx是多进程的。ngx_start_worker_processes()函数内部调用ngx_worker_process_cycle()函数建立每个进程的实际工作内容,在这个函数中首先调用ngx_create_thread()初始化各线程。我们知道每个线程都有一个启动处理函数,nginx的线程处理函数为ngx_worker_thread_cycle(),内部过程中最重要的是对ngx_event_thread_process_posted()函数的调用,用于实际处理每一次请求。

在初始化线程结束后,首先调用ngx_process_events_and_timers()函数,该函数继续调用ngx_process_events接口监听事件,一般情况下对应的函数是ngx_epoll_process_events(),如果使用的是其它种类的IO模型,则应该实现相应的实际函数。这个接口负责把事件投递到ngx_posted_events事件队列里,并在 ngx_event_thread_process_posted()函数中进行处理。

六、connection和event

nginx的connection和event是按照链表的方式进行存放的,区别在于connection是单向链表而event是双向链表。nginx中的connection链表是提前分配的,定义在ngx_event_process_init()函数内,具体代码如下:

    ...

    cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * ecf->connections, cycle->log);

    ...

    i = cycle->connection_n;

    next = NULL;

    do {

        i--;

        c[i].data = next;

        c[i].read = &cycle->read_events[i];

        c[i].write = &cycle->write_events[i];

        c[i].fd = (ngx_socket_t) -;

        next = &c[i];

#if (NGX_THREADS)

        c[i].lock = ;

#endif

    } while (i);

    cycle->free_connections = next;

    cycle->free_connection_n = ecf->connections;

在内存池里为所有connection分配空间,并依次初始化,并依次初始化各连接的链表关系,也就是在data上存下一个connection的指针。在具体应用中通过ngx_get_connection()函数取出空闲的connection并使用。

至于event是一个双向链表,链表的入队和出队有下面的定义:

#define ngx_locked_post_event(ev, queue)                                      \if (ev->prev == NULL) {                                                   \

        ev->next = (ngx_event_t *) *queue;                                    \

        ev->prev = (ngx_event_t **) queue;                                    \

        *queue = ev;                                                          \

        if (ev->next) {                                                       \

            ev->next->prev = &ev->next;                                       \

        }                                                                     \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, , "post event %p", ev); \

    } else {                                                                 \

        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, , "update posted event %p", ev);                         \

    }

#define ngx_post_event(ev, queue)                                             \

    ngx_mutex_lock(ngx_posted_events_mutex);                                  \

    ngx_locked_post_event(ev, queue);                                         \

    ngx_mutex_unlock(ngx_posted_events_mutex);

#define ngx_delete_posted_event(ev)                                           \*(ev->prev) = ev->next;                                                   \

    if (ev->next) {                                                           \

        ev->next->prev = ev->prev;                                            \

    }                                                                         \

    ev->prev = NULL;                                                          \

    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, , "delete posted event %p", ev);

简单说 ngx_post_event 用于插入事件、ngx_delete_posted_event 用于删除事件。这两个是宏定义,会比函数定义用起来节省。整个程序就围绕这event和connection进行。不断的投入、拿出。

七、nginx的模块处理

nginx由若干模块组成,但所有模块全部采用静态编译的办法。我们以rewrite模块为例,我们输入./configure --help 命令可以查看的有关rewrite模块的描述 --without-http_rewrite_module      disable ngx_http_rewrite_module。这是在auto/options下定义的,在auto/soruces里面指定了与这个模块有关的源代码位置

HTTP_REWRITE_MODULE=ngx_http_rewrite_module

HTTP_REWRITE_SRCS=src/http/modules/ngx_http_rewrite_module.c

在auto/summary下提供了关于rewrite模块的依赖条件检查:

if [ $HTTP_REWRITE = YES ]; then

    if [ $USE_PCRE = DISABLED ]; then 

cat << END

$: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option or you have to enable the PCRE support.

END

        exit 

    fi

    if [ $PCRE = NONE -o $PCRE = NO ]; then

cat << END

$: error: the HTTP rewrite module requires the PCRE library.

You can either disable the module by using --without-http_rewrite_module

option, or install the PCRE library into the system, or build the PCRE library

statically from the source with nginx by using --with-pcre=<path> option.

END

        exit 

    fi

fi

下面是对rewrite模块代码分析,其它模块也带有同样的结构。

一般模块都会有一个ngx_command_t结构,存放在ngingx配置文件中,这个模块的一些语法命令,详细结构为:

struct ngx_command_s {

    ngx_str_t             name;

    ngx_uint_t            type;

    char               *(*set)(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);

    ngx_uint_t            conf;

    ngx_uint_t            offset;

    void                 *post;

};
  • name 是名称
  • type 是类型
  • set 是处理这个命令的具体函数
  • conf 属于那种配置,一共三种 main结构、server结构、location结构。
  • offset 在配置文件中的偏移量。
  • post 没见到用到。

下面是和rewrite模块中的代码:

static ngx_command_t ngx_http_rewrite_commands[] = {

    { ngx_string("rewrite"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF

                       |NGX_CONF_TAKE23,

      ngx_http_rewrite,

      NGX_HTTP_LOC_CONF_OFFSET,

      ,

      NULL },

    { ngx_string("return"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1,

      ngx_http_rewrite_return,

      NGX_HTTP_LOC_CONF_OFFSET,

      ,

      NULL }, 

    { ngx_string("break"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_NOARGS,

      ngx_http_rewrite_break,

      NGX_HTTP_LOC_CONF_OFFSET,

      ,

      NULL }, 

    { ngx_string("if"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,

      ngx_http_rewrite_if,

      NGX_HTTP_LOC_CONF_OFFSET,

      ,

      NULL },

    { ngx_string("set"),

      NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE2,

      ngx_http_rewrite_set,

      NGX_HTTP_LOC_CONF_OFFSET,

      ,

      NULL }, 

    { ngx_string("rewrite_log"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF |NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, log),

      NULL },

    { ngx_string("uninitialized_variable_warn"),

      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF|NGX_CONF_FLAG,

      ngx_conf_set_flag_slot,

      NGX_HTTP_LOC_CONF_OFFSET,

      offsetof(ngx_http_rewrite_loc_conf_t, uninitialized_variable_warn),

      NULL },

      ngx_null_command

};

rewrite模块内部一共有四类函数,关键的一类为一类是动作处理函数,一共有:

  • ngx_http_rewrite_init 模块初始化函数。
  • ngx_http_rewrite_handler 在具体请求中的处理函数。

这两个函数通过在模块初始化的过程中,把handler推送到phases结构中,这是一个简单数组,请求会对数组中存放的每一个handler依次进行处理。如果我们自己写什么模块也应该放进去,例如同样的ngx_http_access_init()等初始化函数也是把自己模块的ngx_http_access_handler注册进去。

nginx在内部已经定义了所有可以在http请求中被处理的模块列表:

typedef enum {

    NGX_HTTP_POST_READ_PHASE = ,

    NGX_HTTP_SERVER_REWRITE_PHASE,

    NGX_HTTP_FIND_CONFIG_PHASE,

    NGX_HTTP_REWRITE_PHASE,

    NGX_HTTP_POST_REWRITE_PHASE,

    NGX_HTTP_PREACCESS_PHASE,

    NGX_HTTP_ACCESS_PHASE,

    NGX_HTTP_POST_ACCESS_PHASE,

    NGX_HTTP_CONTENT_PHASE, 

    NGX_HTTP_LOG_PHASE

} ngx_http_phases;

和rewrite有关的一共是两个,都是在ngx_http_rewrite_handler()函数中进行处理,一类为与配置文件有关的创建和合并函数,一共有:

  • ngx_http_rewrite_create_loc_conf
  • ngx_http_rewrite_merge_loc_conf

一类作为common命令的解析函数,一共有:

  • ngx_http_rewrite
  • ngx_http_rewrite_return
  • ngx_http_rewrite_break
  • ngx_http_rewrite_if
  • ngx_http_rewrite_set

一类为解析命令中内部使用的函数,一共有:

  • ngx_http_rewrite_if_condition
  • ngx_http_rewrite_variable
  • ngx_http_rewrite_value
  • ngx_http_rewrite_var

NGINX源码分析——概览的更多相关文章

  1. nginx源码分析之网络初始化

    nginx作为一个高性能的HTTP服务器,网络的处理是其核心,了解网络的初始化有助于加深对nginx网络处理的了解,本文主要通过nginx的源代码来分析其网络初始化. 从配置文件中读取初始化信息 与网 ...

  2. nginx源码分析--使用GDB调试(strace、 pstack )

    nginx源码分析--使用GDB调试(strace.  pstack ) http://blog.csdn.net/scdxmoe/article/details/49070577

  3. Nginx源码分析:3张图看懂启动及进程工作原理

    编者按:高可用架构分享及传播在架构领域具有典型意义的文章,本文由陈科在高可用架构群分享.转载请注明来自高可用架构公众号「ArchNotes」.   导读:很多工程师及架构师都希望了解及掌握高性能服务器 ...

  4. nginx源码分析-源码结构

    本文主要简单介绍nginx源码目录结构.程序编译流程.如何构建学习nginx的环境等.本文以及后续nginx源码分析文章是基于nginx当前(2009-02-27)的稳定版本0.6.35进行的分析,该 ...

  5. nginx源码分析线程池详解

    nginx源码分析线程池详解 一.前言     nginx是采用多进程模型,master和worker之间主要通过pipe管道的方式进行通信,多进程的优势就在于各个进程互不影响.但是经常会有人问道,n ...

  6. nginx源码分析——configure脚本

    源码:nginx 1.13.0-release   一.前言      在分析源码时,经常可以看到类似 #if (NGX_PCRE) .... #endif 这样的代码段,这样的设计可以在不改动源码的 ...

  7. Nginx源码分析--数组(转)

    原文地址:http://blog.csdn.net/marcky/article/details/5747431 备注:以下关于Nginx源码的分析基于淘宝开源项目Tengine. Nginx中对数组 ...

  8. [nginx] nginx源码分析--SNI性能分析

    概念 我们已经知道什么是SNI,以及如何为用户配置SNI. [nginx] nginx使用SNI功能的方法 问题 通过观察配置文件,可以发现,针对每一个SSL/TLS链接, nginx都会动态的查找( ...

  9. nginx源码分析之hash的实现

    nginx实现了自己的hash数据结构,正如数据结构中讲述的那样,nginx用开放链表法解决冲突,不过不同的是一旦一个hash表被初始化后就不会被修改,即插入和删除,只进行查询操作,所以nginx通过 ...

随机推荐

  1. 三层架构下实现用户登陆C#

    上篇文章讲到三层.接下来就通过一个实例详细的看怎么用三层实现用户登陆界面. 一.Model实体(LoginModel): namespace LoginModel { //加入类:UserInfo M ...

  2. Java CMYK图片转RGB图片(TwelveMonkeys方式)

    TwelveMonkeys的使用比较简单,只要把相关的jar包加入到类路径,他的类我们基本不会用到,只要使用jdk ImageIO或其上层的接口就行了.jdk的ImageIO有自动发现功能,会自动查找 ...

  3. 豪斯医生第一季/全集House M.D 1迅雷下载

    豪斯医生 第一季 House M.D. Season 1 (2004)本季看点:态度无礼,表情凶恶,跛足拄着一根藤棍,永远是牛仔裤运动鞋的便装打扮而不是整洁的白大褂,普林斯顿大学附属医院的格雷戈·豪斯 ...

  4. Xcode修改项目的Build Location

    Xcode默认project通过build输出的目录,存放于目录 /Users/用户名/Library/Developer/Xcode/DerivedData/产品名称-hjwqiconnjhpjle ...

  5. Java(C#)基础差异-数组

    1.填充数组 Java 数组填充替换方法Arrays.fill() 举例如下: import java.util.Arrays; public class FillDemo { public stat ...

  6. [转]Zend Studio中将tab转换为4个空格

    From : http://our2848884.blog.163.com/blog/static/14685483420129318619284/ 例子如下:  1 选中需要转换的区域   2 Ct ...

  7. [转]MySQL单列索引和组合索引的区别介绍

    FROM : http://database.ctocio.com.cn/353/11664853.shtml MySQL单列索引是我们使用MySQL数据库中经常会见到的,MySQL单列索引和组合索引 ...

  8. Jackcess 1.2.13 发布,Java 访问 Access 数据库

    Jackcess 1.2.13 包含新的方法用于在数据库和附件内容解码中查找复杂值类型的关系,修复了 CodeHandler 相关的一些小 bug. Jackcess 是一个Java 类库,用来读写微 ...

  9. vim去除行显示;vim全部复制命令

    行显示和隐藏: set nu set nu! 直接上命令: ggVG*y 解释一下上面的命令: ggVG   ——全选 *y  —— 复制到剪贴板 gg 是光标定位到文件首行 V  是进入可视模式 G ...

  10. 一些NLP相关的JD,作参考

    NLP 应用研发工程师 - AI Lab工作城市: 上海岗位描述:1.用户搜索意图识别,分词和词性标注,实体词标注 2.利用机器学习.NLP 技术优化文本分类.语义理解及信息抽取岗位要求:1.有自然语 ...