nginx 学习记录

使用问题

 open() "/run/nginx.pid" failed (2: No such file or directory)
解决:https://blog.csdn.net/llnara/article/details/8691049 

 

基本架构

进程模型 -- 多进程模型

一个 master 进程,多个 worker 进程. master进程负责管理 workers, 例如接受来自外界信号,发送信号给 worker.worker 负责处理基本的网络事件.如图:

多进程模型的好处:避免上下文切换,每个事件占用内存小.工作进程之间相互独立,可以热升级.

worker 处理事件流程: 当有连接请求过来,每个 worker 都去竞争 accept_mutex ( 互斥锁 ), 拿到锁的 worker 会调用 accept(),与客户进行连接,并且处理事件.(避免了惊群效应

 

I/O 模型  -- epoll (处理网络事件)

I/O 多路复用中的 epoll (同步非阻塞).

运行模式:每个 worker 有自己的 epoll 表,当事件准备好时,就去处理;没准备好就继续放 epoll 表里面.这样,一个进程就能同时处理多个并发请求(请求主动切换,异步事件没准备好的请求自动让出).这样事件处理就非常轻量级,避免了上下文切换.(一个进程)

总:reactor + processes (reactor + 进程模型)

处理信号与定时器

处理信号

对于 nginx 来说,如果 nginx 正在等待事件( epoll_wait 时),如果程序收到信号,在信号处理函数处理完后, epoll_wait 会返回错误,然后程序可再次进入 epoll_wait 调用.

处理定时器

采用时间堆,借助超时时间来实现定时器.

进入 epoll_wait 前,判断 epoll_wait 是否超时,若超时则说明定时器事件到了.这时检查所有的超时事件,把它们设置为超时,再去处理网络事件.

 

基本概念

connection

在 nginx 中, connection 即为 TCP 的连接. nginx 对连接的封装为 ngx_connection_t 结构体.

对 ngx_connection_t 的管理

每个进程有一个连接池来管理连接, 连接池数为 worker_connections. 连接池里保持的并非真实的连接,只是一个 worker_connections 大小的 ngx_connection_t结构的数组. free_connections 管理一个进程中空闲的 ngx_connection_t,每次要用时就从里面取.用完就放回去.

某个进程的最大连接上限

与系统对 fd 的限制无关.最大连接上限即为 worker_connections.

nginx 的最大连接建立数

worker_processes * worker_connections

worker 竞争连接的优先级

ngx_accept_disabled :这个值是 nginx 单进程的所有连接总数的八分之一,再减去剩下的空闲连接数量.

为了避免某个进程的空间连接不多,却又同等竞争得到连接,导致连接得不到处理,又不能转给其他进程处理的情况, nginx 根据ngx_accept_disabled 的值来确定某进程是否参加对 accept_mutex 的竞争.

ngx_accept_disabled = ngx_cycle->connection_n / 8
    - ngx_cycle->free_connection_n;

if (ngx_accept_disabled > 0) { /* 大于0则不竞争,并且自减1 */
    ngx_accept_disabled--;

} else { /* 小于0则竞争 */
    if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
        return;
    }

    if (ngx_accept_mutex_held) {
        flags |= NGX_POST_EVENTS;

    } else {
        if (timer == NGX_TIMER_INFINITE
                || timer > ngx_accept_mutex_delay)
        {
            timer = ngx_accept_mutex_delay;
        }
    }
}

 

request

nginx 中的request 对应 http 请求. nginx 封装的 reuqest 为 ngx_http_request_t.其保存了解析请求和输出响应相关的数据.

对 http request 的处理流程

                                        è¯·æ±å¤çæµç¨

注意点

1.  与 webserver 不同, nginx 对请求的处理是,请求头读取完成之后,就开始进行请求的处理.

2.在 nginx_requst_parse_line 中,采用状态机来解析请求行(提高效率),且在进行 method 的比较时,没有直接使用字符串比较,而是将四个字符转换成一个整型,然后一次比较以减少 cpu 的指令数.

