【Nginx】Nginx新增自定义模块

系统环境

# uname -a
Linux localhost.localdomain 3.10.0-1160.el7.x86_64 #1 SMP Mon Oct 19 16:18:59 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

Nginx模块分类

  1. handler模块:客户端请求nginx,nginx可以修改客户端请求的信息或者直接返回信息给客户端
  2. filter模块:服务端返回结果给nginx时,nginx可以在服务端返回的结果上添加一些信息,然后返回给客户端
  3. upstream模块:客户端信息经nginx转发给服务端

Nginx模块执行流程

  1. nginx启动时(即nginx -c conf/nginx.conf),解析conf数据,放到模块内。并且设置客户端每一次请求的入口函数。
  2. 每一次客户端请求,调用模块设置的入口函数。

Nginx Handler模块示例

实现一个统计客户端查看资源次数的handler模块。

创建目录

# cd /home/ronghui/nginx/
# ls
nginx-1.21.0
# mkdir ngx_http_location_count_module
nginx-1.21.0  ngx_http_location_count_module

创建文件

# cd /home/ronghui/nginx/ngx_http_location_count_module
# touch ngx_http_location_count_module.c config

示例代码

ngx_http_location_count_module.c

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

// static void *ngx_http_location_count_create_main_conf(ngx_conf_t *cf);
// static void *ngx_http_location_count_create_server_conf(ngx_conf_t *cf);
static void *ngx_http_location_count_create_loc_conf(ngx_conf_t *cf);
static char *ngx_http_location_count_create_cmd_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_location_count_shm_zone_init(ngx_shm_zone_t *zone, void *data);
static ngx_int_t ngx_http_location_count_handler(ngx_http_request_t *r);
static void ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,
        ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel);

typedef struct {
	
	ngx_rbtree_t rbtree;
	ngx_rbtree_node_t sentinel;

} ngx_http_location_count_shm_t;

typedef struct {

	ssize_t shmsize;
	ngx_slab_pool_t *pool;

	ngx_http_location_count_shm_t *lcshm;

	//ngx_uint_t interval;
	//ngx_uint_t client_count;

} ngx_http_location_conf_t;

ngx_command_t ngx_http_location_count_cmd[] = {

	{
		ngx_string("count"),
		NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
		ngx_http_location_count_create_cmd_set,		
		NGX_HTTP_LOC_CONF_OFFSET,
		0, NULL	
	},
	ngx_null_command
};

static ngx_http_module_t ngx_http_location_count_ctx = {

	NULL,	//preconfiguration
	NULL,	//postconfiguration

	NULL,	//ngx_http_location_count_create_main_conf,	//create_main_conf
	NULL,	//init_main_conf

	NULL,	//ngx_http_location_count_create_server_conf,	//create_srv_conf
	NULL,	//merge_srv_conf

	ngx_http_location_count_create_loc_conf,	//create_loc_conf
	NULL,	//merge_loc_conf
};

ngx_module_t ngx_http_location_count_module = {

	NGX_MODULE_V1,
	&ngx_http_location_count_ctx,
	ngx_http_location_count_cmd,
	NGX_HTTP_MODULE,
	
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,
	NULL,

	NGX_MODULE_V1_PADDING,
};

static void *ngx_http_location_count_create_loc_conf(ngx_conf_t *cf){

	ngx_http_location_conf_t *conf = ngx_palloc(cf->pool, sizeof(ngx_http_location_conf_t));
	if(conf == NULL) {
		return NULL;
	}

	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_location_count_create_loc_conf");
		
	return conf;
	
}  

static char *ngx_http_location_count_create_cmd_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){

	ngx_http_location_conf_t *lconf = (ngx_http_location_conf_t*)conf;
	ngx_str_t name = ngx_string("location_count_slab");

	lconf->shmsize = 128*1024;

	//申请一块共享内存,存入模块数据
	//业务:统计客户端访问次数
	//因为客户端每次访问资源的时候可能在nginx的不同work进程中,因此把访问次数存放在共享内存中方便任意进程访问
	ngx_shm_zone_t *zone = ngx_shared_memory_add(cf, &name, lconf->shmsize, &ngx_http_location_count_module);
	if(zone == NULL){
		return NGX_CONF_ERROR;
	}

	//共享内存初始化时调用的回调函数
	zone->init = ngx_http_location_count_shm_zone_init;
	zone->data = lconf;

	ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
	//每次请求的回调函数
	corecf->handler = ngx_http_location_count_handler;

	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_location_count_create_cmd_set");
	
	return NULL;
	
}

