Nginx事件模块

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

typedef struct {
    //添加事件方法到操作系统提供的事件驱动机制(如epoll、kqueue等
    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);

    //启用1个事件
    ngx_int_t (*enable)(ngx_event_t *ev,ngx_int_t event,ngx_uint_t flags);

    //禁用1个事件
    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_int_t (*process_changes)(ngx_cycle_t *cycle,ngx_uint_t nowait);

    //通过下面的函数来处理事件,它是处理、分发事件的核心
    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 {
    //事件相关的对象
    void                *data;

    //1:事件可写
    unsigned            write:1;

    //1:事件可以建立新的连接
    unsigned            accept:1;

    //区分当前事件是否过期
    unsigned            instance:1;

    //1:事件是活跃的;0:不活跃
    unsigned            active:1;

    //1:禁用事件
    unsigned            disabled:1;

    //仅对kqueue、eventport有意义,对epoll无意义
    unsigned            oneshot:1;

    //用于异步AIO事件的处理
    unsigned            complete:1;

    //1:当前处理的字符流已经结束
    unsigned            eof:1;

    //1:表示事件在处理过程中出现错误
    unsigned            error:1;

    //1:事件超时
    unsigned            timeout:1;

    //1:事件处于定时器计时中
    unsigned            time_set:1;

    //delayed=1需要延迟处理这个事件,它仅用于限速功能
    unsigned            delayed:1;

    //该标志位目前没有使用
    unsigned            read_discarded:1;

    //未被使用
    unsigned            unexpected_eof:1;

    //1:延迟建立TCP连接,即三次握手后等待数据包到来才建立连接
    unsigned            deferred:accept:1;

    //1:等待字符流结束,只与kqueue和aio事件驱动有关
    unsigned            pending_eof:1;

#if !(NGX_THREADS)
    //1:在处理post事件时,当前事件已经准备就绪
    unsigned            post_ready:1;
#endif

    //1:epoll事件驱动机制下尽可能多地建立TCP连接
    unsigned            avaliable:1;

    //这个事件发生时的处理函数,每个事件消费者模块都会重新实现!
    ngx_event_handler_pt handler;

#if (NGX_HAVE_AIO)

#if (NGX_HAVE_IOCP)
    //Windows下的一种事件驱动模型
    ngx_event_ovlp_t    ovlp;
#else
    //Linux aio机制中定义的结构体
    ngx_aiocb_t         aiocb;
#endif

#endif

    //epoll中不用
    ngx_uint_t      index;

    //用于记录error_log日志
    ngx_log_t       *log;

    //定时器节点,用于定时器红黑树
    ngx_rbtree_node_t   timer;

    //1:当前事件已关闭,epoll中未使用
    unsigned            closed:1;

    //无意义
    unsigned            channel:1;

    //无意义
    unsigned            resolver:1;

    //post事件会构成一个双向链表
    ngx_event_t     *next;
    ngx_event_t     **prev;
};

//其中的最核心的是handler成员,它由每一个事件消费者模块实现,以此决定如何处理这个事件。
typedef void (*ngx_event_handler_pt)(ngx_event_t *ev);

添加事件到epoll的简单封装函数

添加读事件

ngx_int_t ngx_handle_read_event(ngx_event_t *rev,ngx_uint_t falgs);

ngx_handler_read_event函数会将读事件添加到事件驱动模块中。
rev:要操作的事件;
flags:对于不同的事件驱动模块,取值范围不同。

添加写事件

ngx_int_t ngx_handle_write_event(ngx_event_t *wev,size_t lowat);

ngx_handle_write_event函数会将写事件添加到事件驱动模块中。
wev:是要操作的事件;
lowat:只有当连接对应的套接字缓冲区中有lowat大小的可用空间时,时间收集器才能处理这个事件(lowat=0时不考虑可写缓冲区大小)。

Nginx连接

被动连接ngx_connection_t

这个连接表示是客户端主动发起、Nginx服务器被动接受的TCP连接。

typedef struct ngx_connection_s ngx_connection_t;

struct ngx_connection_s {
     /* 连接未使用时,data充当连接池中的空闲链表中的next指针。当连接被使用时,data的意义由使用它的Nginx
     模块而定,如在HTTP模块中,data指向ngx_http_request_t请求 */
    void               *data;

    //连接对应的读事件
    ngx_event_t        *read;

