1. 为什么用Nginx:(Requests/second, RPS)
- 高并发,高性能 以RPS为y轴,以并发连接数为x轴, 大部分的web服务器,随着并发数的提升,它的性能会急剧的下降。 而nginx可以同时具备高并发和高性能,随着并发数的提升,它的RPS可以稳定在60%。
- 可扩展性好 主要体现在Nginx的模块化设计,模块化设计非常稳定,使得nginx第三方模块生态圈非常丰富,甚至于有openresty这样的第三方插件,在它的基础之上又生成了一套新的生态圈,丰富的生态圈为nginx丰富的功能提供了保证
- 高可靠性 就是nginx可以在服务器上不间断地运行数年,而很多的web服务器,往往几周几个月就需要一次重启,对于nginx这样的高并发高性能的web服务器,它往往运行在企业内网的边缘节点上,这个时候企业如果想提供4个9,5个9甚至更高的高可靠性时,对于nginx持续运行能够宕机的时间,一年可能要按秒来计。
- 热部署 指可以在不停止服务的情况下升级nginx,这个功能对nginx来说非常重要,因为在nginx上可能跑了数百万的并发连接,如果是普通的服务,我们可能只需要kill进程再重启的方式就可以处理好,但是对于nginx而言,kill掉nginx进程会导致操作系统为所有已经建立连接的客户端发送tcp中的reset异常终止报文,而很多客户端是没有办法很好处理这个报文的,而在大并发场景下,一些偶然事件就会导致必然的恶性结果,所以热部署是非常有必要的。
- BSD许可证 nginx是免费的开源的,而且在有定制需求的场景下,去修改nginx的源代码,并将其应用到我们的商业场景下,这是合法的。
2. Nginx的四大主要组成部分:
- Nginx二进制可执行文件:由各模块源码编译出的一个文件
- access.log访问日志:记录每一条http请求信息
- error.log错误日志:定位问题
- Nginx.conf配置文件:控制nginx的行为
3. 如何查看Nginx新版本的特性
nginx每发布一个版本会有三个特性: feature(表示新增的功能), bugfix(表示修复的bug), change(表示所做的重构)
版本分为Mainline主干版本,和stable稳定版本。
nginx.org/en/download.html:
nginx: download
Mainline version:
CHANGES nginx-1.15.5 pgp nginx/Windows-1.15.5 pgp
Stable version:
CHANGES-1.14 nginx-1.14.0 pgp nginx/Windows-1.14.0 pgp
Lagacy versions:
只会列出历史稳定版本
...
CHANGES文件内容:
|
|
4. 编译出适合自己的Nginx
4.1 nginx源码目录介绍:
|
|
auto目录下有四个子目录:cc、lib、os、types
cc目录是用于编译的,os用于判断操作系统,lib是依赖的库函数
其他所有文件都是为了辅助./configure
脚本执行时去判定nginx支持哪些模块、
当前操作系统有什么特性可以供给nginx使用。
conf目录下存放了各种nginx配置的示例文件,为了方便运维,编译时会把示例文件拷贝到安装目录。
contrib目录提供了两个perl脚本,和vim语法提示的插件,cp -r contrib/vim/* ~/.vim/
html目录提供了两个标准的html文件:50x.html和index.html
man目录存放nginx的帮助文件
src目录:nginx的源代码
4.2 执行编译
- 确定Nginx执行中它会去找那些目录下的文件
- –modules-path : 动态模块功能会使用到这个参数
- –lock-path : 确定nginx.lock文件放在哪里
- –prefix : 如果没有任何变动的话,只指定prefix就可以了 所有其他的文件会在prefix目录下面,建相应的文件夹
- 确定使用哪些模块和不使用哪些模块
- –with-xxxx : 表示这个模块默认不会被编译进nginx
- –without-xxxx : 表示这个模块默认会被编译进nginx
- 指定nginx编译中需要的一些特殊参数
- 我用gcc编译的时候需要加一些优化的参数
- 打印debug级别的日志
- 需要加一些第三方的模块
|
|
上面命令执行完以后,会生成objs目录,该目录下最重要的一个文件是ngx_modules.c
,
它决定了,nginx在编译的时候,会有哪些模块会被编译进nginx。
异常处理:
# 问题:
make[2]: Entering directory `/root/pcre2-10.40'
make[2]: *** No rule to make target `libpcre.la'. Stop.
make[2]: Leaving directory `/root/pcre2-10.40'
make[1]: *** [/root/pcre2-10.40/.libs/libpcre.a] Error 2
# 解决
$ cp /usr/lib64/libpcre.a /root/pcre2-10.40/.libs
解决openssl依赖:
使用--with-http_ssl_module参数的时候,需要提供openssl依赖:
要么:
yum install -y openssl-devel
要么:
编译时使用参数: --with-http_ssl_module --with-openssl=/tmp/openssl-1.0.2k
注意: /tmp/openssl-1.0.2.k是openssl源码目录
--prefix=./ --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-stream_realip_module --with-pcre=../pcre2-10.39 --with-openssl=../openssl-OpenSSL_1_0_2k
|
|
上面命令会在objs文件中生成大量的中间文件和nginx二进制文件,在执行nginx版本升级的时候,
不能执行make install
,而应该把objs目录下的nginx二进制文件拷贝到安装目录中,覆盖旧版本。
如果我们使用到了动态模块,那么动态模块的so文件也会放在objs目录下。
|
|
首次安装时可以使用这个命令。
sbin: 存放nginx的二进制文件 conf: 存放决定nginx功能的配置文件 logs: 存放access.log和err.log
5. Nginx命令行及演示:重载、热部署、日志切割
5.1 nginx命令行:
- 格式: nginx -s reload
- 帮助: -? -h
- 使用指定的配置文件: -c
- 指定配置指定: -g 指定一些配置的指令,这些指令可以使用-g,所谓指令就是nginx.conf文件的指令,但是这些指令,如果我要在命令行中覆盖其中的某条指令的值,这个时候就可以使用-g。
- 指定运行目录: -p
- 发送信号: -s [stop|quit|reload|reopen] stop: 立刻停止服务 等价于 kill -SIGTERM <进程ID> quit: 优雅的停止服务 等价于 kill -SIGQUIT <进程ID> reload: 重载配置文件 等价于 kill -SIGHUP <进程ID> reopen: 重新开始记录日志文件 kill -SIGUSR1 <进程ID>
- 测试配置文件是否有语法错误:-t -T
- 打印nginx的版本信息、编译信息等:-v -V
5.2 热部署
|
|
5.3 切割日志文件
- 临时日志切割
- 定时执行日志切割 首先,创建一个名为rotate.sh的脚本
|
|
然后创建如下所示的crontab:
5.4 为什么kill掉worker进程后,会自动生成一个新的worker进程
如果我向一个worker进程,比如16982发送一个让它退出的信号,那么这个worker进程就会退出, 但是进程在退出时,会自动地向它的父进程9170,也就是master进程,发送一个SIGCHILD,这样一个信号受到以后,master进程就知道它的子进程退出了,它会新起一个worker进程,维持总共两个worker进程这样的进程结构。
5.5 使用信号来管理Nginx的父子进程
Master进程: Worker进程: nginx命令行:
>监控worker进程 >接收信号 >reload: HUP
CHLD TERM INT >reopen: USR1
>管理worker进程 QUIT >stop: TERM
>接收信号 USR1 >quit: QUIT
TERM,INT WINCH
QUIT(表示优雅的停
,可以慢慢的停,但是
要保证对用户不要发送
停止连接,像发送TCP的
reset复位请求这样的报文)
HUP (表示重载配置文件)
USR1 (表示重新打开日志文件)
USR2
WINCH
5.6 reload重载配置文件的真相
- 向master进程发送HUP信号(reload信号)
- master进程校验配置语法是否正确
- master进程打开新的监听端口(nginx.conf文件新增的监听端口) 为什么在master进程中打开新的监听端口呢? 所有的worker进程是master进程的子进程,子进程会继承父进程 所有已经打开的端口,这是linux操作系统所定义的。
- master进程用新配置启动新的worker子进程
- master进程向老worker子进程发送QUIT信号
- 老worker进程关闭监听句柄,处理完当前连接后结束进程。
异常的情况,如果有些请求出了问题了,那么客户端长时间没有处理,
那么,会导致这个请求长时间的占用在这个worker进程上面,那这个worker
进程就会一直存在,当然因为新的连接已经跑在了新的worker子进程中了,
所以影响并不会很大,唯一会导致这个这个老的worker子进程长时间存在,
但只会影响已存在的连接,不会影响新的连接,这个时候,有没有办法来处理呢?
实际上nginx比较新的版本中,它提供了一个配置项,叫
worker_shutdown_timeout
5.7 优雅地关闭worker进程
当nginx代理websocket的时候,在websocket后面进行通讯的frame帧里面,nginx是不解析它的帧的,所以这个时候,它是没有办法。 nginx在做TCP层或者UDP层反向代理的时候,它也没有办法识别一个请求需要经历多少报文才算是结束。 但是对于http请求,nginx可以做到的,所以优雅的关闭我们主要针对的是http请求。
- 设置定时器:
worker_shutdown_timeout
设置完定时器以后,它会加一个标志位,就是表示我现在进入优雅的关闭这样的一套流程了。 - 关闭监听句柄 先关闭监听句柄,保证我的worker进程不会再去处理新的连接了
- 关闭空闲连接 接下来,它回去看它的连接池,因为nginx为了保证自己对资源的利用是最大化的,所以它经常 会保存一些空闲的连接,但是没有断开,这时候会首先关闭所有的空闲连接。
- 在循环中等待全部连接关闭
这是一个时间可能非常长的一步,因为nginx不是主动地立刻关闭,所以它通过第一步我们加的
一个标志位,在循环中,每当发现一个请求处理完毕,就会把这个请求使用的连接关掉,在循环
中等待全部连接关闭的时间,可能会超过第一步我们说的
worker_shutdown_timeout
。 - 退出进程
6. 用GoAccess实现可视化并实时监控access日志
添加report.html文件的location:
server {
listen 8080;
access_log logs/geek.access.log main;
location /report.html {
alias /usr/local/nginx/html/report.html;
}
}
7. Nginx的模块究竟是什么?
7.1 使用模块的四个步骤
- 保证它被编译到我们的nginx二进制文件中,
- 该模块提供了哪些配置项,
- 我们要了解这个模块何时被使用 因为有的模块只要你编译进nginx,它默认就会被使用, 而有一些模块,你必须使用相应的配置项并且配置正确时,模块才会被使用。
- 这个模块提供哪些变量。
7.2 对于文档不详细的模块,如何确定以上四点呢?
-
保证它被编译进nginx二进制文件: 首先我们在开始编译时,我们在它的源代码根目录下会执行./configure,通过–with添加官方模块,通过–add-module添加第三方模块。 我们可以在./configure执行完之后,去{nginx-root-dir}/objs目录下,打开文件
ngx_modules.c
。 在这个文件中有一个数组,叫做ngx_modules
,这个数组包含了所有编译进nginx的模块,这里可以找到对应的模块,比如ngx_http_gzip_filter_module
。 -
该模块提供了哪些配置项(指令) 我们可以在nginx的源代码中有一个叫做{nginx-root-dir}/src/http/modules的目录, 然后我们打开
ngx_http_gzip_filter_module.c
文件,所有的模块都有对应的这样的一个.c的源文件。 在这个源文件中,我们去搜这样一个结构体,它是唯一的,叫做ngx_command_t
。 这是每个模块必须具备的一个结构体。这个结构体是一个数组,数组的每个成员是它支持的指令名,比如gzip的:
|
|
指令名的后面会描述,这个指令名的后面可以跟几个参数,参数的类型是怎样的,是空间类的 还是时间类型,可不可以加相应的参数,等等。
ngx_module_t
有4个子结构体:ngx_core_command_t
,ngx_http_module_t
,ngx_event_module_t
,ngx_mail_conf_ctx_t
ngx_module_t
有个成员index
,如果模块间存在冲突,那么先生效的模块会阻碍后生效的模块发挥作用。
7.3 Nginx模块的分类
ngx_module_t
有个成员type
,它定义了模块的类别。
+-----------------+ +----------------------+ +------------------------+
|{ngx_conf_module}| |{upstream相关模块} | +>|{ngx_mail_core_module} |
| NGX_CONF_MODULE | |{响应过滤模块} | | |NGX_MAIL_MODULE |
+-----------------+ |{请求处理模块} | | +------------------------+
+-----------------+ |{ngx_http_core_module}| | +------------------------+
|{epoll} | | NGX_HTTP_MODULE | | |{stream模块} |
|{event_core} | | | | |{ngx_stream_core_module}|
|NGX_EVENT_MODULE | | | | |NGX_STREAM_MODULE |
+---------^-------+ +-----------^----------+ | +-----------^------------+
| | | |
+---------|----------------------|------------|-------------|------------+
| {events} {http} {mail} {stream} |
| NGX_CORE_MODULE {core} {errlog} {thread_pool} {openssl}|
+------------------------------------------------------------------------+
分析nginx源码的src目录:
core目录并不是指NGX_CORE_MODULE
,而是指nginx核心框架代码
7.4 使用动态模块来提升运维效率
-
动态库和静态库的区别: 静态库会直接把所有的源代码编译进最终的二进制可执行文件中 动态库在nginx二进制可执行文件里,只保留了调用它的位置。
-
添加动态库的步骤 (1) Configure加入动态模块 (2) 编译进binary (3) 启动时初始化模块数组 (4) 读取
load_module
配置 (5) 打开动态库并加入模块数组 (6) 基于模块数组开始初始化
并不是所有的模块都能动态化,只有特定的某些模块才有这个功能。
-
使用
./configure --help | less
来查看支持动态库的模块 例如,--with-http_image_filter_module=dynamic
-
使用动态库编译后,会在安装目录下创建一个modules目录, 该目录下会生成对应的so文件。然后在nginx.conf目录的首行添加:
load_module modules/xxxx.so
,就可以生效了
logs/service/zeus-merge-v2/permission-da-service/migration_upgrade_2.4.1.0_15***8
本文发表于 0001-01-01,最后修改于 0001-01-01。
本站永久域名「 jiavvc.top 」,也可搜索「 后浪笔记一零二四 」找到我。