static ngx_int_t ngx_http_location_count_shm_zone_init(ngx_shm_zone_t *zone, void *data){

	ngx_http_location_conf_t *conf;
	ngx_http_location_conf_t *oconf = data;

	conf = (ngx_http_location_conf_t*)zone->data;
	if (oconf) {
		conf->lcshm = oconf->lcshm;
		conf->pool = oconf->pool;
		return NGX_OK;
	}

	printf("ngx_http_location_count_shm_zone_init 0000\n");
	
	conf->pool = (ngx_slab_pool_t*)zone->shm.addr;
	conf->lcshm = ngx_slab_alloc(conf->pool, sizeof(ngx_http_location_conf_t));
	if (conf->lcshm == NULL) {
		return NGX_ERROR;
	}

	conf->pool->data = conf->lcshm;

	printf("ngx_http_location_count_shm_zone_init 1111\n");

	//rbtree init
	//创建红黑树,存放客户端的访问次数,该红黑树在共享内存中
	ngx_rbtree_init(&conf->lcshm->rbtree, &conf->lcshm->sentinel, 
		ngx_http_pagecount_rbtree_insert_value);	

	return NGX_OK;

}

static ngx_int_t ngx_http_location_count_rbtree_lookup(ngx_http_request_t *r, ngx_http_location_conf_t *conf, ngx_uint_t key){

	ngx_rbtree_node_t *node, *sentinel;

	node = conf->lcshm->rbtree.root; 
	sentinel = conf->lcshm->rbtree.sentinel;

	//search node
	while(node != sentinel){

		if(key < node->key) {
			node = node->left;
			continue;
		} else if (key > node->key) {
			node = node->right;
			continue;
		} else {
			node->data ++;	
			return NGX_OK;
		}
	}
	
	//if not exit then insert in rbtree

	//在共享内存中分配内存
	node = ngx_slab_alloc_locked(conf->pool, sizeof(ngx_rbtree_node_t));
	if(node == NULL){
		return NGX_ERROR;
	}
	node->key = key;
	node->data = 1;

	ngx_rbtree_insert(&conf->lcshm->rbtree, node);

	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " insert success\n");
	
	return NGX_OK;
}

static int ngx_encode_http_page_rb(ngx_http_location_conf_t *conf, char *html){

	sprintf(html, "<h1>Source Insight </h1>");
	strcat(html, "<h2>");

	//ngx_rbtree_traversal(&ngx_pv_tree, ngx_pv_tree.root, ngx_http_count_rbtree_iterator, html);
	ngx_rbtree_node_t *node = ngx_rbtree_min(conf->lcshm->rbtree.root, conf->lcshm->rbtree.sentinel);

	do {

		char str[INET_ADDRSTRLEN] = {0};
		char buffer[128] = {0};

		sprintf(buffer, "req from : %s, count: %d <br/>",
			inet_ntop(AF_INET, &node->key, str, sizeof(str)), node->data);

		strcat(html, buffer);

		node = ngx_rbtree_next(&conf->lcshm->rbtree, node);

	} while (node);
	

	strcat(html, "</h2>");

	return NGX_OK;

}

static ngx_int_t ngx_http_location_count_handler(ngx_http_request_t *r){

	struct sockaddr_in *client_addr = (struct sockaddr_in*)r->connection->sockaddr;

	ngx_uint_t key = client_addr->sin_addr.s_addr;

	ngx_http_location_conf_t *conf = ngx_http_get_module_loc_conf(r, ngx_http_location_count_module);
	
	//客户端访问次数+1
	ngx_shmtx_lock(&conf->pool->mutex);
	ngx_http_location_count_rbtree_lookup(r, conf, key);
	ngx_shmtx_unlock(&conf->pool->mutex);

	//log
	ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, "ngx_http_location_count_handler");

	返回信息给客户端

	//生成html内容
	u_char html[1024] = {0};
	int len = sizeof(html);
	ngx_encode_http_page_rb(conf, (char*)html);

	//header
	r->headers_out.status = 200;
	ngx_str_set(&r->headers_out.content_type, "text/html"); 
	ngx_http_send_header(r);
	
	//body
	ngx_buf_t *b = ngx_pcalloc(r->pool,  sizeof(ngx_buf_t));

	ngx_chain_t out;
	out.buf = b;
	out.next = NULL;

	b->pos = html;
	b->last = html+len;
	b->memory = 1;
	b->last_buf = 1;

	return ngx_http_output_filter(r, &out);
}