3.ngx_http_core_run_phases 函数将执行多阶段请求处理, nginx 将一个 http 请求的处理分为多个阶段,这个函数就是执行这些阶段来产生数据。

4.nginx 会将整个请求头都放在一个 buffer 里面,这个 buffer 的大小通过配置项 client_header_buffer_size 来设置.在应用场景,我们需要根据实际的需求来调整这些参数,来优化程序。

 

与 request 相关的概念

keepalive

长连接. nginx 为了解决客户端长时间不发送数据,占用连接的情况,使用 keepalive_timeout 来配置解决.(=0表示经过最大等待时间关掉长连接)

pipe

利用一个连接作多次请求.客户端可以在发送一个请求后继续发送其余的请求(要顺序). nginx 依然串行的处理.

nginx 对 pipeling 的处理: nginx 在读取数据时,会将读取的数据放到一个 buffer 里面,如果 nginx 在处理完前一个请求后,如果发现 buffer 里面还有数据,就认为剩下的数据是下一个请求的开始,然后就接下来处理下一个请求,否则就设置 keepalive。

lingering_close ( 延迟关闭 )

 lingering_close 存在的意义就是来读取剩下的客户端发来的数据,所以 nginx 会有一个读超时时间,通过 lingering_timeout 选项来设置,如果在 lingering_timeout 时间内还没有收到数据,则直接关掉连接。 

 

基本数据结构

ngx_str_t ( 字符串 )

ngx_str_t 结构体

typedef struct {
    size_t      len;
    u_char     *data;
} ngx_str_t;

它用 len 来代替 '\0' 来计算长度.不用每次计算长度都算一遍,同时需要某个子字符串时,并且在不更改原串的条件下,只需要直接指向子字符串的开始位置,利用长度来得到即可.而不用 copy 一遍来得到子字符串.

基本操作可看源码, nginx 定义了很多对于字符串处理的函数,使用时尽量用 nginx 的,因为更高效.

 

ngx_pool_t 

一个很重要的结构.它提供了一种机制,帮助管理一系列的资源(如内存,文件等),使得对这些资源的使用和释放统一进行,免除了使用过程中考虑到对各种各样资源的什么时候释放,是否遗漏了释放的担心。

结构

typedef struct ngx_pool_s        ngx_pool_t;

struct ngx_pool_s {
    ngx_pool_data_t       d; /* 存放小块内存块,若还不够,则继续申请空间链接在 d.next上 */
    size_t                max;
    ngx_pool_t           *current;
    ngx_chain_t          *chain;
    ngx_pool_large_t     *large; /* 存放大块内存块的链表 */
    ngx_pool_cleanup_t   *cleanup; /* 存放需要删除的资源的链表 */
    ngx_log_t            *log;  
};

ngx_pool_t相关结构及操作被定义在文件src/core/ngx_palloc.h|c中。

 

ngx_array_t

ngx_array_t是nginx内部使用的数组结构。

结构:

typedef struct ngx_array_s       ngx_array_t;
struct ngx_array_s {
    void        *elts; /* 指向实际的数据存储区域。 */
    ngx_uint_t   nelts; /* 数组实际元素个数。 */
    size_t       size; /* 数组单个元素的大小,单位是字节. */
    ngx_uint_t   nalloc; /* 数组的容量。表示该数组在不引发扩容的前提下,可以最多存储的元素的个数。当nelts增长到达 nalloc 时,如果再往此数组中存储元素,则会引发数组的扩容。数组的容量将会扩展到原有容量的2倍大小。实际上是分配新的一块内存,新的一块内存的大小是原有内存大小的2倍。原有的数据会被拷贝到新的一块内存中。 */
    ngx_pool_t  *pool; /* 该数组用来分配内存的内存池。 */
};

ngx_array_t的定义位于src/core/ngx_array.c|h里面。

 

ngx_hash_t

ngx_hash_t 是 nginx 自己的 hash 表的实现。使用拉链法解决冲突.它的实现和一般实现差不多.

