Nginx源码分析 - Event事件篇 - Event模块和配置的初始化

Event的模块和配置的初始化,必须得结合我们讲过的两篇文章:《Nginx源码分析 - 主流程篇 - 模块的初始化》     和  《Nginx源码分析 - 主流程篇 - 解析配置文件》   

前面我们讲解了模块的初始化以及核心模块的配置文件的解析。而Event的配置解析会比核心模块解析会复杂一些,但是原理是一样的。

Event模块的数据结构

event事件模块的配置:

[cpp]  view plain  copy
 print ?
  1. events {    
  2.         use epoll;    
  3.         worker_connections  1024;    
  4. }    

event事件模块,配置分为两层: ngx_events_module 事件模块 和 ngx_event_core_module 事件核心模块

ngx_events_module:模块类型NGX_CORE_MODULE,所以此模块在最外层核心模块解析“events”命令的时候会回调ngx_events_block函数。

ngx_event_core_module:模块类型NGX_EVENT_MODULE,所以此模块在ngx_events_block函数被回调后,才能解析配置信息

最外层的events模块,类型NGX_CORE_MODULE,属于核心模块,核心模块在最开始配置文件初始化的时候,就会调用指令的命令集。所以在核心模块启动的时候就会调用events的模块配置解析指令函数:ngx_events_block

ngx_events_module 事件模块

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * event模块命令集 
  3.  * 回调函数:ngx_events_block 
  4.  * 用于解析 event{} 块中的配置参数 
  5.  */  
  6. static ngx_command_t ngx_events_commands[] = {  
  7.   
  8. { ngx_string("events"), NGX_MAIN_CONF | NGX_CONF_BLOCK | NGX_CONF_NOARGS,  
  9.         ngx_events_block, 0, 0, NULL },  
  10.   
  11. ngx_null_command };  
  12.   
  13. /** 
  14.  * event模块上下文 
  15.  */  
  16. static ngx_core_module_t ngx_events_module_ctx = { ngx_string("events"), NULL,  
  17.         ngx_event_init_conf };  
  18.   
  19. /** 
  20.  * event模块 
  21.  * 模块类型:NGX_CORE_MODULE 
  22.  * 模块类型为核心模块,所以在ngx_init_cycle就会初始化conf 
  23.  */  
  24. ngx_module_t ngx_events_module = { NGX_MODULE_V1, &ngx_events_module_ctx, /* module context */  
  25. ngx_events_commands, /* module directives */  
  26. NGX_CORE_MODULE, /* module type */  
  27. NULL, /* init master */  
  28. NULL, /* init module */  
  29. NULL, /* init process */  
  30. NULL, /* init thread */  
  31. NULL, /* exit thread */  
  32. NULL, /* exit process */  
  33. NULL, /* exit master */  
  34. NGX_MODULE_V1_PADDING };  

