本篇文章主要记录说明使用nginx时,开辟一个简单http模块的时序图,还有nginx中封装的数据结构及其处理函数。

 1.如何开发一个充满异步调用,无阻塞的http模块?

 首先,我们需要把程序嵌如到nginx中(最终变异处的二进制程序nginx要包含我们的代码)。

 然后,这个http模块要能介入到处理流程中。

 在正式请求处理时,还要可以获取nginx框架定义的数据结构,解析后的用户请求信息

 业务执行完毕后,则要考虑发送响应给用户(包括将磁盘中的文件以http包体的形式发送给用户)


我们都是讨论C/C++语言来进行编写,虽然nginx官方不倡导,本文章不讨论各个核心模块如何配合工作。


 2.关于http模块的调用。

  1.首先nginx是master和多个worker进程的组合,worket进程会在一个for循环语句例反复调用时间模块检测网络事件,即检测到TCP请求(接收到SYN包),将会为它建立TCP连接。

  2.成功建立连接后根据nginx.conf文件中的配置会交由http框架处理。

  ps:http框架会试图接收完整的HTTP头部,并在接收到完整的http头部后将请求的uri和nginx.conf里的location配置项的匹配来决定如何分发。

  3.处理结束后会调用过滤模块,然后根据配置文件决定自己的行为。

 时序图:

    wKiom1e7wK7wTg9VAAEnwBDVnR8861.png ps:需要注意HTTP框架到具体http模块间数据流的传递,还有之间的协同,以后会写到。


模块命名为:ngx_http_mytest_module.c


下面进入正题,所以下nginx中封装的数据类型还有数据结构

 以下都为linux操作系统下的解释:

 1.×××的封装:

 typedef intptr_t ngx_int_t; //代表int
 typedef uintptr_t ngx_uint_t; //代表unsinged int


 2.数据结构:

 1.字符串

//字符串的封装。其中data不是普通的字符串,很有可能不以‘0’结尾。必须配合len
  typedef struct{
      size_t len;
      u_char *data;   
 }ngx_str_t;  
 //用于字符串的比较函数,因为未必有0,所以必须用ngx_strncmp比较
 #define ngx_strncmp(s1,s2,n) strncmp((const char *)s1,(const char *)s2,n)

 2.链表容器:

 

//这里封装的链表容器使用是很频繁,比如http头部就是使用这个数据结构来存储的。
    typedef struct ngx_list_part_s ngx_list_part_t;
    
    struct ngx_list_part_s
    {
        void            *elts;//数组的起始地址
        ngx_uint_t      nelts; //已经存在多少个元素,必须小于nalloc
        ngx_list_part_t *next; //下一个地址
    }
    
    typedef ngx_list_part_s
    {
        ngx_list_part_t *last;    //链表最后一个元素
        ngx_list_part_t part; //链表第一个数组元素
        size_t          size;    //每一个数组元素占用空间大小
        ngx_uint_t      nalloc;    //数组可以存储多少个数据
        ngx_pool_t      *poll;    //nginx内存池的对象。
    }ngx_list_t;
    
    //下面是封装的ngx_list_t的函数借口
    ngx_list_t *ngx_list_create(ngx_pool_t *pool,ngx_uint_t n,size_t size);//创建
    //返回创建链表地址。
    static ngx_inline ngx_int_t 
    ngx_list_init(ngx_list_t,ngx_pool_t *pool,ngx_uint_t n,size_t size); //初始化
    //返回NGX_OK,    NGX_ERROR;
    void *ngx_list_push(ngx_list_t *list);//添加元素、
    
    //遍历链表
    ngx_list_part_t *part = &testlist.part;
    ngx_str_t = part->elts;
    for(i = 0;;i++)
    {
        if(i >= part->nelts)
        {
            if(part->next = NULL)
                break;
            part = part->next;
            header = part->elts;
            i = 0 ;
        }
        printf("%*s\n",str[i].len,str[i].data);
    }

 3.key/value (用于存储http头部信息)

 

    typedef struct
    {
        ngx_uint_t hash;
        ngx_str_t key;
        ngx_str_t value;
        u_char*   lowcase_key; //全是小写的key字符串。
    }ngax_table_elt_t;

 4.缓冲区的数据结构

typedef struct ngx_buf_s ngx_buf_t;
typedef void *           ngx_buf_tag_t;
struct  ngx_buf_s{
    u_char *pos;    //数据处理的开始位置;
    u_char *last;    //数据处理的结束位置;
    off_t     file_pos; //处理文件的文件开始位置
    off_t    file_last;    //处理文件的文件结束位置
    u_char    *start;    //如果ngx_buf_t用于内存,那么指向这个内存的起始地址
    u_char    *end;    //缓冲区内存的末尾
    ngx_buf_tag_t tag;    //缓冲区类型
    ngx_file_t    *file;    //引用的文件
    ngx_buf_t    *shadow;    //影子缓冲区,很少用,设计复杂,简单理解为多个指向同一块内存
    unsigned    temporary:1;    //临时内存标志位,1表示这段内存可以修改
    unsigned    memory:1;    //标志位,为1表示数据在内存中且内存不可以修改
    unsigned    mmap:1;    //1表示这顿啊内存用mmap系统调用映射过来的,不可以被修改
    unsigned    recycled:1;    //1表示可以回收
    unsigned    in_file:1;    //1表示这段缓冲区处理的是文件而不是内存
    unsigned    flush:1;    //1表示需要执行flush操作
    unsigned    sync:1;    //是否需要使用同步方式
    unsigned    last_buf:1;    //表示是否为最后一块缓冲区
    unsigned    last_in_chain:1;    //表示是否是ngx_chain_t中的最后一块缓冲区
    unsigned    last_shadow:1;    //是否是最后一个影子缓冲区,与shadow域配合使用。不建议使用
    unsigned    temp_file:1;    //表示当前缓冲去是否是临时文件
}

5.ngx_chain_t(与ngx_buf_t配合使用,存储向用户发送的HTTP包体,结尾设置为null)

typedef struct ngx_chain_s    ngx_chain_t;
struct ngx_chain_s{
    ngx_buf_t *buf;
    ngx_chain_t *next;
}


以上就是nginx的数据结构。