`

nginx源代码分析

 
阅读更多
http://hi.baidu.com/langwan/blog/item/6b18ef24cd859e064c088d28.html

nginx源代码分析2008-11-23 22:07


Nginx可以开启多个进程,每个进程拥有最大上限128个子线程以及一定的可用连接数。如果你希望使用线程可以在配置文件中设置worker_threads这个参数,但这个参数在Nginx官方手册上没有。只有通过阅读源代码才看到。最大客户端连接数等于进程数与连接数的乘积,连接是在主进程中初始化的,一开始所有连接处于空闲状态。

每一个客户端请求进来以后会通过事件处理机制,在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()开始算

    ngx_max_module = 0;
    for (i = 0; ngx_modules[i]; i++) {
        ngx_modules[i]->index = ngx_max_module++;
    }

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

接下来比较重要的函数是 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彻头彻尾是自己实现的。

在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()函数中进行处理。

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) -1;

        next = &c[i];

#if (NGX_THREADS)
        c[i].lock = 0;
#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, 0, "post event %p", ev); \
                                                                              \
    } else {                                                                 \
        ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,                        \
                       "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, 0,                            \
                   "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
$0: 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 1
    fi

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

cat << END
$0: 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 1
    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,
      0,
      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,
      0,
      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,
      0,
      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,
      0,
      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,
      0,
      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 = 0,

    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 源码 解析 资料大全

    收集的 nginx 源码 解析 ,非常全 architecture.png Emiller的Nginx模块开发指南.docx ...Nginx源代码分析.docx Nginx源代码分析与导读.ppt nginx源码分析.doc nginx源码剖析.pdf Ningx代码研究.docx

    nginx源码剖析

    nginx源码说明;其他下载即可,没下载分了,大家互相帮忙。其他情况大概就这些吧,其他也没什么好说的了,看文档即可

    Nginx源代码分析

    Nginx源代码分析是学习使用Nginx源码开发人员的指导丛书,为开发者提供了详细的Nginx源代码分析指导。下面是对Nginx源代码的分析指导。 目录结构 Nginx的源码目录结构层次明确,从自动编译脚本到各级的源码,层次...

    Nginx源代码分析.pdf

    Nginx 源代码分析 Nginx 是一个流行的开源 Web 服务器软件,广泛应用于生产环境中。本文档旨在对 Nginx 的源代码进行分析,了解其目录结构、基本数据结构、内存分配机制等关键组件。 一、目录结构 Nginx 的源代码...

    nginx源码分析

    nginx 源码分析主要涉及以下几个核心知识点: 1. **事件驱动模型**:nginx 使用的是事件驱动的架构,这使得它能够在处理大量并发连接时保持高效。它支持多种事件模型,如 Linux 的 epoll、FreeBSD 的 kqueue 等,...

    Nginx源代码分析与导读

    **Nginx源代码分析与导读** Nginx是一款高性能的Web服务器和反向代理服务器,以其高效、轻量级的特性在互联网行业中广泛应用。本文将深入探讨Nginx的源代码结构、重要概念与基础设施、事件驱动架构、HTTP处理流程...

    nginx源码分析--带注释

    总的来说,通过分析Nginx的源代码,我们可以深入理解其内部工作原理,包括事件处理、配置解析、模块化设计、内存管理和错误日志。这些知识对于优化Nginx配置、开发自定义模块或排查问题都大有裨益。因此,花时间研究...

    nginx源码分析-高性能服务器编写

    ### Nginx源码分析-高性能服务器编写 #### 前言 在互联网技术发展过程中,HTTP服务器的设计和实现一直是关键技术领域之一。从早期的C语言实现如Apache、Lighttpd,到现代流行的Nginx,这些服务器软件不仅支撑着...

    nginx源码分析文档

    在深入探讨Nginx源代码之前,我们首先需要了解Nginx的基本架构及其核心组件。 1. **worker进程**:Nginx启动时会创建一个master进程和多个worker进程。worker进程负责处理客户端请求。 2. **线程(Thread)**:...

    nginx的源码

    **Nginx源码分析与理解** Nginx是一款高性能的HTTP和反向代理服务器,以其轻量级、高并发处理能力以及优秀的稳定性在互联网行业中广泛使用。它的源代码主要用C语言编写,深入理解Nginx的源码有助于我们更好地优化...

    nginx 源代码 注释版

    在这个"nginx源代码注释版"中,我们有机会深入理解其内部工作原理,这对于开发者来说是一份宝贵的资源。 Nginx的源代码组织结构清晰,主要分为以下几个部分: 1. **事件模型**:Nginx采用异步非阻塞的事件模型,如...

    nginx源码 1.18版

    8. **模块化设计**:Nginx的模块化设计允许开发者方便地扩展其功能,只需编写特定模块的源代码,然后将其编译进Nginx即可。 分析Nginx源码,我们可以学习到以下知识点: 1. **C语言编程**:Nginx主要用C语言编写,...

Global site tag (gtag.js) - Google Analytics