Воскресенье, 9 Ноябрь 2008
nginx programming guide: добавляем свой блок в конфигурацию
Давно я не писал про потроха nginx, пора исправляться.
Сегодня речь пойдет о создании своих блоков с директивами в конфиге.
Для примера напишем модуль, который позволит написать в конфиге:
dc sample {
uri http://catap.ru/;
}
Начнем мы с анатомии модулей в nginx. Каждый модуль описывается структурой:
typedef struct ngx_module_s ngx_module_t;
struct ngx_module_s {
ngx_uint_t ctx_index;
ngx_uint_t index;
ngx_uint_t spare0;
ngx_uint_t spare1;
ngx_uint_t spare2;
ngx_uint_t spare3;
ngx_uint_t version;
void *ctx;
ngx_command_t *commands;
ngx_uint_t type;
ngx_int_t (*init_master)(ngx_log_t *log);
ngx_int_t (*init_module)(ngx_cycle_t *cycle);
ngx_int_t (*init_process)(ngx_cycle_t *cycle);
ngx_int_t (*init_thread)(ngx_cycle_t *cycle);
void (*exit_thread)(ngx_cycle_t *cycle);
void (*exit_process)(ngx_cycle_t *cycle);
void (*exit_master)(ngx_cycle_t *cycle);
uintptr_t spare_hook0;
uintptr_t spare_hook1;
uintptr_t spare_hook2;
uintptr_t spare_hook3;
uintptr_t spare_hook4;
uintptr_t spare_hook5;
uintptr_t spare_hook6;
uintptr_t spare_hook7;
};
Если детально расматривать поля ngx_module_t:
-
ctx_index
— индекс данных модуля в контексте модуля, реализующего данный тип -
index
— индекс модуля в массиве модулей -
version
— версия модуля -
ctx
— представляет из себя указатель на контекст конфигурации данного типа модуля, для NGX_CORE_MODULE это ngx_core_module_t, для NGX_HTTP_MODULE — ngx_http_module_t. -
commands
— указатель на перечень комманд (ngx_command_t) для конфигурации -
type
— тип (NGX_CORE_MODULE, NGX_HTTP_MODULE и т.п.) -
*init_master
— хук, который сработает во время инициализации master процесса -
*init_module
— хук, который сработает во время инициализации модуля -
*init_process
— хук, который сработает во время инициализации рабочего (worker) процесса -
*init_thread
— хук, который сработает во время инициализации треда -
*exit_thread
— хук, который сработает во время завершения треда -
*exit_process
— хук, который сработает во время завершения рабочего (worker) процесса -
*exit_master
— хук, который сработает во время завершения master процесса
spare0
…spare3
и spare_hook0
…spare_hook7
зарезервированы на будущее.
Перечень комманд — это ngx_command_t, который выглядит так:
typedef struct ngx_module_s ngx_module_t;
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
— смещение до конфига от начала контекста -
offset
— смещение относительно начала конфига до поля, которое настраиваем -
post
— указатель на список handlers, которые будут выполнены после конфигурации.
Типичный модуль инициализируется так:
ngx_module_t ngx_dc_module = {
NGX_MODULE_V1,
&ngx_dc_module_ctx, /* module context */
ngx_dc_commands, /* module directives */
NGX_CORE_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
Тут используется два макроса для упрощения записи, которые выглядят так:
#define NGX_MODULE_V1 0, 0, 0, 0, 0, 0, 1
#define NGX_MODULE_V1_PADDING 0, 0, 0, 0, 0, 0, 0, 0
Между макросами распологаются «данные модуля».
Наш пример будет состоять из двух модулей. В первом мы реализуем блок и это будет модуль с типом NGX_CORE_MODULE
, а во втором логику модуля и тип будет NGX_DC_MODULE
.
В nginx есть 5 типа модулей, которые объявляются макросами:
#define NGX_HTTP_MODULE 0x50545448 /* "HTTP" */
#define NGX_MAIL_MODULE 0x4C49414D /* "MAIL" */
#define NGX_CORE_MODULE 0x45524F43 /* "CORE" */
#define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */
#define NGX_EVENT_MODULE 0x544E5645 /* "EVNT" */
Цифры в индексе — это ascii коды букв в комментариях ☺, тогда мы можем «объявить» наш тип:
#define NGX_DC_MODULE 0x00004443 /* "DC" */
Теперь нам нужен контекст модуля. Контекст модуля это ngx_core_module_t
(зависит от типа модуля, у нас пока NGX_CORE_MODULE
):
typedef struct {
ngx_str_t name;
void *(*create_conf)(ngx_cycle_t *cycle);
char *(*init_conf)(ngx_cycle_t *cycle, void *conf);
} ngx_core_module_t;
Тут все просто:
-
name
— имя модуля для ругани, если не удается сконфигурироваться ☺ -
*create_conf
— указатель на функцию создания конфигурации -
*int_conf
— указатель на функцию инициализации конфигурации
Для нашего модуля достаточно простой нужен контекст:
static ngx_core_module_t ngx_dc_module_ctx = {
ngx_string("dc"),
NULL,
NULL
};
Теперь мы можем сделать команду:
static ngx_command_t ngx_dc_commands[] = {
{ ngx_string("dc"),
NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_TAKE1,
ngx_dc_block,
0,
0,
NULL },
ngx_null_command
};
Тут я использую ngx_null_command
, который из себя представляет макрос‑sentinel:
#define ngx_null_command { ngx_null_string, 0, NULL, 0, 0, NULL }
Теперь нам нужен контекст:
typedef struct {
ngx_str_t name;
void **conf;
} ngx_dc_conf_ctx_t;
и конфигурация:
typedef struct {
ngx_str_t uri;
} ngx_dc_core_conf_t;
и наш модуль:
typedef struct {
void *(*create_conf)(ngx_conf_t *cf);
char *(*init_conf)(ngx_conf_t *cf, void *conf);
} ngx_dc_module_t;
еще нам нужен тип для команд
#define NGX_DC_CONF 0x02000000
и т.к. у нас в контексте не только указатель на конфигурацию, то нам нужно смещение
#define NGX_DC_CONF_OFFSET offsetof(ngx_dc_conf_ctx_t, conf)
Дальше нам надо только описать блок конфигурации (ngx_dc_block
) — он будет простым — у нашего dc будет только один конфиг, а не три, как в NGX_HTTP_MODULE
.
static char *ngx_dc_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
char *rv;
ngx_str_t *value;
ngx_uint_t i;
ngx_uint_t mi;
ngx_conf_t pcf;
ngx_dc_module_t *m;
ngx_dc_conf_ctx_t *ctx;
/* the dc context */
ctx = ngx_pcalloc(cf->pool, sizeof(ngx_dc_conf_ctx_t));
if (ctx == NULL) {
return NGX_CONF_ERROR;
}
*(ngx_dc_conf_ctx_t **) conf = ctx;
ngx_dc_max_module = 0;
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_DC_MODULE) {
continue;
}
ngx_modules[i]->ctx_index = ngx_dc_max_module++;
}
value = cf->args->elts;
ctx->name = value[1];
/* the dc conf context, it is the same in the all dc contexts */
ctx->conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_dc_max_module);
if (ctx->conf == NULL) {
return NGX_CONF_ERROR;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_DC_MODULE) {
continue;
}
m = ngx_modules[i]->ctx;
mi = ngx_modules[i]->ctx_index;
if (m->create_conf) {
ctx->conf[mi] = m->create_conf(cf);
if (ctx->conf[mi] == NULL) {
return NGX_CONF_ERROR;
}
}
}
pcf = *cf;
cf->ctx = ctx;
cf->module_type = NGX_DC_MODULE;
cf->cmd_type = NGX_DC_CONF;
rv = ngx_conf_parse(cf, NULL);
*cf = pcf;
if (rv != NGX_CONF_OK) {
*cf = pcf;
return rv;
}
for (i = 0; ngx_modules[i]; i++) {
if (ngx_modules[i]->type != NGX_DC_MODULE) {
continue;
}
m = ngx_modules[i]->ctx;
mi = ngx_modules[i]->ctx_index;
if (m->init_conf) {
rv = m->init_conf(cf, ctx->conf[mi]);
if (rv != NGX_CONF_OK) {
return rv;
}
}
}
return NGX_CONF_OK;
}
Теперь мы можем написать директивы привычным способом:
static void *ngx_dc_core_create_conf(ngx_conf_t *cf);
static ngx_command_t ngx_dc_core_commands[] = {
{ ngx_string("uri"),
NGX_DC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_str_slot,
NGX_DC_CONF_OFFSET,
offsetof(ngx_dc_core_conf_t, uri),
NULL },
ngx_null_command
};
ngx_dc_module_t ngx_dc_core_module_ctx = {
ngx_dc_core_create_conf, /* create configuration */
NULL /* init configuration */
};
ngx_module_t ngx_dc_core_module = {
NGX_MODULE_V1,
&ngx_dc_core_module_ctx, /* module context */
ngx_dc_core_commands, /* module directives */
NGX_DC_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static void *
ngx_dc_core_create_conf(ngx_conf_t *cf)
{
ngx_dc_core_conf_t *conf;
conf = ngx_pcalloc(cf->pool, sizeof(ngx_dc_core_conf_t));
if (conf == NULL) {
return NGX_CONF_ERROR;
}
return conf;
}
Вот пример этого кода, собранный в кучу: config, ngx_dc.c и ngx_dc.h.
Комментарии
Форма комментирования для «nginx programming guide: добавляем свой блок в конфигурацию»