ngx_hash_init_t 结构 (初始化 hash 表的一些信息)


typedef struct {
    ngx_hash_t       *hash; /* 指向的 hash 表 */
    ngx_hash_key_pt   key; /* hash 函数 */

    ngx_uint_t        max_size; /* hash 表的最大桶数 */
    ngx_uint_t        bucket_size; /* 桶的最大容量 */

    char             *name; /* hash 表的名字 */
    ngx_pool_t       *pool; /* 分配给 hash 表内存的 pool */
    ngx_pool_t       *temp_pool; /* 该hash表使用的临时pool,在初始化完成以后,该pool可以被释放和销毁掉。 */
} ngx_hash_init_t;

特点

1.ngx_hash_t 不像其他的hash表的实现,可以插入删除元素,它只能一次初始化,就构建起整个 hash 表以后,既不能再删除,也不能在插入元素了。

2.ngx_hash_t 的开链并不是真的开了一个链表,实际上是开了一段连续的存储空间,几乎可以看做是一个数组。这是因为ngx_hash_t 在初始化的时候,会经历一次预计算的过程,提前把每个桶里面会有多少元素放进去给计算出来,这样就提前知道每个桶的大小了。那么就不需要使用链表,一段连续的存储空间就足够了。这也从一定程度上节省了内存的使用。

 

ngx_hash_wildcard_t

其处理带有通配符的域名的匹配问题.其可以支持两种类型的带有通配符的域名。一种是通配符在前的,例如:“*.abc.com”,也可以省略掉星号;第二种是通配符在末尾的,例如:“mail.xxx.*”,通配符在末尾的不可以被省略。

 

ngx_hash_combined_t

组合型 hash 表.

/* 组合型 hash 表,包含一个普通表,一个包含向前通配符的表,一个包含向后通配符的表 */
typedef struct {
    ngx_hash_t            hash;
    ngx_hash_wildcard_t  *wc_head;
    ngx_hash_wildca rd_t  *wc_tail;
} ngx_hash_combined_t;

 

ngx_hash_keys_arrays_t

为了方便构造前面几种 hash 表而提供的辅助类型.

结构

/* 为了方便构造三种 hash 表而提供的辅助类型 */
typedef struct {
    ngx_uint_t        hsize; /* 将要构造的 hash 表的桶的个数 */

    ngx_pool_t       *pool; /* 构造这些 hash 表所使用的 pool */
    ngx_pool_t       *temp_pool; /* 所使用的临时 pool ,用完后可销毁 */

    ngx_array_t       keys; /* 存放所有非通配符 key 的数组 */
    ngx_array_t      *keys_hash; /* 这是个二维数组,第一维代表的是 bucket,第二维度代表的是
                                    所有的 key 算出来的的 hash 值对 hsize 取模后得到的 key 
                                    .该值用来保存和检测是否有冲突的 key 值. */

    ngx_array_t       dns_wc_head; /* 存放向前通配符 key 处理完后得到的值 */
    ngx_array_t      *dns_wc_head_hash; /* 该值在调用的过程中用来保存和检测是否有冲突的前向通配符的key值,
                                           也就是是否有重复。 */

    ngx_array_t       dns_wc_tail; /* 保存向后通配符的 key 处理完后得到的值. */
    ngx_array_t      *dns_wc_tail_hash; /* 同上 */
} ngx_hash_keys_arrays_t;

ngx_chain_t

这是从别的 filter/handler 传递过来的数据(实际为需发送的 http response ),其为链表形式.

结构

struct ngx_chain_s {
    ngx_buf_t    *buf; /* 存放的实际数据 */
    ngx_chain_t  *next; /* 下一个数据节点 */
};

ngx_buf_t

一种抽象的数据结构,它代表某种具体的数据.例如指向某个缓冲区,或是某个文件.

结构

struct ngx_buf_s {
    u_char          *pos;
    u_char          *last;
    off_t            file_pos;
    off_t            file_last;

