不同的操作系统相应不同的事件驱动机制。在Linux 2.6之后使用epoll机制。相应的事件驱动模块是ngx_epoll_module。Nginx的ngx_event_core_module模块依据操作系统确定使用哪一个事件驱动模块。

事件驱动模块在ngx_module_t的ctx通用接口是ngx_event_module_t。定义例如以下所看到的:


typedef struct {
    ngx_str_t              *name;   // 事件模块名字
    // 解析配置项之前调用,创建存储配置项參数的结构体
    void                 *(*create_conf)(ngx_cycle_t *cycle);
    // 解析完配置项后的回调函数
    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);
    // 每一个事件模块须要实现的10个抽象方法
    ngx_event_actions_t     actions;
} ngx_event_module_t;   // 事件模块通用接口




ngx_event_actions_t结构体定义例如以下:



// 每一个事件模块须要实现的10个方法
typedef struct
{
    // 将一个事件加入到事件驱动机制
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 将一个事件从事件驱动机制中删除
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 启动一个事件,眼下没有调用这种方法
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 禁用一个事件,眼下没有调用这种方法
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 将一个连接加入到事件驱动机制中。连接上的读、写事件即被加入到了事件驱动机制中
    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    // 从事件驱动机制中删除一个连接的读、写事件
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
    // 暂无用
    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    // 处理事件的方法,由处理、分发核心函数ngx_process_events_and_timers调用
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
    // 初始化事件驱动模块
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    // 事件驱动模块退出前调用的方法
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;






事件由ngx_event_t结构体定义:



typedef struct ngx_event_s       ngx_event_t;   // 代表事件的结构体
// 代表事件的结构体
struct ngx_event_s {
    ...
    ngx_event_handler_pt  handler;  // 核心,事件消费函数,定义怎样处理事件
    ...
};






在ngx_event_t中,我们最关心handler成员。它决定了它所属的事件发生时的处理方法。





事件不须要创建。由于在Nginx启动时初始化ngx_cycle_t的过程中就分配了全部读、写事件的空间:



struct ngx_cycle_s {
    ...
    ngx_connection_t         *connections;  // 保存当前进程中全部的连接
    ngx_event_t              *read_events;  // 保存全部的读事件
    ngx_event_t              *write_events; // 保存全部的写事件
    ...
};






这样,每一个连接就相应了一个写事件和一个读事件。将事件加入到epoll的方法例如以下:



  • ngx_handle_read_event:将读事件加入到事件驱动模块(epoll)中。
  • ngx_handle_write_event:将写事件加入到事件驱动模块(epoll)中。



当把事件加入到epoll等事件驱动机制中后,将会发生一连串的触发事件的反应:



  • 连接上出现可读/可写事件
  • epoll机制捕获到这一事件后返回
  • 连接所相应的读/写事件结构体中的handler函数被调用



连接的定义



在Nginx中定义了两种连接:



  • ngx_connection_t:被动连接。由client主动发起。
  • ngx_peer_connection_t:主动连接。用于主动和上游server进行通信。

这里仅仅关心被动连接,被动连接的定义例如以下所看到的:



// 表示一个连接的结构体
struct ngx_connection_s {
    void               *data;   // 连接未使用时,充当next指针连接池中下一个空暇的连接,当被使用时,是详细情况而定
    ngx_event_t        *read;   // 连接相应的读事件
    ngx_event_t        *write;  // 连接相应的写事件
    ngx_socket_t        fd;     // 套接字描写叙述符
    ngx_recv_pt         recv;       // 接收方法1
    ngx_send_pt         send;       // 发送方法1
    ngx_recv_chain_pt   recv_chain; // 接收方法2
    ngx_send_chain_pt   send_chain; // 发送方法2
    ngx_listening_t    *listening;  // 连接相应的监听对象
    off_t               sent;       // 已发出去的字节数
    ngx_log_t          *log;
    ngx_pool_t         *pool;       // 内存池
    struct sockaddr    *sockaddr;   // 保存网络地址
    socklen_t           socklen;    // 网络地址长度
    ngx_str_t           addr_text;  // clientIP地址
    ngx_str_t           proxy_protocol_addr;
    struct sockaddr    *local_sockaddr; // 本机所监听的网络地址
    socklen_t           local_socklen;  // 网络地址长度
    ngx_buf_t          *buffer; // 接收buffer
    ...
};






当Nginxserver接和client建立连接后,就会获得一个ngx_connection_t实体。和事件一样,连接不须要额外创建。它是从ngx_connection_t连接池中获得的,连接池在Nginx启动阶段就已经分配好了。由ngx_cycle_t结构体的connections成员和free_connections成员共同管理,例如以下图所看到的:




nginx wait连接中断 nginx主动断开连接_epoll




注意,上图左側的队列在物理存储上是连续存储的,也就是数组,而空暇链表是一条逻辑上的链表(通过data成员相连)用于管理空暇连接。数组的大小由配置文件里的配置项决定。上面说过,一个连接相应一个读事件和一个写事件,从上图能够看出,同样下标的连接和事件相互关联。



向连接池获取连接和归还连接到连接池的函数接口例如以下:


  • ngx_get_connection:获取连接。也就是得到一个ngx_connection_t结构体实体。
  • ngx_free_connection:归还连接。



參考:


《深入理解Nginx》 P287-P300.


转载于:


不同的操作系统相应不同的事件驱动机制。在Linux 2.6之后使用epoll机制。相应的事件驱动模块是ngx_epoll_module。Nginx的ngx_event_core_module模块依据操作系统确定使用哪一个事件驱动模块。

事件驱动模块在ngx_module_t的ctx通用接口是ngx_event_module_t。定义例如以下所看到的:


typedef struct {
    ngx_str_t              *name;   // 事件模块名字
    // 解析配置项之前调用,创建存储配置项參数的结构体
    void                 *(*create_conf)(ngx_cycle_t *cycle);
    // 解析完配置项后的回调函数
    char                 *(*init_conf)(ngx_cycle_t *cycle, void *conf);
    // 每一个事件模块须要实现的10个抽象方法
    ngx_event_actions_t     actions;
} ngx_event_module_t;   // 事件模块通用接口



ngx_event_actions_t结构体定义例如以下:


// 每一个事件模块须要实现的10个方法
typedef struct
{
    // 将一个事件加入到事件驱动机制
    ngx_int_t  (*add)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 将一个事件从事件驱动机制中删除
    ngx_int_t  (*del)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 启动一个事件,眼下没有调用这种方法
    ngx_int_t  (*enable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 禁用一个事件,眼下没有调用这种方法
    ngx_int_t  (*disable)(ngx_event_t *ev, ngx_int_t event, ngx_uint_t flags);
    // 将一个连接加入到事件驱动机制中。连接上的读、写事件即被加入到了事件驱动机制中
    ngx_int_t  (*add_conn)(ngx_connection_t *c);
    // 从事件驱动机制中删除一个连接的读、写事件
    ngx_int_t  (*del_conn)(ngx_connection_t *c, ngx_uint_t flags);
    // 暂无用
    ngx_int_t  (*process_changes)(ngx_cycle_t *cycle, ngx_uint_t nowait);
    // 处理事件的方法,由处理、分发核心函数ngx_process_events_and_timers调用
    ngx_int_t  (*process_events)(ngx_cycle_t *cycle, ngx_msec_t timer, ngx_uint_t flags);
    // 初始化事件驱动模块
    ngx_int_t  (*init)(ngx_cycle_t *cycle, ngx_msec_t timer);
    // 事件驱动模块退出前调用的方法
    void       (*done)(ngx_cycle_t *cycle);
} ngx_event_actions_t;




事件由ngx_event_t结构体定义:


typedef struct ngx_event_s       ngx_event_t;   // 代表事件的结构体
// 代表事件的结构体
struct ngx_event_s {
    ...
    ngx_event_handler_pt  handler;  // 核心,事件消费函数,定义怎样处理事件
    ...
};




在ngx_event_t中,我们最关心handler成员。它决定了它所属的事件发生时的处理方法。



事件不须要创建。由于在Nginx启动时初始化ngx_cycle_t的过程中就分配了全部读、写事件的空间:


struct ngx_cycle_s {
    ...
    ngx_connection_t         *connections;  // 保存当前进程中全部的连接
    ngx_event_t              *read_events;  // 保存全部的读事件
    ngx_event_t              *write_events; // 保存全部的写事件
    ...
};




这样,每一个连接就相应了一个写事件和一个读事件。将事件加入到epoll的方法例如以下:


  • ngx_handle_read_event:将读事件加入到事件驱动模块(epoll)中。
  • ngx_handle_write_event:将写事件加入到事件驱动模块(epoll)中。


当把事件加入到epoll等事件驱动机制中后,将会发生一连串的触发事件的反应:


  • 连接上出现可读/可写事件
  • epoll机制捕获到这一事件后返回
  • 连接所相应的读/写事件结构体中的handler函数被调用


连接的定义


在Nginx中定义了两种连接:


  • ngx_connection_t:被动连接。由client主动发起。
  • ngx_peer_connection_t:主动连接。用于主动和上游server进行通信。

这里仅仅关心被动连接,被动连接的定义例如以下所看到的:


// 表示一个连接的结构体
struct ngx_connection_s {
    void               *data;   // 连接未使用时,充当next指针连接池中下一个空暇的连接,当被使用时,是详细情况而定
    ngx_event_t        *read;   // 连接相应的读事件
    ngx_event_t        *write;  // 连接相应的写事件
    ngx_socket_t        fd;     // 套接字描写叙述符
    ngx_recv_pt         recv;       // 接收方法1
    ngx_send_pt         send;       // 发送方法1
    ngx_recv_chain_pt   recv_chain; // 接收方法2
    ngx_send_chain_pt   send_chain; // 发送方法2
    ngx_listening_t    *listening;  // 连接相应的监听对象
    off_t               sent;       // 已发出去的字节数
    ngx_log_t          *log;
    ngx_pool_t         *pool;       // 内存池
    struct sockaddr    *sockaddr;   // 保存网络地址
    socklen_t           socklen;    // 网络地址长度
    ngx_str_t           addr_text;  // clientIP地址
    ngx_str_t           proxy_protocol_addr;
    struct sockaddr    *local_sockaddr; // 本机所监听的网络地址
    socklen_t           local_socklen;  // 网络地址长度
    ngx_buf_t          *buffer; // 接收buffer
    ...
};




当Nginxserver接和client建立连接后,就会获得一个ngx_connection_t实体。和事件一样,连接不须要额外创建。它是从ngx_connection_t连接池中获得的,连接池在Nginx启动阶段就已经分配好了。由ngx_cycle_t结构体的connections成员和free_connections成员共同管理,例如以下图所看到的:


nginx wait连接中断 nginx主动断开连接_epoll




注意,上图左側的队列在物理存储上是连续存储的,也就是数组,而空暇链表是一条逻辑上的链表(通过data成员相连)用于管理空暇连接。数组的大小由配置文件里的配置项决定。上面说过,一个连接相应一个读事件和一个写事件,从上图能够看出,同样下标的连接和事件相互关联。



向连接池获取连接和归还连接到连接池的函数接口例如以下:


  • ngx_get_connection:获取连接。也就是得到一个ngx_connection_t结构体实体。
  • ngx_free_connection:归还连接。