    //写事件
    ngx_event_t        *write;

     //套接字句柄
    ngx_socket_t        fd;

     //接收网络字符流的函数
    ngx_recv_pt         recv;
    //发送网络字符流的函数
    ngx_send_pt         send;
    //以ngx_chain_t链表为参数来接收网路字符流的函数
    ngx_recv_chain_pt   recv_chain;
    //以ngx_chain_t链表为参数来发送网路字符流的函数
    ngx_send_chain_pt   send_chain;

     //此连接由listening监听端口的事件建立
    ngx_listening_t    *listening;

     //这个连接上已经发送出去的字节数
    off_t               sent;

     //记录日志
    ngx_log_t          *log;

     /* 内存池。一般在accept一个新连接时,会创建一个内存池,连接结束时会销毁它。内存池大小由listening
     中的pool_size决定 */
    ngx_pool_t         *pool;

     //连接客户端的sockaddr结构体
    struct sockaddr    *sockaddr;
    //sockaddr结构体的长度
    socklen_t           socklen;
    连接客户端字符串形IP地址
    ngx_str_t           addr_text;

    ngx_str_t           proxy_protocol_addr;

#if (NGX_SSL)
    ngx_ssl_connection_t  *ssl;
#endif

     //本机监听端口对应的sockaddr结构体,就是listening中的sockaddr
    struct sockaddr    *local_sockaddr;
    socklen_t           local_socklen;

     //用于接收、缓存客户端发来的字符串流。
    ngx_buf_t          *buffer;

     /* 用来将当前连接以双向链表元素的形式添加到ngx_cycle_t核心结构体reusable_connections_queue
     双向链表中,表示可重用的连接 */
    ngx_queue_t         queue;

     //连接使用次数
    ngx_atomic_uint_t   number;

     //处理的请求次数
    ngx_uint_t          requests;

     //缓存中的业务类型
    unsigned            buffered:8;

     //日志级别
    unsigned            log_error:3;     /* ngx_connection_log_error_e */

     //1:不期待字符流结束
    unsigned            unexpected_eof:1;
    //1:已经超时
    unsigned            timedout:1;
    //1:连接处理过程中出现错误
    unsigned            error:1;
    //1:连接已经销毁(只对应的TCP连接,而非此结构体)
    unsigned            destroyed:1;

     //1:连接处于空闲状态
    unsigned            idle:1;
    //1:连接可重用
    unsigned            reusable:1;
    //1:连接关闭
    unsigned            close:1;

     //1:正在将文件中的数据发往连接的另一端
    unsigned            sendfile:1;
    //1:只有在连接套接字对应的发送缓冲区必须满足最低设置的大小阈值时,事件驱动模块才会分发该事件
    unsigned            sndlowat:1;

    //表示如何使用TCPnodelay特性
    unsigned            tcp_nodelay:2;   /* ngx_connection_tcp_nodelay_e */
    //表示如何使用TCPnopush特性
    unsigned            tcp_nopush:2;    /* ngx_connection_tcp_nopush_e */

    unsigned            need_last_buf:1;

#if (NGX_HAVE_IOCP)
    unsigned            accept_context_updated:1;
#endif

#if (NGX_HAVE_AIO_SENDFILE)
    unsigned            aio_sendfile:1;
    unsigned            busy_count:2;
    ngx_buf_t          *busy_sendfile;
#endif

#if (NGX_THREADS)
    ngx_atomic_t        lock;
#endif
};

主动连接ngx_peer_connection_t

typedef struct ngx_peer_connection_s ngx_peer_connection_t;

//当使用长连接与上游服务器通信时,可通过该函数由连接池中获取一个新连接
typedef ngx_int_t (*ngx_event_get_peer_pt)(ngx_peer_connection_t *pc,void *data);

//当使用长连接与上游服务器通信时,通过该函数释放使用完毕的连接
typedef void (*ngx_event_free_peer_pt)(ngx_peer_connection_t *pc,void *data,ngx_uint_t state);

struct ngx_peer_connection_s {
     //基于ngx_connection_t结构体
    ngx_connection_t                *connection;

     //远端服务器的socket地址
    struct sockaddr                 *sockaddr;
    //sockaddr地址的长度
    socklen_t                        socklen;
    //远端服务器的名称
    ngx_str_t                       *name;

     //表示连接一个服务器失败收可以尝试重连的次数
    ngx_uint_t                       tries;