static void
ngx_http_pagecount_rbtree_insert_value(ngx_rbtree_node_t *temp,
        ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)
{
   ngx_rbtree_node_t **p;
   //ngx_http_testslab_node_t *lrn, *lrnt;
 
    for (;;)
    {
        if (node->key < temp->key)
        {
            p = &temp->left;
        }
        else if (node->key > temp->key) {
           	p = &temp->right;
        }
        else
        {
          	return ;
        }
 
        if (*p == sentinel)
        {
            break;
        }
 
        temp = *p;
    }
 
    *p = node;
 
    node->parent = temp;
    node->left = sentinel;
    node->right = sentinel;
    ngx_rbt_red(node);
}

// static void *ngx_http_location_count_create_main_conf(ngx_conf_t *cf){

// 	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_location_count_create_main_conf");
	
// 	return NULL;
// }

// static void *ngx_http_location_count_create_server_conf(ngx_conf_t *cf){

// 	ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_location_count_create_server_conf");
	
// 	return NULL;

// }

config

ngx_addon_name=ngx_http_location_count_module
HTTP_MODULES="$HTTP_MODULES ngx_http_location_count_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_location_count_module.c" 

编译生成模块文件

# cd /home/ronghui/nginx/nginx-1.21.0
# ./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-stream --with-pcre=/home/ronghui/pcre-8.41 --with-zlib=/home/ronghui/zlib-1.2.11 --add-module=/home/ronghui/nginx/ngx_http_location_count_module
# make && make install

修改配置文件

# cd /usr/local/nginx
# vi conf/nginx.conf
35     server {
36         listen       80;
37         server_name  localhost;
38 
39         #charset koi8-r;
40 
41         #access_log  logs/host.access.log  main;
42 
43         location / {
44             root   html;
45             index  index.html index.htm;
46         }
47 
48         location /test {
49             count; //与ngx_http_location_count_module模块中的ngx_command_t的name一样。
50         }

如上代码增加了48-50行

48         location /test {
49             count;
50         }

重启nginx

# cd /usr/local/nginx
# ./sbin/nginx -s stop
# netstat -anop  | grep 80
# pkill nginx
# netstat -anop  | grep 80
# ./sbin/nginx -c conf/nginx.conf

客户端访问

在这里插入图片描述

每刷新一次,count++。这个count值是存放在了nginx的共享内存中,方便不同worker进程访问,具体实现可以看上面代码。

Nginx filter模块示例

实现一个统计客户端查看资源次数的handler模块。

创建目录

# cd /home/ronghui/nginx/
# ls
nginx-1.21.0
# mkdir ngx_http_myfilter_module
nginx-1.21.0  ngx_http_myfilter_module

创建文件

# cd /home/ronghui/nginx/ngx_http_myfilter_module
# touch ngx_http_myfilter_module.c config

示例代码

ngx_http_myfilter_module.c

#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_http.h>

typedef struct {
    ngx_flag_t enable;
} ngx_http_myfilter_conf_t;

typedef struct {
    ngx_int_t add_prefix;
} ngx_http_myfilter_ctx_t;

static ngx_int_t ngx_http_myfilter_init(ngx_conf_t *cf);
static ngx_int_t ngx_http_myfilter_header_filter(ngx_http_request_t *r);
static ngx_int_t ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in);

static ngx_str_t filter_prefix = ngx_string("[my prefix filter]");


static void* ngx_http_myfilter_create_conf(ngx_conf_t *cf)
{
    ngx_http_myfilter_conf_t *mycf;
    mycf = (ngx_http_myfilter_conf_t *)ngx_pcalloc(cf->pool,sizeof(ngx_http_myfilter_conf_t));
    if(mycf == NULL) {
        return NULL;
    }
    mycf->enable = NGX_CONF_UNSET;
    return mycf;
}

static char* ngx_http_myfilter_merge_conf(ngx_conf_t *cf,void *parent, void *child)
{
    ngx_http_myfilter_conf_t *prev = (ngx_http_myfilter_conf_t *)parent;
    ngx_http_myfilter_conf_t *conf = (ngx_http_myfilter_conf_t *)child;

    ngx_conf_merge_value(conf->enable,prev->enable,0);
    return NGX_CONF_OK;
}

static ngx_command_t ngx_http_mytest_commands[] = {
    {
        ngx_string("add_prefix"),
        NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_FLAG,
        ngx_conf_set_flag_slot,
        NGX_HTTP_LOC_CONF_OFFSET,
        offsetof(ngx_http_myfilter_conf_t,enable),
        NULL },
    ngx_null_command
};

static ngx_http_module_t ngx_http_myfilter_module_ctx = {
    NULL,
    ngx_http_myfilter_init,
    NULL,
    NULL,

    NULL,
    NULL,
    ngx_http_myfilter_create_conf,
    ngx_http_myfilter_merge_conf
};