ngx_event_core_module 事件核心模块

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * event核心模块名称 
  3.  */  
  4. static ngx_str_t event_core_name = ngx_string("event_core");  
  5.   
  6. /** 
  7.  * 定义Event核心模块的命令参数 
  8.  * 主要: 
  9.  * worker_connections 工作线程最大连接数 
  10.  * use 使用什么模型,例如epoll 
  11.  * multi_accept 
  12.  * accept_mutex_delay 
  13.  * debug_connection 
  14.  */  
  15. static ngx_command_t ngx_event_core_commands[] = {  
  16.   
  17. { ngx_string("worker_connections"), NGX_EVENT_CONF | NGX_CONF_TAKE1,  
  18.         ngx_event_connections, 0, 0, NULL },  
  19.   
  20.         { ngx_string("use"), NGX_EVENT_CONF | NGX_CONF_TAKE1, ngx_event_use, 0,  
  21.                 0, NULL },  
  22.   
  23.         { ngx_string("multi_accept"), NGX_EVENT_CONF | NGX_CONF_FLAG,  
  24.                 ngx_conf_set_flag_slot, 0, offsetof(ngx_event_conf_t,  
  25.                         multi_accept), NULL },  
  26.   
  27.         { ngx_string("accept_mutex"), NGX_EVENT_CONF | NGX_CONF_FLAG,  
  28.                 ngx_conf_set_flag_slot, 0, offsetof(ngx_event_conf_t,  
  29.                         accept_mutex), NULL },  
  30.   
  31.         { ngx_string("accept_mutex_delay"), NGX_EVENT_CONF | NGX_CONF_TAKE1,  
  32.                 ngx_conf_set_msec_slot, 0, offsetof(ngx_event_conf_t,  
  33.                         accept_mutex_delay), NULL },  
  34.   
  35.         { ngx_string("debug_connection"), NGX_EVENT_CONF | NGX_CONF_TAKE1,  
  36.                 ngx_event_debug_connection, 0, 0, NULL },  
  37.   
  38.         ngx_null_command };  
  39.   
  40. /** 
  41.  * Event核心模块上下文 
  42.  * ngx_event_core_create_conf:创建配置文件 
  43.  * ngx_event_core_init_conf:初始化配置文件 
  44.  */  
  45. ngx_event_module_t ngx_event_core_module_ctx = { &event_core_name,  
  46.         ngx_event_core_create_conf, /* create configuration */  
  47.         ngx_event_core_init_conf, /* init configuration */  
  48.   
  49.         { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL } };  
  50.   
  51. /** 
  52.  * Event核心模块 
  53.  * ngx_event_module_init:模块初始化 
  54.  * ngx_event_process_init:进程初始化 
  55.  * 类型:NGX_EVENT_MODULE 
  56.  */  
  57. ngx_module_t ngx_event_core_module = { NGX_MODULE_V1,  
  58.         &ngx_event_core_module_ctx, /* module context */  
  59.         ngx_event_core_commands, /* module directives */  
  60.         NGX_EVENT_MODULE, /* module type */  
  61.         NULL, /* init master */  
  62.         ngx_event_module_init, /* init module */  
  63.         ngx_event_process_init, /* init process */  
  64.         NULL, /* init thread */  
  65.         NULL, /* exit thread */  
  66.         NULL, /* exit process */  
  67.         NULL, /* exit master */  
  68.         NGX_MODULE_V1_PADDING };  

ngx_event_conf_t 事件conf结构

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * event事件模块配置的结构对象 
  3.  */  
  4. typedef struct {  
  5.     ngx_uint_t    connections;  
  6.     ngx_uint_t    use;  
  7.   
  8.     ngx_flag_t    multi_accept;  
  9.     ngx_flag_t    accept_mutex;  
  10.   
  11.     ngx_msec_t    accept_mutex_delay;  
  12.   
  13.     u_char       *name;  
  14.   
  15. #if (NGX_DEBUG)  
  16.     ngx_array_t   debug_connection;  
  17. #endif  
  18. } ngx_event_conf_t;  

Event模块结构图


Event模块的初始化

1. ngx_event_module_init 模块初始化函数

