深入理解Nginx 读书笔记

来源:互联网 发布:云狐网络科技园怎么样 编辑:程序博客网 时间:2024/06/05 04:39
1、Apache 的优点和缺点
    APache有许多优点,如稳定、开源、跨平台等。
    缺点:是一个重量级的、不支持高并发的Web服务器。在Apache服务器上如果有数以万计的并发HTTP请求同时访问,就会导致服务器上消耗大量的内存,操作系统内核对成百上千的Apache进程间切换也会消耗大量CPU资源,导致HTTP请求的平均响应速度降低。

2、Nginx 
   Nginx使用基于事件驱动的架构能够并发处理百万级别的TCP连接,高度模块化的设计和自由许可证使得扩展Nginx功能的第三方库层出不穷,而且优秀的设计带来了极佳的稳定性。

    优点: 更快、高扩展性、高可靠性、低内存消耗、单机支持10万以上的并发连接、热部署、最自由的BSD许可协议。

    Nginx基于事件驱动设计、全异步的网络I/O处理机制、极少的进程间切换

3、运行中Nginx进程间的关系
    Nginx服务器的进程分为master管理进程和worker工作进程。 一般情况下,worker进程的数量与服务器上的CPU核心数相等。每一个worker进程都是繁忙的,真正提供互联网服务,master进程只负责监控管理worker进程。

4、为什么使用master-worker方式
    a. master进程不会对用户请求提供服务,只专注于自己的纯管理工作。当任意一个worker进程出现错误从而导致coredump时,master进程会立刻启动新的worker进程继续服务。
    b.多个worker进程可以提高服务的健壮性;最重要的是充分利用了多核架构,从而实现微观上真正的多核并发处理。
    
    在Apache上每个进程在一个时刻只处理一个请求,因此如果希望Web服务器拥有并发处理的请求数更多,就要把Apache的进程或线程设置的更多,通常会达到一台服务器上拥有几百个工作进程,这样大量的进程间切换将带来大量的系统资源消耗。
    而Nginx一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制。因此,当Nginx上的worker进程数与CPU核心数相等时,进程切换的代价最小。

5、Nginx提供的高级数据结构
    1)ngx_queue_t双向链表
    优点:a.实现了排序功能。 插入排序。
          b.非常轻量级,是一个纯粹的双向链表。他不负责链表元素所占内存的分配
          c.支持两个链表间的合并。

    2)ngx_array_t动态数组, 
    以数组的形式存储元素,并支持在达到数组容量的上限时动态改变数组的大小。
    优点:a.访问速度快;
          b.允许元素个数具备不确定性;
          c.负责元素中用内存的分配,这些内存将由内存池统一管理。

    3)ngx_list_t 单向链表
        相当于动态数组与单向链表的结合体,扩容的时候可以一次性扩容1个数组。

    4)ngx_rbtree_t 红黑树
    
    5)ngx_radix_tree_t基数数

    散列表
    6)ngx_hash_t基本散列表
        解决碰撞问题的方法:分离链接法和开放寻址法
    分离链接法:把散列到同一个槽中的所有元素都放在散列表外的一个链表中。SGI STL使用的就是这种方法。每一个链表为一个桶
    开放寻址法:所有的元素都放在散列表中,当查找一个元素时,要检查规则内所有的表项。
    Nginx的散列表使用的就是开放寻址法。具体使用的是来连续非空槽存储碰撞元素的方法。
    当插入一个元素时,可以按照散列方法找到指定槽,如果该槽非空且其存储的元素与待插入的元素并非同一个元素,则依次检查其后的连续的槽,直到找到一个空槽来放置这个元素为止。查找元素也是类似的方法。

    为什么我看源代码时hash的实现也是通过桶来解决碰撞问题的呢?


6、事件驱动架构
    由一些事件发生源来产生事件,由一个或多个事件收集器来收集、分发事件,然后许多事件处理器会注册自己感兴趣的事件,同时会消费这些事件。
    具体到Nginx这个Web服务器而言,一般会由网卡、磁盘产生事件,事件模块将负责事件的收集、分发操作,而所有的模块都可能是事件消费者,它们首先要向事件模块注册感兴趣的事件类型,这样,在有事件产生时,事件模块会把事件分发到相应的模块中进行处理。
    
    传统Web服务器,采用的事件驱动往往局限在TCP连接建立、关闭事件上,一个连接建立后,在其关闭之前所有的操作都不再是事件驱动,这时就会退化成按序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用着系统资源,直到连接关闭才会释放资源。

    Nginx不会使用进程或线程来作为事件消费者,所谓的事件消费者只能是某个模块。
    传统Web服务器与Nginx间的重要差别:前者是每个事件的消费者独占一个进程资源,后者的事件消费者只是被事件分发者进程短期调用而已。Nginx的处理事件的架构设计使得网络性能、用户感知的请求时延都得到了提升。但是这要求每个事件的消费者都不能有阻塞行为,否则会长事件占用事件分发者进程而导致其他事件得不到及时响应。

7、一个管理进程、多工作进程设计
    Nginx采用一个master管理进程、多个工作进程的设计。其优点:
    a.利用多核系统的并发处理能力。
    b.负载均衡。 多个worker进程间通过进程间通信来实现负载均衡。一个请求到来时更容易分配到负载较轻的worker工作进程中处理。
    c.管理进程会负责监控工作进程的状态,并负责管理其行为。