ngx_module_t ngx_http_myfilter_module = {
    NGX_MODULE_V1,
    &ngx_http_myfilter_module_ctx,
    ngx_http_mytest_commands,
    NGX_HTTP_MODULE,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NGX_MODULE_V1_PADDING
};

static ngx_http_output_header_filter_pt ngx_http_next_header_filter;
static ngx_http_output_body_filter_pt     ngx_http_next_body_filter;

static ngx_int_t ngx_http_myfilter_init(ngx_conf_t *cf)
{
    ngx_http_next_header_filter = ngx_http_top_header_filter;
    ngx_http_top_header_filter = ngx_http_myfilter_header_filter;


    ngx_http_next_body_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_myfilter_body_filter;
    return NGX_OK;
}

static ngx_int_t
ngx_http_myfilter_header_filter(ngx_http_request_t *r)
{
    ngx_http_myfilter_ctx_t  *ctx;
    ngx_http_myfilter_conf_t        *conf;

    if(r->headers_out.status != NGX_HTTP_OK)
    {
        return ngx_http_next_header_filter(r);
    }

    ctx = ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
    if(ctx) {
        return ngx_http_next_header_filter(r);
    }
    conf = ngx_http_get_module_loc_conf(r,ngx_http_myfilter_module);
    if(conf->enable == 0)
    {
        return ngx_http_next_header_filter(r);
    }
    ctx = ngx_pcalloc(r->pool,sizeof(ngx_http_myfilter_ctx_t));
    if(ctx == NULL)
    {
        return NGX_ERROR;
    }
    ctx->add_prefix = 0;

    ngx_http_set_ctx(r,ctx,ngx_http_myfilter_module);
    if(r->headers_out.content_type.len >= sizeof("text/plain")-1 
            && ngx_strncasecmp(r->headers_out.content_type.data,(u_char *)"text/plain",
                sizeof("text/plain")-1) == 0)
    {
        ctx->add_prefix = 1;
        if(r->headers_out.content_length_n > 0) {
            r->headers_out.content_length_n += filter_prefix.len;
        }
    }
    return ngx_http_myfilter_header_filter(r);
}

static ngx_int_t
ngx_http_myfilter_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_http_myfilter_ctx_t *ctx;
    ctx = ngx_http_get_module_ctx(r,ngx_http_myfilter_module);
    if(ctx==NULL||ctx->add_prefix != 1) {
        return ngx_http_next_body_filter(r,in);
    }

    ctx->add_prefix = 2;

    ngx_buf_t* b= ngx_create_temp_buf(r->pool,filter_prefix.len);
    b->start = b->pos = filter_prefix.data;
    b->last = b->pos + filter_prefix.len;

    ngx_chain_t *c1 = ngx_alloc_chain_link(r->pool);
    c1->buf = b;
    c1->next = in;
    return ngx_http_next_body_filter(r,c1);
}

config

ngx_addon_name=ngx_http_myfilter_module
HTTP_MODULES="$HTTP_MODULES ngx_http_myfilter_module"
NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_myfilter_module.c" 

编译生成模块文件

# cd /home/ronghui/nginx/nginx-1.21.0
# ./configure --prefix=/usr/local/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-stream --with-pcre=/home/ronghui/pcre-8.41 --with-zlib=/home/ronghui/zlib-1.2.11 --add-module=/home/ronghui/nginx/ngx_http_location_count_module --add-module=/home/ronghui/nginx/ngx_http_myfilter_module
# make && make install

修改配置文件

# cd /usr/local/nginx
# vi conf/nginx.conf
35     server {
36         listen       80;
37         server_name  localhost;
38 
39         #charset koi8-r;
40 
41         #access_log  logs/host.access.log  main;
42 
43         location / {
44             root   html;
45             index  index.html index.htm;
46         }
47 
48         location /test {
49			   md5_filter on;//nginx自带模块
50             add_prefix on; //与ngx_http_location_count_module模块中的ngx_command_t的name一样。
51			   count; //与ngx_http_myfilter_module模块中的ngx_command_t的name一样。
52         }

如上代码增加了48-51行

48         location /test {
49			   md5_filter on;//nginx自带模块
50             add_prefix on;
51			   count;	
52         }

重启nginx

# cd /usr/local/nginx
# ./sbin/nginx -s stop
# netstat -anop  | grep 80
# pkill nginx
# netstat -anop  | grep 80
# ./sbin/nginx -c conf/nginx.conf

客户端访问

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值