1.下载
下载gcc sudo apt-get install build-essential
下载nginx源码 git clone http://gitlab.0voice.com/2404_vip/nginx.git
sudo apt-get install libpcre3-dev
sudo apt-get install zlib1g-dev
2.安装
解压后进入文件,
./configure --prefix=/home/zjx/share/nginx --with-http_realip_module --with-http_addition_module --with-http_ssl_module --with-http_gzip_static_module --with-http_secure_link_module --with-http_stub_status_module --with-stream --with-pcre=/home/zjx/share/nginx/pcre-8.41 --with-zlib=/home/zjx/share/nginx/zlib-1.2.11 --with-openssl=/home/zjx/share/nginx/openssl-1.1.1s
make
make install
3.运行
./sbin/nginx -c conf/nginx.conf
进入
192.168.88.130:80 80在nginx.conf找
重启
配置conf。backend不是关键字
location /后可跟字符用来https://url/字符
可以加权重,负载均衡
反向代理,指服务器ip和端口被代理。
正向代理,指自己的ip被代理,如vpn。
负载均衡,是在反向代理的基础上,选择代理的子服务器
4.模型
通过fork创建子进程,并在子进程中进行epoll,accept等
5.代码
upstream 把数据从浏览器发送到nginx,再到server
无代码
filter 把数据从nginx发送到浏览器
在子进程中操作
几个接口
pool分配内存时可能会因为对齐造成偏差和浪费
-
void *elts
:这是一个指向数组元素存储区域的指针。由于类型是void *
,它可以指向任何类型的数据,具体类型由数组的创建者决定。 -
ngx_uint_t nelts
:这个字段表示数组中当前实际存储的元素数量。ngx_uint_t
是Nginx中定义的无符号整数类型,通常是一个平台相关的无符号整数。 -
size_t size
:这个字段表示数组中单个元素的大小(以字节为单位)。这个值在数组初始化时设置,用于确定每个元素在内存中的存储空间。 -
ngx_uint_t nalloc
:这个字段表示数组分配的元素槽位总数。即使某些槽位当前没有存储元素,它们也已经被分配了内存空间。 -
ngx_pool_t *pool
:这个字段指向一个内存池(ngx_pool_t
),用于管理数组的内存分配。Nginx使用内存池来高效地管理内存,避免频繁的系统调用和内存碎片问题。
-
ngx_list_part_t *last
:这是一个指向链表中最后一个部分的指针。ngx_list_part_t
是链表的一个部分(或称为节点),每个部分包含一组元素。通过这个指针,可以快速访问链表的末尾。 -
ngx_list_part_t part
:这是链表的第一个部分。它包含了链表的初始元素集合。 -
size_t size
:这个字段表示链表中单个元素的大小(以字节为单位)。这个值在链表初始化时设置,用于确定每个元素在内存中的存储空间。 -
ngx_uint_t nalloc
:这个字段表示每个链表部分(ngx_list_part_t
)中分配的元素槽位总数。即使某些槽位当前没有存储元素,它们也已经被分配了内存空间。 -
ngx_pool_t *pool
:这个字段指向一个内存池(ngx_pool_t
),用于管理链表的内存分配。Nginx使用内存池来高效地管理内存,避免频繁的系统调用和内存碎片问题。
-
ngx_hash_elt_t **buckets
:这是一个指向哈希表桶数组的指针。每个桶(bucket)是一个指向ngx_hash_elt_t
结构体数组的指针。ngx_hash_elt_t
结构体代表哈希表中的一个元素,包含键和值。哈希表通过键的哈希值来确定元素应该存储在哪个桶中。 -
ngx_uint_t size
:这个字段表示哈希表中桶的数量。哈希表的大小(size)决定了哈希冲突的可能性。较大的哈希表大小可以减少冲突,但也会增加内存消耗
-
ngx_rbtree_node_t *root
:这是指向红黑树根节点的指针。根节点是红黑树的入口点,所有的操作都从根节点开始。 -
ngx_rbtree_node_t *sentinel
:这是指向哨兵节点的指针。在红黑树中,哨兵节点(也称为空节点或叶子节点)用于表示树的边界和结束条件。所有的叶子节点和根节点的父节点都指向这个哨兵节点。 -
ngx_rbtree_insert_pt insert
:这是一个函数指针,指向用于插入新节点的函数。红黑树的插入操作需要根据具体的应用场景来定义,因此通过函数指针来指定插入操作的具体实现
-
ngx_queue_t *prev
:这是一个指向链表中前一个节点的指针。通过这个指针,可以快速访问链表中当前节点的前一个节点。 -
ngx_queue_t *next
:这是一个指向链表中后一个节点的指针。通过这个指针,可以快速访问链表中当前节点的后一个节点。
-
ngx_queue_s
本身不存储数据,它只是一个链表节点的定义。 -
实际的数据存储在另一个包含
ngx_queue_s
字段的结构体中。 -
通过将数据节点链接到链表中,可以实现数据的高效管理和访问
makefile
CXX = gcc CXXFLAGS += -g -Wall -Wextra
NGX_ROOT = /home/king/share/nginx/nginx-1.22.1 PCRE_ROOT = /home/king/share/nginx/pcre-8.45
TARGETS = ngx_code TARGETS_C_FILE = $(TARGETS).c
CLEANUP = rm -f $(TARGETS) *.o
all: $(TARGETS)
clean: $(CLEANUP)
CORE_INCS = -I. \ -I$(NGX_ROOT)/src/core \ -I$(NGX_ROOT)/src/event \ -I$(NGX_ROOT)/src/event/modules \ -I$(NGX_ROOT)/src/os/unix \ -I$(NGX_ROOT)/objs \ -I$(PCRE_ROOT) \
NGX_PALLOC = $(NGX_ROOT)/objs/src/core/ngx_palloc.o NGX_STRING = $(NGX_ROOT)/objs/src/core/ngx_string.o NGX_ALLOC = $(NGX_ROOT)/objs/src/os/unix/ngx_alloc.o NGX_ARRAY = $(NGX_ROOT)/objs/src/core/ngx_array.o NGX_HASH = $(NGX_ROOT)/objs/src/core/ngx_hash.o
$(TARGETS): $(TARGETS_C_FILE) $(CXX) $(CXXFLAGS) $(CORE_INCS) $(NGX_PALLOC) $(NGX_STRING) $(NGX_ALLOC) $(NGX_ARRAY) $(NGX_HASH) $^ -o $@
config文件配置,用在add_module中
main_conf代表http下,srv_conf代表server下,loc_conf代表location,conf_falg代表值为on/off的
ngx_conf_set_flag_slot 用cf把cmd的flags解析,并存放在conf中
offsetof指出存储位置
7个NULL对应图中7个函数,这里不加
8个hook也不用,用NGX padding填充
ngx_http_module_t中输入8个回调函数
ngx_http_filter_conf_t *conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_filter_conf_t));不论location中是否有label,都会分配内存
ngx_conf_merge_value
函数的作用是:
-
如果
conf->enable
没有被显式设置(即其值为NGX_CONF_UNSET
),则将其设置为prev->enable
的值。 -
如果
conf->enable
已经被设置,则保持其当前值。 -
如果
prev->enable
也没有被设置,则将其设置为默认值0
在
ngx_int_t ngx_http_header_filter(ngx_http_request_t *r){
}添加额外内容长度或者其他头部
ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *chain){
}添加具体内容,比如附加广告。需要在conf文件中,添加flags为on和数据
#include <ngx_http.h>
#include <ngx_core.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
typedef struct{
int count;
struct in_addr addr;
}ngx_px_table_t;
#define ENABLE_RBTREE 1
ngx_px_table_t pv_table[256];
char flags = 0;
char *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_int_t (*postconfiguration)(ngx_conf_t *cf);
ngx_int_t ngx_http_count_handler(ngx_http_request_t *r);
#if ENABLE_RBTREE
static ngx_rbtree_t rbtree;
static ngx_rbtree_t sentinel =nullptr;
#endif
static ngx_command_t hander_cmds[]={
{
ngx_string("zjx"),
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_handler_count_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,NULL
},
ngx_null_command
};
static ngx_http_module_t hander{
NULL,ngx_http_count_handler_init,NULL,NULL,NULL,NULL,NULL,NULL,
}
//名字要和conf中一样
ngx_module_t ngx_http_filter_module={
NGX_MODULE_V1,&ngx_http_filter_module_ctx,ngx_http_filter_module_commands,
NGX_HTTP_MODULE,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING,
};
//自定义红黑树查找方法
ngx_rbtree_node_t*
ngx_rbtree_search_counter_value(ngx_rbtree_node_t *temp, ngx_rbtree_t *key,
ngx_rbtree_node_t *sentinel)
{
ngx_rbtree_node_t **p;
for ( ;; ) {
/*
* Timer values
* 1) are spread in small range, usually several minutes,
* 2) and overflow each 49 days, if milliseconds are stored in 32 bits.
* The comparison takes into account that overflow.
*/
/* node->key < temp->key */
#if ENABLE_RBTREE
if(key < temp->key){
p = &temp->left;
}else if(key > temp->key){
p = &temp->right;
}else{
return temp;
}
#else
p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)
? &temp->left : &temp->right;
#endif
if (*p == sentinel) {
return NULL;
}
temp = *p;
}
}
//自定义红黑树插入方法,temp为树中结点,node为待插入结点
void
ngx_rbtree_insert_counter_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,
ngx_rbtree_node_t *sentinel)
{
ngx_rbtree_node_t **p;
for ( ;; ) {
/*
* Timer values
* 1) are spread in small range, usually several minutes,
* 2) and overflow each 49 days, if milliseconds are stored in 32 bits.
* The comparison takes into account that overflow.
*/
/* node->key < temp->key */
#if ENABLE_RBTREE
if(node->key < temp->key){
p = &temp->left;
}else if(node->key > temp->key){
p = &temp->right;
}else{
//由于时间单位为毫秒几乎不会冲突,所以有冲突时结束
return;
}
#else
p = ((ngx_rbtree_key_int_t) (node->key - temp->key) < 0)
? &temp->left : &temp->right;
#endif
if (*p == sentinel) {
break;
}
temp = *p;
}
*p = node;
node->parent = temp;
node->left = sentinel;
node->right = sentinel;
ngx_rbt_red(node);
}
//自定义红黑树遍历函数
void ngx_rbtree_travelsal_counter_value(ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node,ngx_rbtree_node_t *sentinel, char*html) {
if (node != sentinel) {
ngx_rbtree_travelsal_counter_value(temp, node->left,sentinel,html);
char str[INET_ADDRSTRLEN] = {0};
char buffer[128] = {0};
//把pv_table[i].addr从ip结构体转化为16进制,并存储到str中
snprintf(buffer,128, "req from: %s, count: %d <br/>",
inet_ntop(AF_INET, node->key, str, sizeof(str)), node->data);
//把buffer内容加到html中
strcat(html, buffer);
//printf("key:%d, color:%d\n", node->key, node->color,sentinel);
ngx_rbtree_travelsal_counter_value(temp, node->right,sentinel,html);
}
}
char *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
#if ENABLE_RBTREE
ngx_rbtree_init(&rbtree, &sentinel, ngx_rbtree_insert_counter_value);
#endif
//获取对应module的conf
ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
corecf->handler = ngx_http_count_handler;
return NGX_CONF_OK;
}
static int ngx_http_encode_page(char *html){
//固定显示内容
sprintf(html, "<h1>zjxzjx</h1>");
sprintf(html, "<h2>")
#if ENABLE_RBTREE
ngx_rbtree_travelsal_counter_value(&rbtree, rbtree.root,sentinel, html);
#else
//非固定内容
for(int i =0;i<256;i++){
if(pv_table[index].count!=0){
char str[INET_ADDRSTRLEN] = {0};
char buffer[128] = {0};
//把pv_table[i].addr从ip结构体转化为16进制,并存储到str中
snprintf(buffer,128, "req from: %s, count: %d <br/>",
inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)), pv_table[i].count);
//把buffer内容加到html中
strcat(html, buffer);
}
}
#endif
strcat(html, "</h2>")
return 0;
}
ngx_int_t ngx_http_count_handler(ngx_http_request_t *r){
//获得对端的ip地址
struct sockaddr_in *client_addr = (struct sockaddr_in *)r->connection->sockaddr;
#if ENABLE_RBTREE
ngx_rbtree_key_t key = client_addr->sin_addr.s_addr;
ngx_rbtree_node_t*node = ngx_rbtree_search_counter_value(&rbtree, &key,&sentinel);
if(node){
node->data++;
}else{
node = ngx_palloc(r->pool,sizeof(ngx_rbtree_node_t));
node->data=1;
node->key=key;
ngx_rbtree_insert_counter_value(&rbtree, node, &sentinel);
}
#else
//ip地址大端存储,向右移位24,只保留最后.后面的内容
int index = client_addr->sin_addr.s_addr >>24;
pv_table[index].count++;
memcpy(&pv_table[index].addr, &client_addr->sin_addr,sizeof(client_addr->sin_addr));
#endif
u_char html[1024] = {0};
ngx_http_encode_page((char*)html);
//返回状态,正常默认200
r->headers_out.status = 200;
//数据状态,为string类型
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));
//设置body内容,用链表保存
ngx_chain_t out;
out.buf = b;
out.next = NULL;
//body起始位置
b->pos = html;
//body结尾位置
b->last = html + sizeof(html);
//是否内存有缓存
b->memory = 1;
//是否是最后一个buffer
b->last_buf = 1;
//发送出去
return ngx_http_output_body_filter(r, &out);
}
//使用一块共享内存,开辟在init
ngx_int_t ngx_http_count_handler_init(ngx_conf_t *cf){
ngx_shm_zone_t *shm_zone;
ngx_str_t name = ngx_string("count_shm");
shm_zone = ngx_shared_memory_add(cf, name, 1024*1024, ngx_http_filter_module);
ngx_slab_alloc(shm_zone, 1024*1024);
shm_zone->data
return NGX_OK;
}
比如
handler 数据从浏览器到nginx,再到浏览器
在count后再增加功能
首先复制并修改config,把name改成新建的c文件名,其他跟着改
第一个流程再nginx -c开始,初始化一系列进程。
第二个流程根据conf文件和代码生成功能,比如
在conf中找到location,并且NGX_CONF_NOARGS说明/后不能有参数。对应内有zjx,则执行set函数
第三个流程是当http请求来的时候的流程
输入这两个,其他默认就行
#include <ngx_http.h>
#include <ngx_core.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
typedef struct{
int count;
struct in_addr addr;
}ngx_px_table_t;
ngx_px_table_t pv_table[256];
char flags = 0;
char *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
ngx_int_t ngx_http_count_handler(ngx_http_request_t *r);
static ngx_command_t hander_cmds[]={
{
ngx_string("zjx"),
NGX_HTTP_LOC_CONF | NGX_CONF_NOARGS,
ngx_http_handler_count_set,
NGX_HTTP_LOC_CONF_OFFSET,
0,NULL
},
ngx_null_command
};
static ngx_http_module_t hander{
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,
}
ngx_module_t ngx_http_filter_module={
NGX_MODULE_V1,&ngx_http_filter_module_ctx,ngx_http_filter_module_commands,
NGX_HTTP_MODULE,
NULL,NULL,NULL,NULL,NULL,NULL,NULL,NGX_MODULE_V1_PADDING,
};
char *ngx_http_handler_count_set(ngx_conf_t *cf, ngx_command_t *cmd, void *conf){
//获取对应module的conf
ngx_http_core_loc_conf_t *corecf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
corecf->handler = ngx_http_count_handler;
return NGX_CONF_OK;
}
static int ngx_http_encode_page(char *html){
//固定显示内容
sprintf(html, "<h1>zjxzjx</h1>");
sprintf(html, "<h2>")
//非固定内容
for(int i =0;i<256;i++){
if(pv_table[index].count!=0){
char str[INET_ADDRSTRLEN] = {0};
char buffer[128] = {0};
//把pv_table[i].addr从ip结构体转化为16进制,并存储到str中
snprintf(buffer,128, "req from: %s, count: %d <br/>"),
inet_ntop(AF_INET, &pv_table[i].addr, str, sizeof(str)), &pv_table[i].count);
//把buffer内容加到html中
strcat(html, buffer);
}
}
strcat(html, "</h2>")
return 0;
}
ngx_int_t ngx_http_count_handler(ngx_http_request_t *r){
//获得对端的ip地址
struct sockaddr_in *client_addr = (struct sockaddr_in *)r->connection->sockaddr;
//ip地址大端存储,向右移位24,只保留最后.后面的内容
int index = client_addr->sin_addr.s_addr >>24;
pv_table[index].count++;
memcpy(&pv_table[index].addr, &client_addr->sin_addr,sizeof(client_addr->sin_addr));
u_char html[1024] = {0};
ngx_http_encode_page((char*)html);
//返回状态,正常默认200
r->headers_out.status = 200;
//数据状态,为string类型
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));
//设置body内容,用链表保存
ngx_chain_t out;
out.buf = b;
out.next = NULL;
//body起始位置
b->pos = html;
//body结尾位置
b->last = html + sizeof(html);
//是否内存有缓存
b->memory = 1;
//是否是最后一个buffer
b->last_buf = 1;
//发送出去
return ngx_http_output_body_filter(r, &out);
}
6.命令
strace ./程序名 对调用进行追踪
创建了10个子进程,在发送连接请求后,进程会全部唤醒,并且争夺请求,消耗太大。nginx会给所有子进程加一个共有的锁,需要先争夺锁,然后唤醒epoll wait