     //获取连接的方法
    ngx_event_get_peer_pt            get;
    //释放连接的方法
    ngx_event_free_peer_pt           free;
    //仅用于上面的两个函数传递参数用
    void                            *data;

#if (NGX_SSL)
    ngx_event_set_peer_session_pt    set_session;
    ngx_event_save_peer_session_pt   save_session;
#endif

#if (NGX_THREADS)
    ngx_atomic_t                    *lock;
#endif

     //本机地址信息
    ngx_addr_t                      *local;

     //套接字的接收缓冲区大小
    int                              rcvbuf;

     //记录日志
    ngx_log_t                       *log;

     //1:表示connection连接已经缓存
    unsigned                         cached:1;

                                     /* ngx_connection_log_error_e */
    unsigned                         log_error:2;
};

ngx_connection_t连接池

连接池的使用

  • 连接池是ngx_connection_t的数组,这个数组在Nginx启动阶段就已经分配好,之后只需要从中获取即可。
  • ngx_cycle_t中有connections和free_connections这个两个成员。connection指向整个连接池数组的首部,free_connections指向第一个ngx_connection_t空闲链表。
  • 读事件、写事件、连接池是3个长度相同的数组组成的,相对应的事件和连接池在各自数组中的下标是一样的,所以可以直接找到。

连接池的使用

函数名 参数含义 执行意义
ngx_connection_t *ngx_get_connection(ngx_socket_t s,ngx_log_t *log) s是连接的套接字句柄 从连接池获取一个ngx_connection_t结构体,同时获取相应的读/写事件
void ngx_free_connection(ngx_connection_t *c) c是需要回收的连接 将这个连接回收到连接池中

ngx_events_module核心模块

      它定义了一类新模块:事件模块。
      定义一个Nginx模块就是实现ngx_module_t结构体

实现ngx_events_module配置项

static ngx_command_t ngx_events_commands[]={
    {   ngx_string("events"),
        NGX_MAIN_CONF|NGX_CONF_BLOCK|_NGX_CONF_NOARGS,
        ngx_events_block,
        0,
        0
        NULL},

        ngx_null_command
};

实现核心模块共同接口ngx_core_module_t

static ngx_core_module_t ngx_events_module_ctx = {
    ngx_string("events"),
    NULL,                   //create_conf
    NULL                    //init_conf
};

      由于ngx_events_module模块并不解析配置项的参数,只是在出现events配置项后会调用各事件模块去解析events{}内的配置项,所以不需要实现create_conf和init_conf函数。

ngx_events_module模块的定义

ngx_module_t ngx_events_module = {
    NGX_MODULE_V1,
    &ngx_events_module_ctx,         //module contxt
    ngx_events_commands,            //module directives
    NGX_CORE_MODULE,                    //module type
    NULL,                               //init master
    NULL,                               //init module
    NULL,                               //init process
    NULL,                               //init thread
    NULL,                               //init exit
    NULL,                               //init process
    NULL,                               //init master
    NGX_MODULE_V1_PADDING
};

      每个事件模块都必须遵循ngx_event_modue_t接口。在这个接口中,允许每个事件模块更具自己的喜好建立自己的配置项结构体,ngx_event_module_t中的create_conf方法是就是用来创建这个结构体的。
      这里可以来明确一下ngx_cycle_t中用来存放所有配置项的成员conf_ctx:



      从中可以看出void ***conf_ctx的四个意义:conf_ctx指向一个数组,这个数组中的每个元素都是指向由ngx_moudles数组中规定模块的配置项组成的数组指针,这些指针都指向另外的数组,这些数组中的元素都是指向配置项结构体的指针。
      那么,从conf_ctx获取当前事件模块需配置项结构指针就是在数组中确定下标:

#define ngx_event_get_conf(conf_ctx,module)    \
    (*(ngx_get_conf(conf_ctx,ngx_events_module))) module.ctx_index;

#define ngx_get_conf(conf_ctx,module)    conf_cts[module.index]

      因此,调用时,传入ngx_cycle_t中的conf_ctx和自己的模块名即可。从上面的两个宏中也可以看出,ngx_modules_s中的index成员表示所有模块在ngx_modules数组中的序号(第一个数组中的下标);ctx_index表明了模块在同类型模块中的顺序(第二个数组中的下标)。
      从上看到下,其实ngx_events_module根本没做什么,最主要的是它的ngx_events_block函数。它做了一下几点:
1. 初始化所有事件模块的ctx_index成员;
2. 申请事件模块的整个数组(事件类型的同类型所有模块组成的数组);
3. 依次调用所有事件模块的create_conf函数,将产生的结构体指针保存在上面的数组中;
4. 针对所有事件类型的模块解析配置项,由每个事件模块定义的ngx_command_t决定了配置项解析方法。
5. 解析完配置项后,依次调用所有事件模块通用接口ngx_event_module_t中的init_conf方法。

ngx_event_core_module事件模块

      该模块在ngx_modules数组中的顺序应该比所有事件模块都靠后,相反地,这样能让执行configure脚本时更为优先的执行它。

ngx_event_core_module感兴趣的配置项

static ngx_command_t  ngx_event_core_commands[] = {

       //连接池的大小,就是每个worker进城中支持的TCP最大连接数
    { ngx_string("worker_connections"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_connections,
      0,   
      0,   
      NULL },

        //连接池大小,与上述的重复,后续版本有取消
    { ngx_string("connections"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_connections,
      0,   
      0,   
      NULL },

        //确定选择哪一个事件模块作为事件驱动机制
    { ngx_string("use"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_use,
      0,
      0,
      NULL },

        //尽可能多地接收连接
    { ngx_string("multi_accept"),
      NGX_EVENT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_event_conf_t, multi_accept),
      NULL },

        //是否使用accept_mutex负载均衡锁
    { ngx_string("accept_mutex"),
      NGX_EVENT_CONF|NGX_CONF_FLAG,
      ngx_conf_set_flag_slot,
      0,
      offsetof(ngx_event_conf_t, accept_mutex),
      NULL },

        //开启负载均衡锁后,延迟accept_mutex_delay毫秒后视图重新连接事件
    { ngx_string("accept_mutex_delay"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_conf_set_msec_slot,
      0,
      offsetof(ngx_event_conf_t, accept_mutex_delay),
      NULL },

        //需要对来自指定IP的TCP连接打印debug级别的调试日志
    { ngx_string("debug_connection"),
      NGX_EVENT_CONF|NGX_CONF_TAKE1,
      ngx_event_debug_connection,
      0,
      0,
      NULL },

      ngx_null_command
};

存储配置项的结构体

typedef struct {
    //连接池大小
    ngx_uint_t    connections;
    //选用的事件在所有事件模块中的序号,就是ngx_module_t中的ctx_index
    ngx_uint_t    use;

    //1:在接收到一个新连接事件时,一次性建立尽可能多的连接
    ngx_flag_t    multi_accept;
    //1:启用负载均衡
    ngx_flag_t    accept_mutex;

    //在worker进程得不到负载均衡锁时的延迟建立连接时间
    ngx_msec_t    accept_mutex_delay;

    //所选用的事件模块名
    u_char       *name;

#if (NGX_DEBUG)
    /* 在--with-debug编译下,可仅针对某些客户端建立的连接输出调试级别的日志,而debug_connection数组
    用于保存这些客户端的地址信息 */
    ngx_array_t   debug_connection;
#endif
} ngx_event_conf_t;

ngx_event_core_module实现的ngx_event_module_t接口

static ngx_str_t event_core_name = ngx_string("event_core");

ngx_event_module_t  ngx_event_core_module_ctx = {
    &event_core_name,
    ngx_event_core_create_conf,            /* create configuration */
    ngx_event_core_init_conf,              /* init configuration */

    { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL }
};

      由于它并不真正负责TCP网络时间的驱动,所以不会实现ngx_event_actions_t中的10个方法。

ngx_event_core_module模块的定义

ngx_module_t  ngx_event_core_module = {
    NGX_MODULE_V1,
    &ngx_event_core_module_ctx,            /* module context */
    ngx_event_core_commands,               /* module directives */
    NGX_EVENT_MODULE,                      /* module type */
    NULL,                                  /* init master */
    ngx_event_module_init,                 /* init module */
    ngx_event_process_init,                /* init process */
    NULL,                                  /* init thread */
    NULL,                                  /* exit thread */
    NULL,                                  /* exit process */
    NULL,                                  /* exit master */
    NGX_MODULE_V1_PADDING
};
发布了88 篇原创文章 · 获赞 9 · 访问量 10万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章