ngx_event_module_init方法为事件核心模块的初始化函数。

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * event事件核心模块初始化函数 
  3.  */  
  4. static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) {  
  5.     void ***cf;  
  6.     u_char *shared;  
  7.     size_t size, cl;  
  8.     ngx_shm_t shm;  
  9.     ngx_time_t *tp;  
  10.     ngx_core_conf_t *ccf;  
  11.     ngx_event_conf_t *ecf;  
  12.   
  13.     /* 获取配置信息 */  
  14.     cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);  
  15.     ecf = (*cf)[ngx_event_core_module.ctx_index];  
  16.   
  17.     if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {  
  18.         ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,  
  19.                 "using the \"%s\" event method", ecf->name);  
  20.     }  
  21.   
  22.     ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);  
  23.   
  24.     ngx_timer_resolution = ccf->timer_resolution;  
  25.   
  26. #if !(NGX_WIN32)  
  27.     {  
  28.         ngx_int_t limit;  
  29.         struct rlimit rlmt;  
  30.   
  31.         if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {  
  32.             ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,  
  33.                     "getrlimit(RLIMIT_NOFILE) failed, ignored");  
  34.   
  35.         } else {  
  36.             if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur  
  37.                     && (ccf->rlimit_nofile == NGX_CONF_UNSET  
  38.                             || ecf->connections  
  39.                                     > (ngx_uint_t) ccf->rlimit_nofile)) {  
  40.                 limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?  
  41.                         (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;  
  42.   
  43.                 ngx_log_error(NGX_LOG_WARN, cycle->log, 0,  
  44.                         "%ui worker_connections exceed "  
  45.                                 "open file resource limit: %i",  
  46.                         ecf->connections, limit);  
  47.             }  
  48.         }  
  49.     }  
  50. #endif /* !(NGX_WIN32) */  
  51.   
  52.     if (ccf->master == 0) {  
  53.         return NGX_OK;  
  54.     }  
  55.   
  56.     if (ngx_accept_mutex_ptr) {  
  57.         return NGX_OK;  
  58.     }  
  59.   
  60.     /* cl should be equal to or greater than cache line size */  
  61.   
  62.     cl = 128;  
  63.   
  64.     size = cl /* ngx_accept_mutex */  
  65.     + cl /* ngx_connection_counter */  
  66.     + cl; /* ngx_temp_number */  
  67.   
  68. #if (NGX_STAT_STUB)  
  69.   
  70.     size += cl /* ngx_stat_accepted */  
  71.     + cl /* ngx_stat_handled */  
  72.     + cl /* ngx_stat_requests */  
  73.     + cl /* ngx_stat_active */  
  74.     + cl /* ngx_stat_reading */  
  75.     + cl /* ngx_stat_writing */  
  76.     + cl; /* ngx_stat_waiting */  
  77.   
  78. #endif  
  79.   
  80.     shm.size = size;  
  81.     shm.name.len = sizeof("nginx_shared_zone") - 1;  
  82.     shm.name.data = (u_char *) "nginx_shared_zone";  
  83.     shm.log = cycle->log;  
  84.   
  85.     if (ngx_shm_alloc(&shm) != NGX_OK) {  
  86.         return NGX_ERROR;  
  87.     }  
  88.   
  89.     shared = shm.addr;  
  90.   
  91.     ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;  
  92.     ngx_accept_mutex.spin = (ngx_uint_t) -1;  
  93.   
  94.     if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared,  
  95.             cycle->lock_file.data) != NGX_OK) {  
  96.         return NGX_ERROR;  
  97.     }  
  98.   
  99.     ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);  
  100.   
  101.     (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);  
  102.   
  103.     ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,  
  104.             "counter: %p, %uA",  
  105.             ngx_connection_counter, *ngx_connection_counter);  
  106.   
  107.     ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);  
  108.   
  109.     tp = ngx_timeofday();  
  110.   
  111.     ngx_random_number = (tp->msec << 16) + ngx_pid;  
  112.   
  113. #if (NGX_STAT_STUB)  
  114.   
  115.     ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);  
  116.     ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);  
  117.     ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);  
  118.     ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);  
  119.     ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);  
  120.     ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);  
  121.     ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);  
  122.   
  123. #endif  
  124.   
  125.     return NGX_OK;  
  126. }  

Event模块配置的初始化

1. ngx_events_module 模块配置初始化

ngx_init_cycle方法中的模块初始化。ngx_events_module类型为NGX_CORE_MODULE,所以在ngx_init_cycle中就会进行核心模块的初始化。

但是ngx_events_module中的create_conf方法为NULL,所以不会调用创建配置的方法。

[cpp]  view plain  copy
 print ?
  1. /* 
  2.  * 核心模块的配置文件创建 
  3.  * 配置创建调用nginx.c 中的 ngx_core_module_create_conf 
  4.  * */  
  5. for (i = 0; cycle->modules[i]; i++) {  
  6.     if (cycle->modules[i]->type != NGX_CORE_MODULE) {  
  7.         continue;  
  8.     }  
  9.   
  10.     module = cycle->modules[i]->ctx;  
  11.   
  12.     if (module->create_conf) {  
  13.         rv = module->create_conf(cycle); //模块回调函数,创建模块的配置信息  
  14.         if (rv == NULL) {  
  15.             ngx_destroy_pool(pool);  
  16.             return NULL;  
  17.         }  
  18.         cycle->conf_ctx[cycle->modules[i]->index] = rv; //配置文件复制  
  19.     }  
  20. }  

2. ngx_conf_parse 解析顶层“event”的配置

ngx_init_cycle方法中会调用ngx_conf_parse方法,并且解析的/usr/local/nginx/conf/nginx.conf配置文件。此次调用只解析最顶层的配置信息“events”,而不会解析{}块中的内容

[cpp]  view plain  copy
 print ?
  1. /* 解析命令行中的配置参数;例如:nginx -t -c /usr/local/nginx/conf/nginx.conf */  
  2. if (ngx_conf_param(&conf) != NGX_CONF_OK) {  
  3.     environ = senv;  
  4.     ngx_destroy_cycle_pools(&conf);  
  5.     return NULL;  
  6. }  
  7.   
  8. /* 解析配置文件/usr/local/nginx/conf/nginx.conf 信息 */  
  9. if (ngx_conf_parse(&conf, &cycle->conf_file) != NGX_CONF_OK) {  
  10.     environ = senv;  
  11.     ngx_destroy_cycle_pools(&conf);  
  12.     return NULL;  
  13. }  

