Nginx基础架构知识

原创:https://blog.csdn.net/ndzjx/article/details/89090981

高度模块化的设计是Nginx的架构基础。

高度抽象的模块接口:ngx_module_t,足够简单,只涉及模块的初始化、退出以及对配置项的处理。

    1)7个回调方法:init_master/init_module/init_process/int_thread/exit_thread/exit_process/exit_master。(事实上,init_master/init_thread/exit_thread目前都没有使用)

    2)ngx_command_t类型的commands数组指定了模块处理配置项的方法。

    3)void *ctx,一般用于表示在不同类型的模块中一种类型模块所具备的通用性接口。

    4)type 模块类型,不同领域的模块按照类型来区别。(NGX_CONF_MODULE配置类型模块是唯一一个只有一个模块的模块类型,ngx_conf_module,是Nginx的最底层模块)

Nginx定义了一种基础类型的模块:核心模块,类型为NGX_CORE_MODULE,目前官方的核心类型模块共有6个具体模块:ngx_core_module/ngx_errlog_module/ngx_events_module/ngx_openssl_module/ngx_http_module/ngx_mail_module

为什么定义核心模块?因为这样可以简化Nginx的设计,使得非模块化的框架代码只关注于如何调用6个核心模块(大部分Nginx模块都是非核心模块)

核心模块接口ngx_core_module_t结构,提供

1)create_conf回调方法,创建存储配置项的数据结构。(只有核心模块有这个方法,其他非核心模块都从属于一个核心模块)

2)init_conf回调方法,用于解析完配置文件后,使用解析出的配置项初始化核心模块的功能。

所有模块间都是分层次、分类别的,官方Nginx共有5大类型的模块:核心模块、配置模块、事件模块、HTTP模块、mail模块。

配置模块、核心模块这两种模块是由Nginx框架代码所定义的,其中配置模块是所有模块的基础。(配置项解析功能)

事件模块、HTTP模块、mail模块3中模块的共性:它们在核心模块中各有1个模块作为自己的代言人,并在同类模块中有一个作为核心业务与管理功能的模块。

事件模块的代言人---ngx_enents_module核心模块,所有事件模块的加载操作不是由Nginx框架完成的,而是由ngx_event_core_module模块负责。

HTTP模块代言人---ngx_http_module核心模块定义的。与事件模块不同的是,这个核心模块还会负责加载所有的HTTP模块,但业务的核心逻辑以及对于具体的请求该选哪一个HTTP模块处理这样的工作,则是由ngx_http_core_module模块决定。

mail模块类似HTTP模块。

配置模块与核心模块都与Nginx框架密切相关,是其他模块的基础。

事件模块则是HTTP模块和mail模块的基础。

事件驱动架构:

    1)事件发生源(网卡、磁盘)产生事件,

    2)由事件收集器(事件模块)来收集、分发事件,

    3)事件处理器注册自己感兴趣的事件,同时会消费这些事件。(所有模块都可能是事件消费者)

Nginx采用完全的事件驱动架构来处理业务。而传统的Web服务器(如Apache)采用的所谓事件驱动往往局限在TCP连接建立、关闭事件上。连接建立后,在其关闭之前的所有操作都不再是事件驱动。

传统Web服务器与Nginx间的重要差别:前者是事件的每个消费者独占一个进程资源,后者是事件消费者(某个模块)只是被事件分发者进程短期调用而已。

弊端:每个事件消费者都不能由阻塞行为,否则将会由于长时间占用事件分发者进程而导致其他事件得不到及时响应。加大了事件消费者程序的开发者的编程难度。

请求的多阶段异步处理,只能基于事件驱动架构实现,就是把一个请求的处理过程按照事件的触发方式划分为多个阶段,每个阶段都可以由事件收集、分发器来触发。

优势:提高网络性能,不会或尽量少的出现进程休眠状况。因为进程休眠,必然减少并发处理事件的数目,降低网络性能,无法满足业务需求将只能增加进程数目,进程数目过多会增加操作系统内核的额外操作:进程间切换。同时,休眠的进程会使进程占用的内存得不到有效释放,导致系统可用内存的下降,影响能够处理的最大并发连接数。

4种方式来划分阶段:

    1)将阻塞进程的方法分解为两个阶段:a)阻塞方法改为非阻塞方法调用 b)增加新的阶段处理非阻塞方法返回的结果

    2)将阻塞方法调用按照时间分解为多个阶段的方法调用。(事件不能被事件收集、分发器处理,如读取文件的调用,10MB均分成1000份,这样读10kb的时间就是可控的。0KB~10KB阶段完成后,怎么进入10KB~20KB阶段?a)网络事件来触发,如通过网络发送它们 b)设置一个简单的定时器 等方法)

    3)进程空转时(如循环检查标志位),使用定时器划分阶段

    4)阻塞方法完全无法划分时,必须使用独立的进程执行这个阻塞方法。这种情况与事件驱动架构相违背,通常由于方法的实现者没有开放非阻塞接口所导致。

Nginx框架中的核心结构体ngx_cycle_t,核心框架围绕着此结构体展开。其中有个动态数组成员listening,每个数组元素都是ngx_listening_t结构体,代表Nginx服务器监听的一个端口。

worker进程的工作方法ngx_worker_process_cycle,会关注4个全局标志位:

sig_atomic_t        ngx_terminate;

sig_atomic_t        ngx_quit;

ngx_uint_t            ngx_exiting;

sig_atomic_t        ngx_reopen;

master进程,管理worker等子进程实现重启服务、平滑升级、更换日志文件、配置文件实时生效等功能,检查7个标志位来决定ngx_master_process_cycle方法的运行。

sig_atomic_t     ngx_reap;

sig_atomic_t     ngx_terminate;

sig_atomic_t     ngx_quit;

sig_atomic_t     ngx_reconfigure;

sig_atomic_t     ngx_reopen;

sig_atomic_t     ngx_change_binary;

sig_atomic_t     ngx_noaccept;

ngx_pool_t内存池。

ngx_palloc 分配地址对齐的内存。

ngx_calloc 分配内存,再调用memset把内存清0

ngx_free 遍历损耗的性能不小(实际只遍历4次),如果不能确定内存确实非常大,最好不要调用。

ngx_pool_t的large链表采用的是首插。

猜你喜欢

转载自blog.csdn.net/ndzjx/article/details/89090981