Nginx学习笔记
Nginx学习笔记
学习时间:2023年1月19日
学习来源:深入理解Nginx——模块开发与架构解析第二版 陶辉著
1 安装
1.1 准备工作
1.1.1 Linux系统内核版本
首先我们需要一个内核为Linux 2.6及以上版本的操作系统,因为Linux 2.6
及以上内核才支持epoll
,而在Linux上使用select或poll来解决事件的多路复用,是无法解决高并发压力问题的。
使用命令查看电脑以及操作系统的相关信息。
1 | uname [-amnrsv][--help][--version] |
-a或--all
:显示全部的信息。-m或--machine
:显示电脑类型。-n或--nodename
:显示在网络上的主机名称。-r或--release
:显示操作系统的发行编号。-s或--sysname
:显示操作系统名称。-v
:显示操作系统的版本。--version
:显示版本信息。
1 | [root@HongyiZeng local]# uname -m |
1.1.2 必备软件
GCC
编译器PCRE
库:PCRE(Perl Compatible Regular Expressions,Perl兼容正则表达式)是由Philip Hazel开发的函数库,目前为很多软件所使用,该库支持正则表达式。Nginx的HTTP模块要靠它来解析正则表达式。
1 | yum install -y pcre pcre-devel |
zlib
库:zlib库用于对HTTP包的内容做gzip
格式的压缩。
1 | yum install -y zlib zlib-devel |
1.1.3 磁盘目录
需要在Linux文件系统上准备以下目录:
- Nginx源代码存放目录:该目录用于放置从官网上下载的Nginx源码文件,以及第三方或我们自己所写的模块源代码文件。
- Nginx编译阶段产生的中间文件存放目录:该目录用于放置在configure命令执行后所生成的源文件及目录,以及make命令执行后生成的目标文件和最终连接成功的二进制文件。默认情况下,configure命令会将该目录命名为objs,并放在Nginx源代码目录下。
- 部署目录:该目录存放实际Nginx服务运行期间所需要的二进制文件、配置文件等。默认情况下,该目录为
/usr/local/nginx
。 - 日志文件存放目录:日志文件通常会比较大,当研究Nginx的底层架构时,需要打开debug级别的日志,这个级别的日志非常详细,会导致日志文件的大小增长得极快,需要预先分配一个拥有更大磁盘空间的目录。
1.2 编译安装
到官网上下载源码包,上传至linux服务器。在usr/local
下创建文件夹nginx
并进入:
1 | cd /usr/local/ |
将源码包解压缩到这里:
1 | [root@HongyiZeng nginx]# tar -zxvf ../nginx-1.22.0.tar.gz -C ./ |
进入解压缩后得到的文件夹,并编译安装:
1 | [root@HongyiZeng nginx]# cd nginx-1.22.0/ |
命令作用:
configure
命令做了大量的幕后工作,包括检测操作系统内核和已经安装的软件,参数的解析,中间目录的生成以及根据各种参数生成一些C源码文件、Makefile
文件等。make
命令根据configure命令生成的Makefile文件编译Nginx工程,并生成目标文件、最终的二进制文件。make install
命令根据configure执行时的参数将Nginx部署到指定的安装目录,包括相关目录的建立和二进制文件、配置文件的复制。
解压缩得到的文件有:
1 | [root@HongyiZeng nginx-1.22.0]# ls |
Nginx运行目录为/usr/local/nginx
,目录结构为:
1 | |---sbin |
1.3 configure详解
1.3.1 命令参数
使用help命令可以查看configure包含的参数。
1 | ./configure --help |
configure的参数分为了四大类型,只列出重要的。
① 路径相关
参数名称 | 含义 | 默认值 |
---|---|---|
--prefix=PATH |
Nginx安装后的根目录 | /usr/local/nginx |
--sbin-path=PATH |
可执行文件的存放路径 | <prefix>/sbin/nginx |
--conf-path=PATH |
配置文件的存放路径 | <prefix>/conf/nginx.conf |
--error-log-path=PATH |
error日志文件的存放路径 | <prefix>/logs/error.log |
--pid-path=PATH |
pid文件的存放路径。这个文件里仅以ASCII码存放着Nginx master的进程ID,有了这个进程ID,在使用命令行(例如nginx -s reload )通过读取master进程ID 向master进程发送信号时,才能对运行中的Nginx服务产生作用 |
<prefix>/logs/nginx.pid |
② 编译相关
略
③ 依赖软件
略
④ 模块相关
除了少量核心代码外,Nginx完全是由各种功能模块组成的。这些模块会根据配置参数决定自己的行为,因此,正确地使用各个模块非常关键。在configure的参数中,我们把它们分为五大类。
- 事件模块
- 默认编译进入Nginx的HTTP模块
- 默认不会编译进入Nginx的HTTP模块
- 邮件代理服务器相关的mail模块
- 其他模块
1.3.2 执行流程
configure由Shell脚本编写,中间会调用<nginx-source>/auto/
目录下的脚本。
1.3.3 生成的文件
当configure执行成功时会生成objs目录,并在该目录下产生以下目录和文件:
1 | |---ngx_auto_headers.h |
1.4 命令行控制
在Linux中,需要使用命令行来控制Nginx服务器的启动与停止、重载配置文件、回滚日志文件、平滑升级等行为。
配置环境变量
默认情况下,二进制可执行文件路径为usr/local/nginx/sbin/nginx
。在执行nginx命令时,为简化输入,可以将其加入到环境变量中。这里直接加入到所有用户的环境变量中:
1 | vim /etc/profile |
在文件最后加入:
1 | export NGINX_HOME=/usr/local/nginx # nginx的安装目录 |
使其生效:
1 | source /etc/profile |
常用命令
- 默认方式启动
直接执行Nginx二进制程序:
1 | nginx |
这时,会读取默认路径下的配置文件:usr/local/nginx/conf/nginx.conf
- 另行指定配置文件的启动方式
使用-c
参数指定配置文件。例如:
1 | nginx -c tmpnginx.conf |
- 测试配置信息是否有错误
在不启动Nginx的情况下,使用-t
参数仅测试配置文件是否有错误。例如:
1 | [root@HongyiZeng nginx]# nginx -t |
- 版本信息
1 | nginx -v |
- 快速停止服务
使用-s stop
可以强制停止Nginx服务。-s
参数其实是告诉Nginx程序向正在运行的Nginx服务发送信号量,Nginx程序通过nginx.pid
文件中得到master进程的进程ID,再向运行中的master进程发送TERM
信号来快速地关闭Nginx服务。
1 | nginx -s stop |
- 优雅停止服务
如果希望Nginx服务可以正常地处理完当前所有请求再停止服务,那么可以使用-s quit
参数来停止服务。
1 | nginx -s quit |
该命令与快速停止Nginx服务是有区别的。当快速停止服务时,worker进程与master进程在收到信号后会立刻跳出循环,退出进程。而优雅地停止服务时,首先会关闭监听端口,停止接收新的连接,然后把当前正在处理的连接全部处理完,最后再退出进程。
- 使运行中的Nginx重读配置项并生效
使用-s reload
参数可以使运行中的Nginx服务重新加载nginx.conf
文件
1 | nginx -s reolad |
事实上,Nginx会先检查新的配置项是否有误,如果全部正确就以优雅的方式关闭,再重新启动Nginx来实现这个目的。类似的,-s
是发送信号,仍然可以用kill命令发送HUP
信号来达到相同的效果。
- 查看nginx的进程号
1 | ps -ef grep | nginx |
-e
:表示列出全部的进程
-f
:显示全部的列(显示全字段)
2 配置
2.1 进程间关系
在正式提供服务的产品环境下,部署Nginx时都是使用一个master进程来管理多个worker进程,一般情况下,worker进程的数量与服务器上的CPU核心数相等。每一个worker进程都是繁忙的,它们在真正地提供互联网服务,master进程则很“清闲”,只负责监控管理worker进程。
worker进程之间通过共享内存、原子操作等一些进程间通信机制来实现负载均衡等功能。
2.2 配置通用语法
Nginx的配置文件nginx.conf
其实是一个普通的文本文件。例如:
1 | user nobody; |
2.2.1 块配置项
块配置项由一个块配置项名和一对大括号组成。具体示例如下:
1 | events { # 块配置项 |
块配置项可以嵌套。内层块直接继承外层块,例如,上例中,server块里的任意配置都是基于http块里的已有配置的。
当内外层块中的配置发生冲突时,究竟是以内层块还是外层块的配置为准,取决于解析这个配置项的模块。例如,上例在http模块中已经打开了gzip on;
,但其下的location/webstatic
又把gzip关闭了:gzip off;
,最终,在/webstatic的处理模块中,gzip模块是按照gzip off
来处理请求的。
2.2.2 配置项语法格式
1 | 配置项名 配置项值 配置项值 配置项值 ...; |
配置项值之间由空格符来分隔,每行配置的结尾需要加上分号。
2.2.3 配置项的单位
当指定空间大小时,可以使用的单位包括:
- K或者k千字节(KB)
- M或者m兆字节(MB)
例如:
1 | gzip_buffers 48k; |
2.3 基本配置
Nginx在运行时,至少必须加载几个核心模块和一个事件类模块。这些模块运行时所支持的配置项称为基本配置,即所有其他模块执行时都依赖的配置项。
2.3.1 调试进程和定位问题的配置项
- 是否以守护进程方式运行Nginx
语法:daemon on|off;
默认:daemon on;
守护进程(daemon)是脱离终端并且在后台运行的进程。它脱离终端是为了避免进程执行过程中的信息在任何终端上显示,这样一来,进程也不会被任何终端所产生的信息所打断。Nginx毫无疑问是一个需要以守护进程方式运行的服务,因此,默认都是以这种方式运行的。
- 是否以主从方式工作
语法:master_process on|off;
默认:master_process on;
- error日志的设置
语法: error_log pathfile level;
默认: error_log logs/error.log error;
2.3.2 正常运行的配置项
- 嵌入其他配置文件
语法: include pathfile;
include配置项可以将其他配置文件嵌入到当前的nginx.conf文件中,它的参数既可以是绝对路径,也可以是相对路径(相对于Nginx的配置目录,即nginx.conf所在的目录),例如:
1 | include mime.types; |
- pid文件的路径
语法: pid path/file;
默认: pid logs/nginx.pid;
保存master进程ID的pid文件存放路径。默认与configure执行时的参数--pid-path
所指定的路径是相同的,也可以随时修改,但应确保Nginx有权在相应的目标中创建pid文件,该文件直接影响Nginx是否可以运行。
- Nginx worker进程运行的用户及用户组
语法:user username[groupname];
默认:user nobody nobody;
user用于设置master进程启动后,fork出的worker进程运行在哪个用户和用户组下。当按照user username;
设置时,用户组名与用户名相同。
若用户在configure命令执行时使用了参数--user=username
和--group=groupname
,此时nginx.conf将使用参数中指定的用户和用户组。
2.3.3 优化性能的配置项
- Nginx worker进程个数
语法:worker_processes number;
默认:worker_processes 1;
在master/worker运行方式下,定义worker进程的个数。
worker进程的数量会直接影响性能。
每个worker进程都是单线程的进程,它们会调用各个模块以实现多种多样的功能。如果这些模块确认不会出现阻塞式的调用,那么,有多少CPU内核就应该配置多少个进程;反之,如果有可能出现阻塞式调用,那么需要配置稍多一些的worker进程。
例如,如果业务方面会致使用户请求大量读取本地磁盘上的静态资源文件,而且服务器上的内存较小,以至于大部分的请求访问静态资源文件时都必须读取磁盘(磁头的寻址是缓慢的),而不是内存中的磁盘缓存,那么磁盘I/O调用可能会阻塞住worker进程少量时间,进而导致服务整体性能下降。
多worker进程可以充分利用多核系统架构,但若worker进程的数量多于CPU内核数,那么会增大进程间切换带来的消耗(Linux是抢占式内核)。一般情况下,用户要配置与CPU内核数相等的worker进程,并且使用下面的worker_cpu_affinity
配置来绑定CPU内核。
- 绑定Nginx worker进程到指定的CPU内核
语法:worker_cpu_affinity cpumask[cpumask...]
假定每一个worker进程都是非常繁忙的,如果多个worker进程都在抢同一个CPU,那么这就会出现同步问题。反之,如果每一个worker进程都独享一个CPU,就在内核的调度策略上实现了完全的并发。
1 | worker_processes 4; |
- Nginx worker进程优先级设置
语法:worker_priority nice;
默认:worker_priority 0;
在Linux或其他类UNIX操作系统中,当许多进程都处于可执行状态时,将按照所有进程的优先级来决定本次内核选择哪一个进程执行。进程所分配的CPU时间片大小也与进程优先级相关,优先级越高,进程分配到的时间片也就越大(例如,在默认配置下,最小的时间片只有5ms,最大的时间片则有800ms)。这样,优先级高的进程会占有更多的系统资源。
优先级由静态优先级和内核根据进程执行情况所做的动态调整(目前只有±5的调整)共同决定。nice值是进程的静态优先级,它的取值范围是–20~+19,–20是最高优先级,+19是最低优先级。因此,如果用户希望Nginx占有更多的系统资源,那么可以把nice值配置得更小一些,但不建议比内核进程的nice值(通常为–5)还要小。
2.3.4 事件类配置项
暂略
2.4 配置静态Web服务器
静态Web服务器的主要功能由ngx_http_core_module
模块(HTTP框架的主要成员)实现。
除了2.3节提到的基本配置项外,一个典型的静态Web服务器还会包含多个server
块和location
块,例如:
1 | http { |
所有的HTTP配置项都必须直属于http块、server块、location块、upstream块或if块等,同时,在描述每个配置项的功能时,会说明它可以在上述的哪个块中存在,因为有些配置项可以任意地出现在某一个块中,而有些配置项只能出现在特定的块中。
Nginx为配置一个完整的静态Web服务器提供了非常多的功能,下面会把这些配置项分为以下8类进行详述。
2.4.1 虚拟主机与请求转发
由于IP地址的数量有限,因此经常存在多个主机域名对应着同一个IP地址的情况,这时在nginx.conf中就可以按照server_name
(对应用户请求中的主机域名)并通过server块来定义虚拟主机,每个server块就是一个虚拟主机,它只处理与之相对应的主机域名请求。
① listen
设置nginx监听的端口
语法:listen address:port[default_server | [backlog=num | rcvbuf=size | sndbuf=size | accept_filter=filter | deferred | bind | ipv6only=[on|off] | ssl]];
默认:listen 80;
配置块:server
示例:
1 | listen 127.0.0.1:8000; |
可用参数含义:
default
:将所在的server块作为整个Web服务的默认server块。如果没有设置这个参数,那么将会以在nginx.conf中找到的第一个server块作为默认server块。已经废弃。default_server
:同上。backlog=num
:表示TCP中backlog队列的大小。默认为–1,表示不予设置。在TCP建立三次握手过程中,进程还没有开始处理监听句柄,这时backlog队列将会放置这些新连接。可如果backlog队列已满,还有新的客户端试图通过三次握手建立TCP连接,这时客户端将会建立连接失败。deferred
:在设置该参数后,若用户发起建立连接请求,并且完成了TCP的三次握手,内核也不会为了这次的连接调度worker进程来处理,只有用户真的发送请求数据时(内核已经在网卡中收到请求数据包),内核才会唤醒worker进程处理这个连接。这个参数适用于大并发的情况下,它减轻了worker进程的负担。当请求数据来临时,worker进程才会开始处理这个连接。只有确认上面所说的应用场景符合自己的业务需求时,才可以使用deferred配置。ssl
:在当前监听的端口上建立的连接必须基于SSL协议。
② server_name
设置主机名。
语法: server_name name [...];
默认: server_name "";
配置块: server
server_name后可以跟多个主机名称,如:
1 | server_name www.testweb.com download.testweb.com; |
在开始处理一个HTTP请求时,Nginx会取出header头中的Host,与每个server中的server_name进行匹配,以此决定到底由哪一个server块来处理这个请求。有可能一个Host与多个server块中的server_name都匹配,这时就会根据匹配优先级来选择实际处理的server块。
Nginx正是使用server_name配置项针对特定Host域名的请求提供不同的服务,以此实现虚拟主机功能。
③ server_name_in_redirect
重定向主机名称的处理
语法: server_name_in_redirect on|off;
默认: server_name_in_redirect on;
配置块: http、server或者location
该配置需要配合server_name使用。在使用on打开时,表示在重定向请求时会使用server_name里配置的第一个主机名代替原先请求中的Host头部,而使用off关闭时,表示在重定向请求时使用请求本身的Host头部。
④ location
语法: location[=|~|~*|^~|@]/uri/{...}
配置块: server
location会尝试根据用户请求中的URI来匹配上面的/uri表达式,如果可以匹配,就选择location块中的配置来处理用户请求。
=
表示把URI作为字符串,以便与参数中的uri做完全匹配。例如:
1 | location = / { |
~
表示匹配URI时是字母大小写敏感的~*
表示匹配URI时忽略字母大小写问题^~
表示匹配URI时只需要其前半部分与uri参数匹配即可。例如:
1 | location ^~ images { |
@
表示仅用于Nginx服务内部请求之间的重定向,带有@的location不直接处理用户请求
在uri参数里是可以用正则表达式的,例如:
1 | location ~* \.(gif|jpg|jpeg)$ { |
在最后一个location中使用/
作为参数,它会匹配所有的HTTP请求,这样就可以表示如果不能匹配前面的所有location,则由/
这个location处理(默认处理)。例如:
1 | location / { |
2.4.2 文件路径的定义
URL
:统一资源定位符(Uniform Resource Locator
,缩写:URL)。是对资源的引用和访问该资源的方法。俗称网址,就是浏览器地址栏里面的。
组成部分:protocol://hostname:port/path?query#fragment
- 协议:通常是 https 或 http,一种告诉浏览器或者设备如何访问资源的方法,当然还有其他的协议,如 ftp 、mailto 或者 file
- 接下来是
://
- 主机名:表示 IP 地址的注册名称(域名) 或 IP 地址,用于识别连接到网络的设备的数字标识符
- 可选的端口号
- 路径:可以引用文件系统路径,通常作为一个代码段使用
- 参数:以问号开头的可选查询参数,其中多个参数用
&
连接 - 锚定:用于为页面上的标题提供快速链接,如锚点链接。
示例:
1 | http://www.aspxfans.com:8080/news/day01/index.asp?boardID=5&pwd=24618&page=1#name |
- 协议 :
http:
- 主机名:
www.aspxfans.com
- 端口:
:8080
- 目录:
/news/day01/
- 文件:
index.asp
- 参数:
boardID=5&pwd=24618&page=1
- 锚定:
name
URI
:统一资源标志符(Uniform Resource Identifier
,缩写:URI),提供了一种识别资源的方法。但与 URL 不同的是,URI 不提供定位所述资源的方法。
URI 的最常见的形式是统一资源定位符(URL),经常指定为非正式的网址。由此,可以看出 URI 是 URL 的超集,并且每个 URL 本质上也是一个 URI。
① root
作用:以root方式设置资源路径。
语法:root path;
默认:root html;
配置块:http、server、location、if
例如,定义资源文件相对于HTTP请求的根目录:
1 | location /foo { |
以请求http://example.com/foo/bar/hello.html
为例。匹配到/foo
,所以实际访问的路径为/home/hfy/foo/bar/hello.html
② index
作用:访问首页。
语法:index file…;`
默认:index index.html;
配置块:http、server、location
有时,访问站点时的URI是/
,这时一般是返回网站的首页。index后可以跟多个文件参数,Nginx将会按照顺序来访问这些文件,例如:
1 | location / { |
当客户端访问/
时,Nginx首先会尝试访问path/index.html
文件,如果可以访问,就直接返回文件内容结束请求,否则再试图返回path/index.php
文件的内容,依此类推。
③ error_page
作用:根据HTTP返回码重定向页面
语法: error_page code[code...][=|=answer-code]uri|@named_location
配置块:http、server、location、if
当对于某个请求返回错误码时,如果匹配上了error_page中设置的code,则重定向到新的URI中。例如:
1 | error_page 404 404.html; |
2.4.3 MIME类型的设置
暂略
2.4.4 配置示例
1 | user root; |
2.5 配置反向代理服务器
2.5.1 简介
反向代理(reverse proxy)方式是指用代理服务器来接受Internet上的连接请求,然后将请求转发给内部网络中的上游服务器,并将从上游服务器上得到的结果返回给Internet上请求连接的客户端,此时代理服务器对外的表现就是一个Web服务器。充当反向代理服务器也是Nginx的一种常见用法(反向代理服务器必须能够处理大量并发请求)。
Nginx作为反向代理服务器时转发请求的流程:
2.5.2 负载均衡的基本配置
作为代理服务器,一般都需要向上游服务器的集群转发请求。这里的负载均衡是指选择一种策略,尽量把请求平均地分布到每一台上游服务器上。
① upstream块
语法:upstream name{...}
配置块:http
upstream块定义了一个上游服务器的集群,便于反向代理中的proxy_pass
使用。例如:
1 | upstream backend { |
② server
语法:server name[parameters];
配置块:upstream
server配置项指定了一台上游服务器的名字,这个名字可以是域名、IP地址端口、UNIX句柄等,在其后还可以跟下列参数。
weight=number
:设置向这台上游服务器转发的权重,默认为1。max_fails=number
:该选项与fail_timeout配合使用,指在fail_timeout时间段内,如果向当前的上游服务器转发失败次数超过number,则认为在当前的fail_timeout时间段内这台上游服务器不可用。max_fails默认为1,如果设置为0,则表示不检查失败次数。fail_timeout=time
:fail_timeout表示该时间段内转发失败多少次后就认为上游服务器暂时不可用,用于优化反向代理功能。它与向上游服务器建立连接的超时时间、读取上游服务器的响应超时时间等完全无关。fail_timeout默认为10秒。down
:表示所在的上游服务器永久下线,只在使用ip_hash配置项时才有用。backup
:在使用ip_hash配置项时它是无效的。它表示所在的上游服务器只是备份服务器,只有在所有的非备份上游服务器都失效后,才会向所在的上游服务器转发请求。
1 | upstream backend { |
③ ip_hash
语法: ip_hash;
配置块: upstream
作用:在有些场景下,我们可能会希望来自某一个用户的请求始终落到固定的一台上游服务器中。例如,假设上游服务器会缓存一些信息,如果同一个用户的请求任意地转发到集群中的任一台上游服务器中,那么每一台上游服务器都有可能会缓存同一份信息,这既会造成资源的浪费,也会难以有效地管理缓存信息。ip_hash就是用以解决上述问题的,它首先根据客户端的IP地址计算出一个key,将key按照upstream集群里的上游服务器数量进行取模,然后以取模后的结果把请求转发到相应的上游服务器中。这样就确保了同一个客户端的请求只会转发到指定的上游服务器中。
ip_hash与weight(权重)配置不可同时使用。如果upstream集群中有一台上游服务器暂时不可用,不能直接删除该配置,而是要down参数标识,确保转发策略的一贯性。例如:
1 | upstream backend { |
2.5.3 反向代理的基本配置
① proxy_pass
语法: proxy_pass URL;
配置块: location、if
此配置项将当前请求反向代理到URL参数指定的服务器上,URL可以是主机名或IP地址加端口的形式,例如:
1 | proxy_pass http://localhost:8000/uri/; |
还可以如上节负载均衡中所示,直接使用upstream块,例如:
1 | upstream backend { |
② proxy_method
语法: proxy_method method;
配置块: http、server、location
此配置项表示转发时的协议方法名。例如设置为:
1 | proxy_method POST; |
那么客户端发来的GET请求在转发时方法名也会改为POST
3 HTTP模块
3.1 一个简单的HTTP模块
Nginx HTTP模块调用的简化流程:
worker进程会在一个for循环语句里反复调用事件模块检测网络事件。当事件模块检测到某个客户端发起的TCP请求时(接收到SYN包),将会为它建立TCP连接,成功建立连接后根据nginx.conf文件中的配置会交由HTTP框架处理。HTTP框架会试图接收完整的HTTP头部,并在接收到完整的HTTP头部后将请求分发到具体的HTTP模块中处理。这种分发策略是多样化的,其中最常见的是根据请求的URI和nginx.conf里location配置项的匹配度来决定如何分发。HTTP模块在处理请求的结束时,大多会向客户端发送响应,此时会自动地依次调用所有的HTTP过滤模块,每个过滤模块可以根据配置文件决定自己的行为。例如,gzip过滤模块根据配置文件中的gzip on|off来决定是否压缩响应。HTTP处理模块在返回时会将控制权交还给HTTP框架,如果在返回前设置了subrequest,那么HTTP框架还会继续异步地调用适合的HTTP模块处理子请求。开发HTTP模块时,首先要注意的就是HTTP框架到具体的HTTP模块间数据流的传递,以及开发的HTTP模块如何与诸多的过滤模块协同工作。
3.2 准备工作
3.2.1 整型封装
Nginx使用ngx_int_t
封装有符号整型,使用ngx_uint_t
封装无符号整型。Nginx各模块的变量定义都是如此使用的,建议读者沿用Nginx的习惯,以此替代int和unsinged int。
在Linux平台下,Nginx对ngx_int_t和ngx_uint_t的定义如下:
1 | typedef intptr_t ngx_int_t; |
3.2.2 ngx_str_t数据结构
在Nginx的领域中,ngx_str_t
结构就是字符串,定义如下:
1 | typedef struct { |
注意,ngx_str_t的data成员指向的并不是普通的字符串,因为这段字符串未必会以’\0’作为结尾,所以使用时必须根据长度len来使用data成员。
3.2.3 ngx_list_t数据结构
ngx_list_t是Nginx封装的链表容器,它在Nginx中使用得很频繁,例如HTTP的头部就是用ngx_list_t来存储的。定义:
1 | typedef struct ngx_list_part_s ngx_list_part_tshi ; // ngx_list_part_s ngx_list_part_t 是同义词 |
ngx_list_t中的所有数据都是由ngx_pool_t
类型的pool内存池分配的,它们通常都是连续的内存(在由一个pool内存池分配的情况下)。
提供的操作接口:
1 | // 用于创建新的链表 |
代码示例
- 建立一个链表,它存储的元素是
ngx_str_t
,其中每个链表数组中存储4个元素,代码如下所示:
1 | ngx_list_t* testlist = ngx_list_create(r->pool, 4, sizeof(ngx_str_t)); |
- 添加元素
1 | ngx_str_t* str = ngx_list_push(testlist); |
- 遍历链表
1 | // part用于指向链表中的每一个ngx_list_part_t数组 |
3.2.4 ngx_table_elt_t数据结构
3.3 将自定义HTTP模块编译进Nginx
Nginx提供了一种简单的方式将第三方的模块编译到Nginx中。首先把源代码文件全部放到一个目录下,同时在该目录中编写一个文件用于通知Nginx如何编译本模块,这个文件名必须为config
。
这样,只要在configure脚本执行时加入参数--add-module=PATH
(PATH就是上面我们给定的源代码、config文件的保存目录),就可以在执行正常编译安装流程时完成Nginx编译工作。
有时,Nginx提供的这种方式可能无法满足我们的需求,其实,在执行完configure脚本后Nginx会生成objs/Makefile
和objs/ngx_modules.c
文件,完全可以自己去修改这两个文件,这是一种更强大也复杂得多的方法。
3.3.1 config文件
config文件其实是一个可执行的Shell脚本。如果只想开发一个HTTP模块,那么config文件中需要定义以下3个变量:
ngx_addon_name
:仅在configure执行时使用,一般设置为模块名称。HTTP_MODULES
:保存所有的HTTP模块名称,每个HTTP模块间由空格符相连。在重新设置HTTP_MODULES变量时,不要直接覆盖它,因为configure调用到自定义的config脚本前,已经将各个HTTP模块设置到HTTP_MODULES变量中了,因此,要像如下这样设置:
1 | "$HTTP_MODULES ngx_http_mytest_module" # 追加形式 |
NGX_ADDON_SRCS
:用于指定新增模块的源代码,多个待编译的源代码间以空格符相连。注意,在设置NGX_ADDON_SRCS
时可以使用$ngx_addon_dir
变量,它等价于configure执行时–add-module=PATH的PATH参数。
因此,对于mytest模块,可以这样编写config文件:
1 | ngx_addon_name=ngx_http_mytest_module |