3. ngx_events_block 解析events块block中的内容

ngx_events_block方法为ngx_events_commands命令集的回调函数。在最顶层解析nginx.conf文件的时候,会进行核心模块的命令集遍历。(参考: Nginx源码分析 - 主流程篇 - 解析配置文件    中的ngx_conf_handler)   会遍历模块命令集的cmd->set方法。

ngx_events_block中主要创建ngx_event_core_module事件的核心模块以及配置信息。

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * 模块解析 
  3.  * 事件模块配置如下: 
  4.  * events { 
  5.     worker_connections  1024; 
  6.     } 
  7.          光使用核心配置的方式,只能解析到 events 这一层。 
  8.          如果需要继续往{}中的内容解析,就得重新调用ngx_conf_parse进行解析 
  9.  */  
  10. static char *  
  11. ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {  
  12.     char *rv;  
  13.     void ***ctx;  
  14.     ngx_uint_t i;  
  15.     ngx_conf_t pcf;  
  16.     ngx_event_module_t *m;  
  17.   
  18.     if (*(void **) conf) {  
  19.         return "is duplicate";  
  20.     }  
  21.   
  22.     /* count the number of the event modules and set up their indices */  
  23.   
  24.     ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);  
  25.   
  26.     /* 分配内存空间 */  
  27.     ctx = ngx_pcalloc(cf->pool, sizeof(void *));  
  28.     if (ctx == NULL) {  
  29.         return NGX_CONF_ERROR ;  
  30.     }  
  31.   
  32.     *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));  
  33.     if (*ctx == NULL) {  
  34.         return NGX_CONF_ERROR ;  
  35.     }  
  36.   
  37.     *(void **) conf = ctx;  
  38.   
  39.     /* 模块初始化,如果是NGX_EVENT_MODULE,则调用模块的create_conf方法 */  
  40.     for (i = 0; cf->cycle->modules[i]; i++) {  
  41.         if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {  
  42.             continue;  
  43.         }  
  44.   
  45.         m = cf->cycle->modules[i]->ctx;  
  46.   
  47.         if (m->create_conf) {  
  48.             (*ctx)[cf->cycle->modules[i]->ctx_index] = m->create_conf(  
  49.                     cf->cycle);  
  50.             if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {  
  51.                 return NGX_CONF_ERROR ;  
  52.             }  
  53.         }  
  54.     }  
  55.   
  56.     /* event*/  
  57.     pcf = *cf;  
  58.     cf->ctx = ctx;  
  59.     cf->module_type = NGX_EVENT_MODULE;  
  60.     cf->cmd_type = NGX_EVENT_CONF;  
  61.   
  62.     /* 调用配置解析,这次解析的是 块中的内容,非文件内容 */  
  63.     rv = ngx_conf_parse(cf, NULL);  
  64.   
  65.     *cf = pcf;  
  66.   
  67.     if (rv != NGX_CONF_OK) {  
  68.         return rv;  
  69.     }  
  70.   
  71.     /* 初始化 模块的init_conf 方法*/  
  72.     for (i = 0; cf->cycle->modules[i]; i++) {  
  73.         if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {  
  74.             continue;  
  75.         }  
  76.   
  77.         m = cf->cycle->modules[i]->ctx;  
  78.   
  79.         if (m->init_conf) {  
  80.             rv = m->init_conf(cf->cycle,  
  81.                     (*ctx)[cf->cycle->modules[i]->ctx_index]);  
  82.             if (rv != NGX_CONF_OK) {  
  83.                 return rv;  
  84.             }  
  85.         }  
  86.     }  
  87.   
  88.     return NGX_CONF_OK;  
  89. }  

4. ngx_event_core_create_conf和ngx_event_core_init_conf

ngx_event_core_create_conf:主要是创建event事件核心模块

ngx_event_core_init_conf:初始化event事件核心模块