8、内存池的设计
    为了避免出现内存碎片、减少向操作系统申请内存的次数,Nginx使用了内存池:它并不负责回收内存池中已经分配出的内存。优点在于:把多次向系统的申请内存的操作整合成一次,这样大大减少了CPU资源的消耗,同时减少了内存碎片。
    Nginx为每个请求都分配一个建议的独立内存池,在请求结束时会销毁整个内存池。

9、如何建立连接
    a.首先调用accept方法试图建立连接,如果没有准备好新的连接事件,就直接返回;
    b.设置负载均衡阈值
    c.由连接池中获取一个ngx_connection_t结构体
    e.为这个连接创建内存池
    f.设置新连接套接字的属性为非阻塞
    g.将这个新连接的读事件添加到epoll中监控
    h.调用监听端口上ngx_listening_t结构体的handler方法处理新连接

10、惊群
    定义:多个子进程在同一时刻监听同一个端口。在没有连接时,所有的worker子进程都休眠且等待新连接的系统调用;当用户向服务器发起了连接,内核在收到TCP的SYN包时,会激活所有的休眠worker子进程。最先开始执行accept的子进程可以成功建立新连接,而其他worker子进程都会accept失败,转而继续休眠。
    多余的子进程被唤醒是不必要的,它们被唤醒后占用了系统资源,引发进程上下文切换,增加了系统开销。

    Nginx解决惊群问题的办法:规定在同一时刻只能有唯一一个worker子进程监听Web端口。
    具体方法:每个worker子进程调用ngx_trylock_accept_mutex方法去尝试获得锁。如果没有获得锁,接下来调用事件驱动模块的process_events方法时只能处理已有的连接上的事件;如果获得了锁,调用process_events方法时就会即处理已有连接上的事件,也会处理新连接的事件。
    该什么时候释放锁:当处理完ngx_posted_accept_events队列中的事件后就要立刻释放ngx_accept_mutex锁。

11、如何实现负载均衡
    利用一个负载均衡阈值来管理。这个阈值是进程允许的总连接数的1/8减去空闲连接数。就是当阈值为整数时当前进程将不再处理新连接事件,取而代之的仅仅是阈值值减1。
    因为阈值的初始值为负值:N(总连接数)/8 - N(初始时,空闲值等于进程总允许连接数) = -(7/8)N。所以当Nginx各worker子进程间的负载均衡仅在某个worker子进程处理的连接数达到它最大处理总数的7/8时才会触发,这时该worker进程就会减少处理新连接的机会,其他空闲的worker进程就有机会去处理更多的新连接。

    要利用锁机制来做互斥与同步,既避免监听套接口被同时加入到多个进程的事件监控机制里,又避免监听套接口在某一时刻没有被任何一个进程监控。
    如果进程并没有处于过载状态,那么就会去征用锁,挣锁成功就会把所有监听套接口加入到自身的事件监控机制里;争锁失败就会把监听套接口从自身的事件监控机制里删除。

12、strace+进程号 查看进程中的系统调用
    ltrace+进程号 查看进程中的动态库函数调用
    pstack+进程号 查看进程中函数调用堆栈关系。

13、master管理进程
    master管理进程由ngx_master_process_cycle()函数作为入口。该函数做完信号处理设置后就会调用ngx_start_worker_process()函数用于fork()产生出子进程,子进程作为worker进程执行ngx_worker_process_cycle()函数。
    Nginx是通过信号来管理worker进程的。在ngx_master_process_cycle()函数的主体部分是一个for( ; ; )循环。在无限for(;;)循环内有一个关键的sigsuspend()函数调用,该函数使得master进程大部分时间都处于挂起等待状态,直到master进程接收到信号为止。当接收到信号后就调用ngx_signal_handler()函数处理该信号。然后执行下一次循环。

14、worker进程
    master进程通过ngx_start_worker_process()函数创建worker进程。在worker进程中执行ngx_worker_process_cycle()函数,该函数主体也是一个无限for(;;)循环,持续不断地处理客户端的服务请求。
    worker进程主要与客户端进行数据的交互,因此主要是I/O交互事件,而不是进程信号,因此worker进程的阻塞点是在epoll_wait()或select()等这样的I/O多路复用函数调用处。

15、进程通信
    master进程和worker进程之间 以及 worker进程之间的通信方式是采用的UNIX域套接字。

16、共享内存
    进程之间也可以通过共享内存来实现进程间的通信。
    在多进程之间共享内存要使用锁机制来保证每一时刻只有一个进程能访问该内存空间。

17、slab机制
    Nginx的slab机制与Linux的slab机制基本相同。
    Nginx的slab机制主要是和共享内存一起使用。

18、信号处理
    Nginx通过signal信号的处理支持与用户进行信息交互。

19、当Nginx作为反向代理服务器时的负载均衡
    这里的负载均衡是请求经过Nginx代理服务器后转发给后端服务器处理。如果后端服务器有多台,则需要处理负载均衡。
    Nginx提供了较多的负载均衡策略:加权轮询、IP哈希、fair、一致哈希等。Nginx观法提供了前两个策略。

    a.加权轮询
        直观上理解就是计算各个后端服务器的当前权值,然后选择得分最高的服务器处理当前请求。
    b.IP哈希
        根据客户端的IP地址来对后端服务器做选择。
0 0
原创粉丝点击