1, Functions to be implemented by the module
Implementation function: count the access times of each ip address and display the corresponding html. (what's the use of this function? It can be made into a black-and-white list according to the statistics of the number of times. For those with more visits, access is prohibited)
In the conf file, you can set the command count to configure the module. When accessing the / test resource, the current ip access times will be counted.
As shown in the figure below, the accessed resource is 102.168.232.137 / test. Jump to 192.168.232.1 and count will be + 1 every time you access it
When accessing different IPs, the corresponding count will restart. Statistics based on client IP
1. Shared memory and slab
To make every access, the count is increased by one, but the worker process of the nginx server accessed by the browser may not be the same. Therefore, the way of shared memory can be used for inter process communication, and the number of accesses is shared resources.
Then, the shared memory can be managed and allocated through the slab allocation strategy.
What is the difference between shmem and slab?
shmem is shared memory and slab is memory allocation strategy. Therefore, the allocation strategy of slab can be adopted for shared memory
2. Stored data structure
You can see that all ip addresses and their corresponding access times are shown below
How to realize the above functions?
The ip address is used as the key and the azimuth count is used as the value, which is organized through the red black tree. During each access, just make count+1 corresponding to the ip. If the key of the ip is not in the red black tree, create a new node and assign count=1.
To display the above content, you only need to traverse the red black tree, then encode it into html format and send it out.
Because it is returned directly on nginx side, there is no reverse proxy to the server. Therefore, it belongs to the handler module.
2, Implement the handler module
The following customized functions are executed in the following order:
1.ngx_http_pagecount_create_location_conf allocates a piece of memory for the structure to pass parameters
2.ngx_http_pagecount_set initializes a shared memory (used to store red and black trees) and defines a handler function (execute count+1 and return html)
3.ngx_http_pagecount_init returns NGX directly_ OK
tatic ngx_command_t count_commands[] = { { ngx_string("count"),//The custom http module instruction is count NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,//Flag bit: the instruction configuration location is http{location {...}}, And the command requires no parameters ngx_http_pagecount_set,//The execution function when parsing the count command (when parsing the conf file, it will be executed when encountering the count command) NGX_HTTP_LOC_CONF_OFFSET,//Instruction storage address. The instruction address is obtained through the function of finding the instruction address. The storage location of the instruction is http{location {..}} 0, NULL }, ngx_null_command//Instruction array end flag }; static ngx_http_module_t count_ctx = { NULL, ngx_http_pagecount_init,//After parsing the conf, you can initialize http_pagecount NULL, NULL, NULL, NULL, ngx_http_pagecount_create_location_conf,//Create a piece of memory to place the configuration information of loc NULL, }; //ngx_http_count_module ngx_module_t ngx_http_pagecount_module = { //The name of the module must be unique and cannot conflict with the names of other modules NGX_MODULE_V1, &count_ctx,//Specific module http module count_commands,//Module supported instructions NGX_HTTP_MODULE,//The module type is http module NULL, NULL, NULL, NULL, NULL, NULL, NULL, NGX_MODULE_V1_PADDING }; typedef struct { int count; //count } ngx_http_pagecount_node_t;
1,ngx_http_pagecount_create_location_conf
Allocate a space for a defined structure to store information such as shared memory
typedef struct { ssize_t shmsize; ngx_slab_pool_t *shpool; ngx_http_pagecount_shm_t *sh; } ngx_http_pagecount_conf_t; void *ngx_http_pagecount_create_location_conf(ngx_conf_t *cf) { ngx_http_pagecount_conf_t *conf; conf = ngx_palloc(cf->pool, sizeof(ngx_http_pagecount_conf_t)); if (NULL == conf) { return NULL; } conf->shmsize = 0; ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_create_location_conf"); // init conf data // ... return conf; }
2,ngx_http_pagecount_set
Get a piece of shared memory, set the shared memory initialization function, and set the handler function
When nginx starts, the shared memory will be initialized
The handler function is called once for each visit
static char *ngx_http_pagecount_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_shm_zone_t *shm_zone;//An area of shared memory (not the entire shared memory) ngx_str_t name = ngx_string("pagecount_slab_shm"); ngx_http_pagecount_conf_t *mconf = (ngx_http_pagecount_conf_t*)conf; ngx_http_core_loc_conf_t *corecf; ngx_log_error(NGX_LOG_EMERG, cf->log, ngx_errno, "ngx_http_pagecount_set000"); mconf->shmsize = 1024*1024; shm_zone = ngx_shared_memory_add(cf, &name, mconf->shmsize, &ngx_http_pagecount_module);//Take one piece out of the shared area (why is it called add here? It means that one piece is added to what has been used outside) if (NULL == shm_zone) { return NGX_CONF_ERROR; } shm_zone->init = ngx_http_pagecount_shm_init;//Initialization function of shared memory (this memory is made into slab here) shm_zone->data = mconf; corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); corecf->handler = ngx_http_pagecount_handler;//This function (i.e. accounting number) is executed every time the web page is visited return NGX_CONF_OK; }
1)ngx_http_pagecount_shm_init
Initialize shared memory
Here, we mainly configure the slab and use the shared memory allocated by the slab to initialize a red black tree
ngx_int_t ngx_http_pagecount_shm_init (ngx_shm_zone_t *zone, void *data) { ngx_http_pagecount_conf_t *conf; ngx_http_pagecount_conf_t *oconf = data; conf = (ngx_http_pagecount_conf_t*)zone->data; if (oconf) { conf->sh = oconf->sh; conf->shpool = oconf->shpool; return NGX_OK; } printf("ngx_http_pagecount_shm_init 0000\n"); conf->shpool = (ngx_slab_pool_t*)zone->shm.addr; conf->sh = ngx_slab_alloc(conf->shpool, sizeof(ngx_http_pagecount_shm_t));//Allocate a piece of space through the slab (that is, conf - > SH is the shared memory in the slab) if (conf->sh == NULL) { return NGX_ERROR; } conf->shpool->data = conf->sh; printf("ngx_http_pagecount_shm_init 1111\n"); ngx_rbtree_init(&conf->sh->rbtree, &conf->sh->sentinel, //The red black tree initialization in ngx needs to be inserted by itself (how to compare key s is the same as the part in the insertion timer, just imitate it) ngx_http_pagecount_rbtree_insert_value); return NGX_OK; }
2)ngx_http_pagecount_handler
handler function. After loading the module under the count command and accessing the specified resources, this function will be called
It mainly refers to the number of accesses corresponding to the ip + 1, and traverses the red black tree to obtain all ip and corresponding times in the way of htmp coding
//handler is a direct return structure after receiving a request. It doesn't need to be like static ngx_int_t ngx_http_pagecount_handler(ngx_http_request_t *r) { u_char html[1024] = {0};//It's best to allocate memory pool int len = sizeof(html); ngx_rbtree_key_t key = 0; struct sockaddr_in *client_addr = (struct sockaddr_in*)r->connection->sockaddr;//Get the address of the client ngx_http_pagecount_conf_t *conf = ngx_http_get_module_loc_conf(r, ngx_http_pagecount_module); key = (ngx_rbtree_key_t)client_addr->sin_addr.s_addr;//ip address ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_handler --> %x\n", key); ngx_shmtx_lock(&conf->shpool->mutex);//Critical resource locking ngx_http_pagecount_lookup(r, conf, key);//If the corresponding ip(key) is found, the number of accesses (value) + +. If it is not found, a new node is inserted, and the number of accesses is 1 ngx_shmtx_unlock(&conf->shpool->mutex); ngx_encode_http_page_rb(conf, (char*)html);//Traverse the red black tree to generate the string format of html //The rest is to encode the header and body //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); }
3) Some operations on red black tree
The following are some operations of red black tree, such as inserting nodes, finding nodes and traversing red black tree
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 ngx_int_t ngx_http_pagecount_lookup(ngx_http_request_t *r, ngx_http_pagecount_conf_t *conf, ngx_uint_t key) { ngx_rbtree_node_t *node, *sentinel; node = conf->sh->rbtree.root; sentinel = conf->sh->rbtree.sentinel; ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 111 --> %x\n", key); //Find the node. If it is found, let it the value (Times) corresponding to the key(ip)++ while (node != sentinel) { if (key < node->key) { node = node->left; continue; } else if (key > node->key) { node = node->right; continue; } else { // key == node node->data ++; return NGX_OK; } } ngx_log_error(NGX_LOG_EMERG, r->connection->log, ngx_errno, " ngx_http_pagecount_lookup 222 --> %x\n", key); //If no node is found, insert a node so that value = 1 // insert rbtree node = ngx_slab_alloc_locked(conf->shpool, sizeof(ngx_rbtree_node_t));//Allocate to shared memory through slab if (NULL == node) { return NGX_ERROR; } node->key = key; node->data = 1; ngx_rbtree_insert(&conf->sh->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_pagecount_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); //Traversal node ngx_rbtree_node_t *node = ngx_rbtree_min(conf->sh->rbtree.root, conf->sh->rbtree.sentinel);//Find the node in the lower left corner 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);//Convert int type key to ip address strcat(html, buffer);//Character connection node = ngx_rbtree_next(&conf->sh->rbtree, node);//Find next node } while (node); strcat(html, "</h2>"); return NGX_OK; }
3,ngx_http_pagecount_init
Null implementation, directly return an NGX_OK
ngx_int_t ngx_http_pagecount_init(ngx_conf_t *cf) { #if 0 ngx_http_core_loc_conf_t *cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); ngx_http_handler_pt *h = ngx_array_push(cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers); if (NULL == h) { return NGX_ERROR; } *h = ngx_http_pagecount_handler; #elif 0 ngx_log_error(NGX_LOG_NOTICE, cf->log, ngx_errno, "ngx_http_pagecount_init"); ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); corecf->handler = ngx_http_pagecount_handler; //return NGX_OK; #endif return NGX_OK; }