对于一些访问量特别大,业务逻辑也相对简单的Web调用来说,通过一个nginx module来实现是一种比较好的优化方法。实现一个nginx module实际上比较简单。
1. nginx 配置添加
./configure --add-module=/path/to/module1/source
2. 添加 /path/to/module1/source/config 文件,内容
ngx_addon_name=ngx_http_hello_module
HTTP_MODULES="$HTTP_MODULES ngx_http_hello_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_hello_module.c"
CORE_LIBS="$CORE_LIBS -lfoo"
最后一行如果没有使用其他library, 可以去掉
3. 源代码 /path/to/module1/source/ngx_http_hello_module.c, 主要的业务逻辑在make_http_get_body 中完善。典型的hello world源代码如下
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>
#define OUT_BUFSIZE 256
static char *ngx_http_hello_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_foo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_hello_process_init(ngx_cycle_t *cycle);
static void ngx_http_hello_process_exit(ngx_cycle_t *cycle);
static ngx_int_t make_http_header(ngx_http_request_t *r);
static ngx_int_t make_http_get_body(ngx_http_request_t *r, char *out_buf);
static char g_foo_settings[64] = {0};
/* Commands */
static ngx_command_t ngx_http_hello_commands[] = {
{ ngx_string("ngx_hello_module"),
NGX_HTTP_LOC_CONF|NGX_CONF_NOARGS,
ngx_http_hello_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
{ ngx_string("hello"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_http_foo_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
ngx_null_command
};
static ngx_http_module_t ngx_http_hello_module_ctx = {
NULL, /* preconfiguration */
NULL, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
NULL, /* create location configuration */
NULL /* merge location configuration */
};
/* hook */
ngx_module_t ngx_http_hello_module = {
NGX_MODULE_V1,
&ngx_http_hello_module_ctx, /* module context */
ngx_http_hello_commands, /* module directives */
NGX_HTTP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
ngx_http_hello_process_init, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
ngx_http_hello_process_exit, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
/* setting header for no-cache */
static ngx_int_t make_http_header(ngx_http_request_t *r){
ngx_uint_t i;
ngx_table_elt_t *cc, **ccp;
r->headers_out.content_type.len = sizeof("text/html") - 1;
r->headers_out.content_type.data = (u_char *) "text/html";
ccp = r->headers_out.cache_control.elts;
if (ccp == NULL) {
if (ngx_array_init(&r->headers_out.cache_control, r->pool,
1, sizeof(ngx_table_elt_t *))
!= NGX_OK)
{
return NGX_ERROR;
}
ccp = ngx_array_push(&r->headers_out.cache_control);
if (ccp == NULL) {
return NGX_ERROR;
}
cc = ngx_list_push(&r->headers_out.headers);
if (cc == NULL) {
return NGX_ERROR;
}
cc->hash = 1;
cc->key.len = sizeof("Cache-Control") - 1;
cc->key.data = (u_char *) "Cache-Control";
*ccp = cc;
} else {
for (i = 1; i < r->headers_out.cache_control.nelts; i++) {
ccp[i]->hash = 0;
}
cc = ccp[0];
}
cc->value.len = sizeof("no-cache") - 1;
cc->value.data = (u_char *) "no-cache";
return NGX_OK;
}
static ngx_int_t make_http_get_body(ngx_http_request_t *r, char *out_buf){
char *qs_start = (char *)r->args_start;
char *qs_end = (char *)r->uri_end;
char uri[128] = {0};
char *id;
if (qs_start == NULL || qs_end == NULL){
return NGX_HTTP_BAD_REQUEST;
}
if ((memcmp(qs_start, "id=", 3) == 0)){
id = qs_start + 3;
*qs_end = '\0';
}else{
return NGX_HTTP_BAD_REQUEST;
}
snprintf(uri, r->uri.len + 1, "%s", r->uri.data);
sprintf(out_buf, "Author: http://timyang.net/\nconfig=%s\nid=%snuri=%s\nret=%lx\n", g_foo_settings, id, uri, ngx_random());
return NGX_OK;
}
static ngx_int_t
ngx_http_hello_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
ngx_buf_t *b;
ngx_chain_t out;
/* Http Output Buffer */
char out_buf[OUT_BUFSIZE] = {0};
if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
return NGX_HTTP_NOT_ALLOWED;
}
rc = ngx_http_discard_request_body(r);
if (rc != NGX_OK && rc != NGX_AGAIN) {
return rc;
}
/* make http header */
rc = make_http_header(r);
if (rc != NGX_OK) {
return rc;
}
if (r->method == NGX_HTTP_HEAD) {
r->headers_out.status = NGX_HTTP_OK;
return ngx_http_send_header(r);
} else if (r->method == NGX_HTTP_GET) {
/* make http get body buffer */
rc = make_http_get_body(r, out_buf);
if (rc != NGX_OK) {
return rc;
}
} else {
return NGX_HTTP_NOT_ALLOWED;
}
b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
if (b == NULL) {
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
out.buf = b;
out.next = NULL;
b->pos = (u_char *)out_buf;
b->last = (u_char *)out_buf + strlen(out_buf);
b->memory = 1;
b->last_buf = 1;
r->headers_out.status = NGX_HTTP_OK;
r->headers_out.content_length_n = strlen(out_buf);
rc = ngx_http_send_header(r);
if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
return rc;
}
return ngx_http_output_filter(r, &out);
}
static char *
ngx_http_hello_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_core_loc_conf_t *clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
/* register hanlder */
clcf->handler = ngx_http_hello_handler;
return NGX_CONF_OK;
}
static char *
ngx_http_foo_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_str_t *value = cf->args->elts;
memcpy(g_foo_settings, value[1].data, value[1].len);
g_foo_settings[value[1].len] = '�';
return NGX_CONF_OK;
}
static ngx_int_t
ngx_http_hello_process_init(ngx_cycle_t *cycle)
{
// do some init here
return NGX_OK;
}
static void
ngx_http_hello_process_exit(ngx_cycle_t *cycle)
{
return;
}
4. 配置文件 nginx.conf
location /hello {
ngx_hello_module;
hello 1234;
}
5. 访问 http://localhost/hello?id=1
也可参考更详细的英文说明:
Emiller’s Guide To Nginx Module Development