Nginx 模块综述
Nginx 所有的代码都是以模块的新式组织的,包括核心模块和功能模块。Nginx加载模块的时候不想Apache一样动态加载,它是直接被编译到二进制执行文件中,所以,如果想要加载新的模块,需要我们重新编译Nginx源码。比如:
./configure --with-http_flv_module
执行上述编译选项后,就可以生成http_flv功能模块了。根据模块的功能性质,Nginx的模块大致可以分为以下四类:
1. handlers:协同完成客户端请求的处理、产生响应数据,比如ngx_http_rewrite_module模块,用于处理客户端请求的地址重写,ngx_http_static_module模块负责处理客户端的静态页面请求,ngx_http_log_module模块,负责记录请求访问日志。
2. filters:对handlers产生的响应数据作各种过滤处理,比如模块ngx_http_not_modified_filter_module,对响应数据进行过滤检测,如果通过时间戳判断出前后响应没有变化,就响应304,让客户端使用本地缓存。
3. upstream:如果存在后端真实服务器,Nginx可以利用upstream模块充当反向代理(例如和uwsgi 一起搭建flask框架web程序),对客户端发起的请求值负责转发。
4. load-balance:在Nginx充当中间代理角色时,由于后端真实服务器往往多余一个,对于某一次客户端的请求,如何选择对应的后端真实服务器来进行处理,实现负载均衡算法。
- Handler 模块
对于客户端http请求的处理,Nginx为了获得更强的控制能力,Nginx将整个过程细分为多个阶段,每个阶段可以有多个回调函数来处理,这样以来,当我们在处理Handlers模块是,必须把模块功能处理函数挂在正确的阶段点上。例如:http请求的整个过程一共被分为11个阶段,每个阶段对应的处理功能都比较单一,这样能尽量让Nginx模块代码更为内聚。如下图所示为http请求对应的11个阶段:
序号0表示的阶段是指当Nginx成功接收到一个客户端请求后(accept返回FD之后),针对该请求所做的第一个实际工作就是读取客户端发过来的请求头内容。并调用相应的回调函数进行解析。
序号1表示的阶段和3阶段都属于地址重写阶段,前者用于server上下文的重写、后者location上下文的重写,先做server的定位,然后调用3阶段的回调函数作location的定位。
序号2表示的阶段不执行任何回调函数,因为它的任务主要是查找Location的位置,位置找到后,进入3阶段读取配置值,重写地址。
序号4主要是作安全检查的,即查看是否做了过多的内部跳转(比如地址重写、redirect等),我们不能让对一个请求的处理在Nginx内部跳转多次,这样非常消耗性能。Nginx规定跳转次数最大值为10。
序号5、6、7主要是做访问权限检查工作,如果当前请求没有访问权限,那么直接返回状态403,序号10是日志模块处理的阶段。
阶段-回调函数对应表如下所示:
- Filter 模块
对于http请求处理handlers产生的响应内容,在输出到客户端之前需要作过滤处理,这些过滤处理对于完成功能的增强实现与性能的提升是非常有必要的。由于响应数据包括响应头和响应体,所以与此相对应,Filter模块必须提供处理header、body的响应函数, 所有的header过滤模块和body过滤模块各自形成自己的过滤链。如果头过滤失败,响应体过滤可以不必进行。
- Upstream模块
Upstream模块与具体的协议无关,其除了支持HTTP外,还支持包括FASTCGI、UWSGI等在内的多种协议。在Nginx配置文件中location设置了upstream,即表示对该location的请求需要后端真实服务器来处理。upstream模块的优先级最高,所以一旦进入upstream模块之后,再调用其他模块,获取配置参数如server、location,不过这一动作由upstream中的回调函数完成。对于任何一个Upstream模块而言,最核心的实现是7个回调函数。交互过程如下所示:
其中create_request的功能是根据Nginx与后端服务器通信协议,将客户端的HTTP请求信息转化为对应的发送到后端的真实请求,这也就是为什么后端获取变量的方式都是:request.Form,request.json等。process_headers的功能是根据Nginx与后端服务器通信协议,将后端服务其返回的头信息转换为HTTP响应头。input_filter_init以及input_filter则是处理后端服务其返回的响应体。
- Load-balance模块
Load-balance模块是一个辅助模块,与前面介绍的模块不一样,它主要为Upstream模块服务,目标单一,即如何从后台服务器中选择一台合适的服务其来处理当前请求。要实现Load-balance模块,需要实现如下的4个回调函数:
ngx_http_upstream_init_round_robin:解析配置文件过程中被调用,根据upstream里的各个配置变量作初始准备工作,配置文件解析完接不在被调用
ngx_http_upstream_init_round_robin_peer():在每次Nginx准备转发客户端请求到后端服务器前都会调用该函数,该函数为选择合适的后端服务器作初始准备工作。
ngx_http_upstream_get_round_robin_peer():该函数实现具体的调度逻辑,加权选择当前权值最高的后端服务器
ngx_http_upstream_free_round_robin_peer():跟新相关数值,如权重,负载。
Nginx默认采用round-robin加权算法,如果选择其他算法,比如IP_hash算法,需要在upstream中配置说明。加权轮询调度算法思路如下:
1 假设有一组服务器S = {S0, S1, …, Sn-1},W(Si)表示服务器Si的权值,一个 2 指示变量i表示上一次选择的服务器,指示变量cw表示当前调度的权值,max(S) 3 表示集合S中所有服务器的最大权值,gcd(S)表示集合S中所有服务器权值的最大 4 公约数。变量i初始化为-1,cw初始化为零。 5 6 while (true) { 7 i = (i + 1) mod n; 8 if (i == 0) { 9 cw = cw - gcd(S); 10 if (cw <= 0) { 11 cw = max(S); 12 if (cw == 0) 13 return NULL; 14 } 15 } 16 if (W(Si) >= cw) 17 return Si; 18 }
IP_hash算法流程如下:
int hash = 89; for(int i = 0; i < 3; i++) { hash = (hash * 113 + rand_num[i]) % 6271; //hash运算 } hash = hash % peer_number;//peer_number是后端server数目 result[hash]++; //统计hash值命中