[cpp]  view plain  copy
 print ?
  1. /** 
  2.  * 创建Event的核心配置文件 
  3.  */  
  4. static void *  
  5. ngx_event_core_create_conf(ngx_cycle_t *cycle) {  
  6.     ngx_event_conf_t *ecf;  
  7.   
  8.     /* 分配配置文件内容 */  
  9.     ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));  
  10.     if (ecf == NULL) {  
  11.         return NULL;  
  12.     }  
  13.   
  14.     /* 设置默认值 */  
  15.     ecf->connections = NGX_CONF_UNSET_UINT;  
  16.     ecf->use = NGX_CONF_UNSET_UINT;  
  17.     ecf->multi_accept = NGX_CONF_UNSET;  
  18.     ecf->accept_mutex = NGX_CONF_UNSET;  
  19.     ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;  
  20.     ecf->name = (void *) NGX_CONF_UNSET;  
  21.   
  22. #if (NGX_DEBUG)  
  23.   
  24.     if (ngx_array_init(&ecf->debug_connection, cycle->pool, 4,  
  25.                     sizeof(ngx_cidr_t)) == NGX_ERROR)  
  26.     {  
  27.         return NULL;  
  28.     }  
  29.   
  30. #endif  
  31.   
  32.     return ecf;  
  33. }  
  34.   
  35. /** 
  36.  * 初始化Event的核心配置文件 
  37.  */  
  38. static char *  
  39. ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {  
  40.     ngx_event_conf_t *ecf = conf;  
  41.   
  42. #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)  
  43.     int fd;  
  44. #endif  
  45.     ngx_int_t i;  
  46.     ngx_module_t *module;  
  47.     ngx_event_module_t *event_module;  
  48.   
  49.     module = NULL;  
  50.   
  51. #if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)  
  52.   
  53.     fd = epoll_create(100);  
  54.   
  55.     if (fd != -1) {  
  56.         (void) close(fd);  
  57.         module = &ngx_epoll_module;  
  58.   
  59.     } else if (ngx_errno != NGX_ENOSYS) {  
  60.         module = &ngx_epoll_module;  
  61.     }  
  62.   
  63. #endif  
  64.   
  65. #if (NGX_HAVE_DEVPOLL) && !(NGX_TEST_BUILD_DEVPOLL)  
  66.   
  67.     module = &ngx_devpoll_module;  
  68.   
  69. #endif  
  70.   
  71. #if (NGX_HAVE_KQUEUE)  
  72.   
  73.     module = &ngx_kqueue_module;  
  74.   
  75. #endif  
  76.   
  77. #if (NGX_HAVE_SELECT)  
  78.   
  79.     if (module == NULL) {  
  80.         module = &ngx_select_module;  
  81.     }  
  82.   
  83. #endif  
  84.   
  85.     /** 
  86.      * 查询使用的事件模型:epoll、kqueue等 
  87.      * 因为在模块初始化的时候,epoll\kqueue等event的模型模块都会被初始化 
  88.      * 但是每个服务器只能选择一种相应的事件模型,所以选择一个适合自己的模块 
  89.      */  
  90.     if (module == NULL) {  
  91.         for (i = 0; cycle->modules[i]; i++) {  
  92.   
  93.             if (cycle->modules[i]->type != NGX_EVENT_MODULE) {  
  94.                 continue;  
  95.             }  
  96.   
  97.             event_module = cycle->modules[i]->ctx;  
  98.   
  99.             if (ngx_strcmp(event_module->name->data, event_core_name.data)  
  100.                     == 0) {  
  101.                 continue;  
  102.             }  
  103.   
  104.             module = cycle->modules[i];  
  105.             break;  
  106.         }  
  107.     }  
  108.   
  109.     if (module == NULL) {  
  110.         ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");  
  111.         return NGX_CONF_ERROR ;  
  112.     }  
  113.   
  114.     ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);  
  115.     cycle->connection_n = ecf->connections;  
  116.   
  117.     /** 
  118.      * 存储使用的事件模型模块索引 例如:epoll、kqueue 
  119.      * nginx.conf中存储的是:use epoll; 
  120.      * 这里会找到cycle->modules的具体模块的索引值,存储最终的索引值 
  121.      */  
  122.     ngx_conf_init_uint_value(ecf->use, module->ctx_index);  
  123.   
  124.     event_module = module->ctx;  
  125.     ngx_conf_init_ptr_value(ecf->name, event_module->name->data);  
  126.   
  127.     ngx_conf_init_value(ecf->multi_accept, 0);  
  128.     ngx_conf_init_value(ecf->accept_mutex, 1);  
  129.     ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);  
  130.   
  131.     return NGX_CONF_OK;  
  132. }  
5. 获取event配置

获取event的配置,先获取ngx_events_module配置,然后再到ngx_events_module模块上找到ngx_event_core_module 事件核心模块的配置。

[cpp]  view plain  copy
 print ?
  1. #define ngx_event_get_conf(conf_ctx, module)                                  \  
  2.              (*(ngx_get_conf(conf_ctx, ngx_events_module))) [module.ctx_index];  


转自:http://blog.csdn.net/initphp/article/details/52434261

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值