    u_char          *start;         /* start of buffer */
    u_char          *end;           /* end of buffer */
    ngx_buf_tag_t    tag;
    ngx_file_t      *file;
    ngx_buf_t       *shadow;


    /* the buf's content could be changed */
    unsigned         temporary:1;

    /*
     * the buf's content is in a memory cache or in a read only memory
     * and must not be changed
     */
    unsigned         memory:1;

    /* the buf's content is mmap()ed and must not be changed */
    unsigned         mmap:1;

    unsigned         recycled:1;
    unsigned         in_file:1;
    unsigned         flush:1;
    unsigned         sync:1;
    unsigned         last_buf:1;
    unsigned         last_in_chain:1;

    unsigned         last_shadow:1;
    unsigned         temp_file:1;

    /* STUB */ int   num;
};

 

ngx_list_t

并非寻常的数组,每个节点都为一个数组,整体为链表结构.添加的时候,在最尾部的节点加入元素,若最尾部的节点已满,则向 list 添加新节点.

结构

/* 具体节点结构 */
struct ngx_list_part_s {
    void             *elts; /* 节点中存放具体元素的起始地址 */
    ngx_uint_t        nelts; /* 节点中已有元素个数 */
    ngx_list_part_t  *next; /* 指向下一个节点 */
};

/* list 结构 */
typedef struct {
    ngx_list_part_t  *last; /* 指向链表的最后一个节点 */
    ngx_list_part_t   part; /* 指向链表第一个存放元素的节点 */
    size_t            size; /* 每个具体元素所需内存大小 */
    ngx_uint_t        nalloc; /* 每个节点的所含的数组的固定大小 */
    ngx_pool_t       *pool; /* 分配内存的 pool */
} ngx_list_t;

定义在src/core/ngx_list.h|c文件中

 

ngx_queue_t

nginx 中的双向链表

结构

/* 双向链表,数据并不放在这里面,链表放在数据节点里面 */
struct ngx_queue_s {
    ngx_queue_t  *prev; 
    ngx_queue_t  *next;
};

定义在 ngx_queue.h|c 里面

 

nginx的配置系统

主配置文件 nginx.conf .在nginx.conf中,包含若干配置项。每个配置项由配置指令和指令参数2个部分构成。指令参数也就是配置指令对应的配置值。

当前 nginx 支持配置指令上下文(作用域)

main:nginx在运行时与具体业务功能(比如http服务或者email服务代理)无关的一些参数,比如工作进程数,运行的身份等。
http:提供http服务相关的一些配置参数。例如:是否使用keepalive啊,是否使用gzip进行压缩等。
server: http服务上支持若干虚拟主机。每个虚拟主机一个对应的server配置项,配置项里面包含该虚拟主机相关的配置。在提供mail服务的代理时,也可以建立若干server.每个server通过监听的地址来区分。
location: http服务中,某些特定的URL对应的一系列配置项。
mail: 实现email相关的SMTP/IMAP/POP3代理时,共享的一些配置项(因为可能实现多个代理,工作在多个监听地址上)。

 

(下面的笔记比较简单,潦草,因为看的时候没有记,只把一些比较印象深刻的点记一下)

nginx的模块化体系结构

event 模块

一般使用 EPOLLET 模式(linux),在处理 epoll 事件的时候,先获得 accept 锁, 获得之后不在获得锁的时候处理事件,只是接受事件( 其中某个事件除外 ), 然后放掉锁后再处理事件,以便其他的进程更快地接受连接,提高效率.

accept 锁

accept 锁是为了解决惊群效应的.避免所有进程同时争夺一个连接,所以让线程先尝试获得锁,不能获得立即返回,直到有下一个就绪事件再去争夺.同时针对某些进程可能处理的 fd 太多,不能再处理连接的情况,为了让其他的进程得到锁,它会调整进程获得锁的机会概率.

定时器的处理

定时器事件存储结构使用红黑树,时间间隔最小的在树顶,每次取出后处理完,再找出其他超时的,直到全部处理完.

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值