Nginx

1.Nginx介绍

1.1.Nginx

  1. Nginx ("engine x") 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP 代理服务器;
  2. 第一个公开版本0.1.0发布于2004年10月4日;
  3. 其将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名;
  4. 官方测试nginx能够支支撑5万并发链接,并且cpu、内存等资源消耗却非常低,运行非常稳定。

一个2C44的虚拟机,一般可以支持2W左右的并发。可以代理多个小型网站。

1.2.Nginx和Apache的优缺点

nginx最大的特点是:资源消耗低,性能强、成本低。扩展性其实不算是特点,应用Apache的扩展性更强。

  1. nginx相对于apache的优点:
    • 轻量级,比apache占用更少的内存及资源;
    • 高并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的,在高并发下nginx能保持低资源低消耗高性能;
    • 高度模块化的设计,编写模块相对简单;
    • 社区活跃,各种高性能模块出品迅速啊
  2. apache 相对于nginx 的优点:
    • nginx只适合静态和反向,Apache可以处理动态请求
    • rewrite,比nginx 的rewrite 强大;
    • 模块超多,基本想到的都可以找到(Tomcat是Apache的一个插件);
    • 少bug ,nginx的bug相对较多;
  3. Nginx 配置简洁, Apache 复杂;
  4. Apache最大的缺点是:采用fork的方式派生子进程,一个连接对应一个进程;nginx采用epoll模型,多个连接(万级别)可以对应一个进程,资源消费非常小。
  5. 实际对比:Apache支撑这日均千万 PV 的网站,服务器平均负载在50~60, CPU 消耗在70%~90%;使用 Nginx 后,系统平均负载降低至1~4, CPU 使用率在20%~40%,效果可见。

1.3.大型网站系统架构

1.4.性能资源使用情况

以常见 LNMP (linux、Nginx、Mysql、PHP)为例,

  • 背景:服务器在3万并发连接下,PHP部署在nginx上,且nginx开启cgi进程
  • 启动状态:开启10个 Nginx 进程消耗内存:15MB*10=150MB
  • 启动状态:开启64个 php-cgi进程,也就消耗20MB*64=1280MB,总共后台进程使用不到2GB内存。通常不会开启这么多
  • 压测状态:Nginx+PHP的程序任然能够飞速运转,单机处理请求能力在700次/秒,那么日承受访问量在7006060*24=60,480,000=6千万,且服务器的负载不会太高。

1.5.可视化管理

2.Nginx安装

1.安装Nginx依赖包
   yum install -y pcre pcre-devel
   yum install -y openssl openssl-devel

ubuntu可能需要以下:
   安装gcc g++的依赖库
   sudo apt-get install build-essential
   sudo apt-get install libtool
   
   安装pcre依赖库(http://www.pcre.org/)
   sudo apt-get update
   sudo apt-get install libpcre3 libpcre3-dev
   
   安装zlib依赖库(http://www.zlib.net)
   sudo apt-get install zlib1g-dev
   
   安装SSL依赖库(16.04默认已经安装了)
   sudo apt-get install openssl

2.下载源码,并解压
   wget http://nginx.org/download/nginx-1.28.0.tar.gz
   tar zxvf nginx-1.28.0.tar.gz

3.进入到解压文件下
  cd nginx-1.28.0

4.配置安装位置,(注意/usr/local/nginx是安装位置,不能使用源码的位置安装,否则一定安装失败)
  ./configure --prefix=/usr/local/nginx
  
  配置成功之后,会返回配置信息。我们可以记下来
  Configuration summary
      + using system PCRE library
      + using system OpenSSL library
      + using system zlib library
      
      nginx path prefix: "/usr/local/nginx"
      nginx binary file: "/usr/local/nginx/sbin/nginx"
      nginx modules path: "/usr/local/nginx/modules"
      nginx configuration prefix: "/usr/local/nginx/conf"
      nginx configuration file: "/usr/local/nginx/conf/nginx.conf"
      nginx pid file: "/usr/local/nginx/logs/nginx.pid"
      nginx error log file: "/usr/local/nginx/logs/error.log"
      nginx http access log file: "/usr/local/nginx/logs/access.log"
      nginx http client request body temporary files: "client_body_temp"
      nginx http proxy temporary files: "proxy_temp"
      nginx http fastcgi temporary files: "fastcgi_temp"
      nginx http uwsgi temporary files: "uwsgi_temp"
      nginx http scgi temporary files: "scgi_temp"

5.配置
   编译工程:make
   安装:make install

6.命令
   nginx [-?hvVtq] [-s][-p][-c][-g]
   -V : 显示版本和配置选项信息
   -t : 检测配置文件是否有语法错误
   -q : 在检测配置文件期间屏蔽非错误信息
   -s reload|reopen|stop|quit 重新加载配置|重启日志文件|停止|退出
   -p : 设置前缀路径
   -c : 设置配置文件
   -g : 设置配置文件外的全局指令
   
   测试启动:./nginx -t
   正式启动:./nginx
     
   重启:        ./nginx -s reload
   重新加载配置文件:        ./nginx -s reopen
   快速停止:              ./nginx -s stop		
   有序停止(等待任务完成): ./nginx -s quit  	
   版本:                 ./nginx -v
   已安装模块:            ./nginx -V
   已安装模块(格式化):    ./nginx -V 2>&1 | sed "s/\s\+--/\n --/g"

7.检查
   ps -ef |grep nginx
   netstat -antup |grep 80
   
* windos使用【尽量不要使用nginx,没有什么研究的意义,没办法添加模块和重新编译】:
    start nginx 或者双击 nginx.exe,控制台没啥日志
    nginx.exe -s stop
    nginx.exe -s reload
    taskkill /f /t /im nginx.exe    kill进程

安装时可选的参数

./configure 
   --prefix="安装路径" --user=安装用户 --group=安装用户组
   # 1.执行文件、配置文件、日志、进程等位置。正常默认就行,不用设置
   --sbin-path=/usr/sbin/nginx 
   --conf-path=/etc/nginx/nginx.conf 
   --error-log-path=/var/log/nginx/error.log 
   --http-log-path=/var/log/nginx/access.log 
   --pid-path=/var/run/nginx/nginx.pid 
   --lock-path=/var/lock/nginx.lock 
   # 2.【开启】内置的模块
   --with-http_ssl_module           # 仅支持https请求,需已经安装openssl 
   --with-http_stub_status_module   # 获取nginx自上次启动以来的工作状态
   --with-http_sub_module           # 允许用一些其他文本替换nginx响应中的一些文本
   --with-http_gzip_static_module   # 在线实时压缩输出数据流
   --with-file-aio                  # 使用nginx的aio特性会大大提高性能。如果有大量的IO,有助于并发处理大量的IO和提高Nginx处理效率
   --with-threads                   # 多线程模块
   --with-http_addition_module      # 在响应之前或者响应之后追加文本内容,比如想在站点底部追加一个js或者css,可以使用这个模块来实现。
   --with-http_auth_request_module  # 认证模块
   --with-http_dav_modeule          # 使应用程可直接对webserver直接读写,并支持文件锁定及解锁,还支持文件的版本控制。启用对WebDav协议支持。
   --with-http_flv_module           # Nginx增加MP4,FLV视频支持模块
   --with-http_mp4_module           # 多媒体模块
   --with-http_gunzip_module        # 压缩模块
   --with-http_random_index_module  # Nginx显示随机首页模块,对隐藏文件不起作用。
   --with-http_realip_module        # Nginx获取真实IP模块
   --with-http_secure_link_module   # Nginx安全下载模块
   --with-http_sice_module          # Nginx中文文档
   --with-mail                      # 启用POP3/IMAP4/SMTP代理模块支持
   --with-mail_ssl_module           # 启用ngx_mail_ssl_module支持
   --with-http_v2_module            # 开启http 2.0
   --with-stream                    # 4层负载支持。ngx1.9之后的stream模块
   --with-stream_realip_module  
   --with-stream_ssl_module 
   --with-stream_ssl_preread_modul 
   
   ## 3.【关闭内置模块】
   --without-select_module
   --without-poll_module          # 一般用epoll模式了,很少用这两个了吧?
   --without-dso                  # 关闭动态加载模块的功能,这是tengine特有的功能
   --without-http_charset_module  # 定义文件编码格式,不是非常明白,MS可关闭
   --without-http_gzip_module     # 这个web服务器基本上都要的,对css,js等gzip压缩用
   --without-http_ssl_module      # 要建ssl站点吗?一般不需要吧。
   --without-http_userid_module   # 在无法使用cookies的地方实现用户标识,用于链接中自动增加sessionID等,MS可关闭
   --without-http_footer_filter_module  # 在请求的响应末尾输出一段内容。输出内容可配置,并支持内嵌变量。这是tengine特有的功能
   --without-http_trim_filter_module    # 该模块用于删除 html , 内嵌 javascript 和 css 中的注释以及重复的空白符。这是tengine特有的功能
   --without-http_access_module         # 访问控制模块,一般需要
   --without-http_auth_basic_module     # 一种简单的验证用户的方式
   --without-http_geo_module            # 据说是根据来访IP判断用户访问,可实现类似智能解析的功能,一般不需要
   --without-http_map_module            # 据说是条件赋值模块,用于复杂条件的判断, 可关闭
   --without-http_split_clients_module  # Splits clients based on some conditions
   --without-http_referer_module        # 来源referer,一般都需要
   --without-http_rewrite_module        # 重写rewrite,一般都需要
   --without-http_proxy_module          # 代理服务,其实一般也不需要
   --without-http_memcached_module      # memcached缓存模块, 通常用于服务器间共享数据, 方便但据说效率不高,一般不需要
   --without-http_limit_conn_module     # 限制连接数
   --without-http_limit_req_module      # 限制请求数等
   --without-http_empty_gif_module      # 强制返回一个在内存中的1*1的空GIF图, 通常用于访问统计/日志记录等, 可关闭
   --without-http_browser_module        # 根据访问者浏览器进行基本判断, 可关闭
   --without-http_upstream              # 对upstream进行一些控制
   --without-http_user_agent_module     # 根据user_agent做些判断,一般不需要
   --without-http_stub_status_module    # 获取Nginx自上次启动以来的工作状态,对于监控web请求数据有点作用
   --without-http                       # 关闭 HTTP server
   --without-http-cache                 # 关闭 HTTP cache
   --without-http_fastcgi_module        # 一般用来运行php,一般都需要。默认开启
   # 常用关闭的模块
   --without-http_uwsgi_module          # uWSGI protocol支持,一般不需要
   --without-http_scgi_module           # SCGI  protocol支持,一般不需要
   --without-mail_pop3_module           # 关闭 ngx_mail_pop3_module
   --without-mail_imap_module           # 关闭 ngx_mail_imap_module
   --without-mail_smtp_module           # 关闭 ngx_mail_smtp_module 
   --without-http_autoindex_module      # 目录下没默认文件时,会把这个目录下的文件都列出,一般不需要
   --without-http_ssi_module            # 简单说允许html可以包含文件,一般来说也不用
   --without-pcre                       # pcre支持,一般不需要
   
   # 3.缓存的目录,一般不要设置
   --http-client-body-temp-path=/var/tmp/nginx/client/  # 客户端缓存
   --http-proxy-temp-path=/var/tmp/nginx/proxy/         # 代理缓存
   --http-fastcgi-temp-path=/var/tmp/nginx/fcgi/        # fastcgi缓存,比如php
   --http-uwsgi-temp-path=/var/tmp/nginx/uwsgi          # uwsgi缓存,比如python
   --http-scgi-temp-path=/var/tmp/nginx/scgi            # scgi缓存,比如python
    
   # 4.增加第三方的模块
   --add-module=../ngx_devel_kit-0.3.0/ 
   --add-module=../lua-nginx-module-0.10.10/ 
   --add-module=../nginx_upstream_check_module-master/ 

我的安装参数

./configure \
	--prefix="/usr/local/nginx" \
	--with-http_ssl_module           \
	--with-http_stub_status_module   \
	--with-http_sub_module           \
	--with-http_gzip_static_module   \
	--with-http_addition_module      \
	--with-http_flv_module           \
	--with-http_mp4_module           \
	--with-http_gunzip_module        \
	--with-http_realip_module        \
	--with-http_secure_link_module   \
	--with-http_v2_module            \
	--with-stream     \
	--add-module=./ngx_devel_kit-0.3.1/   \
	--add-module=./lua-nginx-module-0.10.20/   \
	--add-module=./nginx_upstream_check_module-0.4.0/ 	  

3.使用教程

使用教程https://www.w3cschool.cn/nginx/

3.1.使用案例

尚学堂培训课件【网络收集】

学习案例文档:

3.2.配置说明

  • 配置案例1:拆分为不同的部分进行说明

nginx.conf

【方案1】server.conf

【方案1】server.conf

server-https.conf

四层负载

  • 配置案例2:内容太多,需要到项目中看
  • 配置案例3:内容太多,需要到项目中看

3.3.高可用机群部署文档

3.4.运维常见问题

  • 如安装失败,一般是因安装目录未授权或安装服务器缺少依赖库导致。授权问题使用chmod命令处理,缺少的依赖库可根据报错详情使用yum全局安装或在/.configure里添加依赖。
  • 如修改HTML目录后报403,一般是目录未授权导致,使用chmod授权处理。
  • Nginx Log中部分参数无法输出,是因为参数未定义,Log中的参数除了自带参数,均需要先定义再输出。
  • 如遇到重启无法更新配置或停止失败的情况,可使用kill -9杀死Nginx相关进程,再重新启动。
  • 添加新的第三方模块,或者开启/关闭自带模块的时候,需要重新 配置、编译、安装。
    • 经验1:第一次安装时的源码不要删除,最好把configuration命令记录下来,其中安装的第三方模块也一样处理
    • 经验2:重新配置的时候,最好换一个目录,方便nginx回滚。
  • 配置生效验证。比如最大文件打开数量,最好看下进程中的配置是否生效 ` cat /proc/{nginx的pid}/limits `

3.5.管理工具

https://github.com/NginxProxyManager/nginx-proxy-manager

4.模块

nginx模块分为两种,官方和第三方,我们通过命令 nginx -V 查看 nginx已经安装的模块!

[root@localhost sbin]# ./nginx -V
nginx version: nginx/1.28.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/usr/local/nginx --with-http_ssl_module --with-http_stub_status_module 
--with-http_sub_module --with-http_gzip_static_module --with-http_addition_module --with-http_flv_module 
--with-http_mp4_module --with-http_gunzip_module --with-http_realip_module --with-http_secure_link_module 
--with-http_v2_module --with-stream --add-module=./ngx_devel_kit-0.3.1/ --add-module=./lua-nginx-module-0.10.20/ 
--add-module=./nginx_upstream_check_module-0.4.0/

Nginx 详解(包含各个 Nginx 模块)

对于已经使用的Nginx,增加模块的时候,需要重新编译整个Nginx。

4.1.常用的模块

1.Development Kit  Nginx的开发工具包【推荐】
  https://github.com/simpl/ngx_devel_kit

2.Echo   便捷命令,输出nginx信息
  http://wiki.nginx.org/HttpEchoModule

3.Extended status module   Nginx status模块的扩展
  http://wiki.nginx.org/Extended_status_module

4.Foot filter  在页面输出底部加入字符串
  http://wiki.nginx.org/HttpFootFilterModule

5.GeoIP IP地址识别
  http://wiki.nginx.org/Http3rdPartyGeoIPModule
 
6.HTTP Push   将Nginx改装成comet服务
  https://pushmodule.slact.net/

7.Limit Upload Rate   限制客户端上传速率
  https://github.com/cfsego/limit_upload_rate/

8.lua-nginx-module  使用lua可以为nginx扩展出强大的能力,比如频率限制,访问redis等【推荐】
  https://github.com/openresty/lua-nginx-module

9.ModSecurity   web应用防火墙
  http://www.modsecurity.org/projects/modsecurity/nginx/index.html

10.PageSpeed   google开发的 高性能传输、低延时,基于nginx的页面加速
  http://ngxpagespeed.com/ngx_pagespeed_example/

11.Secure Download   创建安全现在链接
  http://wiki.nginx.org/HttpSecureDownload

12.SysGuard   当系统负载过高时,保护系统
  https://github.com/alibaba/nginx-http-sysguard

13.nginx_upstream_check_module   后端健康检查【推荐】
  https://github.com/yaoweibin/nginx_upstream_check_module

4.2.lua 模块

lua 是 openresty 先提出来的。所有想要在Nginx上使用lua,最好选择使用openresty,在Nginx集成是比较麻烦的。

1.下载相关软件

# 不要下载官网的,要去下载openresty的优化版本,lua-nginx-module 模块会报错
luajit: wget --no-check-certificate https://github.com/openresty/luajit2/archive/refs/tags/v2.1-20210510.tar.gz

# resty 的 lua 依赖库【必须】
lua-resty-core:wget --no-check-certificate https://github.com/openresty/lua-resty-core/archive/refs/tags/v0.1.22.tar.gz
lua-resty-lrucache:wget --no-check-certificate https://github.com/openresty/lua-resty-lrucache/archive/refs/tags/v0.11.tar.gz

# Nginx
nginx-1.22.50:wget --no-check-certificate http://nginx.org/download/nginx-1.17.5.tar.gz

# Nginx的第三方模块
lua-nginx-module:wget --no-check-certificate https://github.com/openresty/lua-nginx-module/archive/refs/tags/v0.10.20.tar.gz
ngx_devel_kit:wget --no-check-certificate https://github.com/vision5/ngx_devel_kit/archive/refs/tags/v0.3.1.tar.gz

2.解压与安装环境

# 1.安装lua。注意:我们的电脑可能已经安装了别的版本的lua。所有我们最好但是安装一个新的目录
tar -zxvf luajit2-2.1-20210510.tar.gz
cd luajit2-2.1-20210510
make install  PREFIX=/usr/local/LuaJIT

# 2.添加lua环境变量
export LUAJIT_LIB=/usr/local/LuaJIT/lib
export LUAJIT_INC=/usr/local/LuaJIT/include/luajit-2.1

# 3.安装Lua核心库
tar -zxvf lua-resty-core-0.1.21.tar.gz [必须]
tar -zxvf lua-resty-lrucache-0.11.tar.gz [必须]

# 4.安装在同一个目录下
cd lua-resty-core-0.1.22
make install PREFIX=/usr/local/lua_core

cd ../lua-resty-lrucache-0.11
make install PREFIX=/usr/local/lua_core


# 5.安装nginx,并添加目录
解压: lua-nginx-module 和 ngx_devel_kit 后,重新配置、编译、安装nginx
    省略。。。。。。。。
	--add-module=./ngx_devel_kit-0.3.1/   \
	--add-module=./lua-nginx-module-0.10.20/
	
# 6.加载lua库,加入到ld.so.conf文件
vim  /etc/ld.so.conf
/usr/local/lib
/usr/local/LuaJIT/lib	

3.修改nginx配置

#修改nginx.conf,添加在http模块下
# 指定lua模块路径,多个之间";"分隔,其中";;"表示默认搜索路径,默认到nginx的根目录下找
lua_package_path "/usr/local/lua_core/lib/lua/?.lua;;";

# 添加一个测试lua是否生效的server 
server {
    listen       8080;
    server_name  127.0.0.1 www.zx.com;
 
    location /lua {
        set $test "hello,world";
        content_by_lua '
            ngx.header.content_type="text/plain"
            ngx.say(ngx.var.test)';
    }
}

访问地址
curl http://127.0.0.1:8080/lua
返回:hello,world

问题1:nginx: error while loading shared libraries: libluajit-5.1.so.2: cannot open shared object file: No such file or directory

出现这个错误,是变量没有写进去,所以就添加变量:

cat /etc/ld.so.conf
include ld.so.conf.d/*.conf

# 添加环境变量的路径。就是lua默认安装的路径
echo "/usr/local/lib" >> /etc/ld.so.conf
echo "/usr/local/LuaJIT/lib" >> /etc/ld.so.conf

刷新配置: ldconfig

问题2:nginx: [alert] detected a LuaJIT version which is not OpenResty's; many optimizations will be disabled and performance will be compromised (see https://github.com/openresty/luajit2 for OpenResty's LuaJIT or, even better, consider using the OpenResty releases from https://openresty.org/en/download.html)

需要安装OpenResty优化的lua

1.卸载LuaJIT官网主分支版本,然后重新安装openresty提供的luajit优化版即可
make uninstall
make clean

2.下载安装openresty的lua。 https://github.com/openresty/luajit2/tags
安装过程参考上面的案例

问题3:nginx: [alert] failed to load the 'resty.core' module (https://github.com/openresty/lua-resty-core); ensure you are using an OpenResty release from https://openresty.org/en/download.html (reason: module 'resty.core' not found:

安装lua-resty-core和依赖文件lua-resty-lrucache。参考上面案例,安装的时需要指定安装目录,并在 nginx.conf 中指定lua模块路径, 多个之间";"分隔,其中";;"表示默认搜索路径,默认到nginx的根目录下找

lua_package_path "/usr/local/lua_core/lib/lua/?.lua;;";

4.2.nginx的客户端状态

  • 内置模块: –with-http_stub_status_module
  • 配置语法: stub_status;
 server {
     listen       8080;
     server_name  127.0.0.1 www.zx.com;
     location /nginx_status {
         stub_status on;
     }
 }

访问地址

curl http://192.168.3.101:8080/nginx_status

Active connections: 2 
server accepts handled requests
 9 9 8 
Reading: 0 Writing: 1 Waiting: 1 

nginx七种状态

  • Active connections : 当前活跃的连接数。
  • accepts TCP连接总数
  • handled 正在通讯的TCP连接数
  • requests 成功的请求数,正常情况,accepts=handled,意味着没有连接丢失。这个数字为累计的值
  • Reading 读取的请求头
  • Writing 响应
  • Waiting 等待的请求数,开启了keepalive

4.3.后端服务健康检测

安装: 需要针对不同版本的nginx安装不同的补丁。

nginx自带健康检查的缺陷:

  • Nginx只有当有访问时后,才发起对后端节点探测。
  • 如果本次请求中,节点正好出现故障,Nginx依然将请求转交给故障的节点,然后再转交给健康的节点处理。所以不会影响到这次请求的正常进行。但是会影响效率,因为多了一次转发。
  • 自带模块无法做到预警。

被动健康检查。使用nginx_upstream_check_module:

  • 区别于nginx自带的非主动式的心跳检测,淘宝开发的tengine自带了一个提供主动式后端服务器心跳检测模块,若健康检查包类型为http, 在开启健康检查功能后,nginx会根据设置的间隔向指定的后端服务器端口发送健康检查包,并根据期望的HTTP回复状态码来判断服务是否健康。
  • 后端真实节点不可用,则请求不会转发到故障节点
  • 故障节点恢复后,请求正常转发
upstream my_demo{
    # 【被动检查】1.nginx自带的健康检测
    #max_fails=1和fail_timeout=10s 
    # 表示在单位周期为10s钟内,中达到1次连接失败,那么接将把节点标记为不可用,
    # 并等待下一个周期(同样时常为fail_timeout)再一次去请求,判断是否连接是否成功。
    server 127.0.0.1:8001 max_fails=1 fail_timeout=10s;
    server 127.0.0.1:8002 max_fails=1 fail_timeout=10s;
    
    # 【主动检查】2.nginx_upstream_check_module配置,如默认的基于端口存活的健康检查不满足要求,可配置基于URI的健康检查,根据返回code判断后端Server是否存活
    #以下心跳检测的规则是:检测方式为http, 每3000ms检测一次,成功2次标识为存活,失败3次则标识后端服务为不存活,检测超时时间为1000ms
    check type=http interval=3000 rise=2 fall=3 timeout=1000;
    
    #配置检测的URI,例如 /test是后端写的一个接口
    check_http_send "HEAD /test HTTP/1.0\r\n\r\n";
    
    #配置正常返回code,即后端返回2xx或3xx均视为正常返回,其他code视为异常
    check_http_expect_alive http_2xx http_3xx;
}

server {
    listen 8080;
    server_name  127.0.0.1 www.zx.com; 
    location / {
        proxy_pass         	http://my_demo;
        proxy_set_header  	X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header   	HOST   $host;
        proxy_set_header   	X-Real-IP $remote_addr;
        proxy_set_header 	Accept-Encoding "";
        proxy_intercept_errors on; 
    }
    # 查看nginx_upstream_check_module检测的结果
    location /status  {
        check_status;
        access_log off;
    }
}

check配置说明

  • interval:向后端发送的健康检查包的间隔,单位为毫秒
  • fall(fall_count): 如果连续失败次数达到fall_count,服务器就被认为是down。
  • rise(rise_count): 如果连续成功次数达到rise_count,服务器就被认为是up。
  • timeout: 后端健康请求的超时时间,单位毫秒。
  • default_down: 设定初始时服务器的状态,如果是true,就说明默认是down的,如果是false,就是up的。默认值是true,也就是一开始服务器认为是不可用,要等健康检查包达到一定成功次数以后才会被认为是健康的。
  • type:健康检查包的类型,现在支持以下多种类型:
    • tcp:简单的tcp连接,如果连接成功,就说明后端正常。
    • ssl_hello:发送一个初始的SSL hello包并接受服务器的SSL hello包。
    • http:发送HTTP请求,通过后端的回复包的状态来判断后端是否存活。
    • mysql: 向mysql服务器连接,通过接收服务器的greeting包来判断后端是否存活。
    • ajp:向后端发送AJP协议的Cping包,通过接收Cpong包来判断后端是否存活。
    • port: 指定后端服务器的检查端口。你可以指定不同于真实服务的后端服务器的端口,比如后端提供的是443端口的应用,你可以去检查80端口的状态来判断后端健康状况。默认是0,表示跟后端server提供真实服务的端口一样。该选项出现于Tengine-1.4.0。

检测过程的日志:【error.log】

## 出现3次send() failed,就将这个节点设置为 down
2022/11/26 19:52:25 [error] 25218#0: send() failed (111: Connection refused)
2022/11/26 19:52:28 [error] 25218#0: send() failed (111: Connection refused)
2022/11/26 19:52:31 [error] 25218#0: send() failed (111: Connection refused)
## 127.0.0.1:8001  这个服务设置为 down,并关闭健康检测
2022/11/26 19:52:31 [error] 25218#0: disable check peer: 127.0.0.1:8001
## 服务正常后,这个服务设置为 up,并开始健康检测
2022/11/26 19:53:54 [error] 25218#0: enable check peer: 127.0.0.1:8001 

问题1:[error] 15265#0: *168 http upstream check module can not find any check server, make sure you've added the check servers, client: 192.168.3.20, server: 127.0.0.1, request: "GET /status/ HTTP/1.1", host: "192.168.3.101:8080"

ngx_http_upstream_hash_module 需要针对不同版本的nginx安装不同的补丁。 补丁需要安装在nginx目录

# 1.安装patch
yum -y install patch

# 2.安装补丁
patch -p1 < ../nginx_upstream_check_module-master/check_1.20.1+.patch 

patching file src/http/modules/ngx_http_upstream_hash_module.c
patching file src/http/modules/ngx_http_upstream_ip_hash_module.c
patching file src/http/modules/ngx_http_upstream_least_conn_module.c
patching file src/http/ngx_http_upstream_round_robin.c
patching file src/http/ngx_http_upstream_round_robin.h

4.4.流量带宽请求状态统计

zone_name       key     max_active      max_bw  traffic requests        active  bandwidth
imgstore_appid  43    27      6M      63G     374063  0        0
imgstore_appid  53    329     87M     2058G   7870529 50      25M
server_addr     10.128.1.17     2        8968   24M     1849    0        0
server_addr     127.0.0.1       1       6M      5G      912     1        0
server_name     d.123.sogou.com 478     115M    2850G   30218726        115     39M

zone_name   - 利用req_status_zone定义的分组标准。例如,按照服务器名称对请求进行分组后;
key         - 请求按分组标准分组后的分组标识(即组名)。例如按服务器名称分组时,组名可能是localhost;
max_active  - 该组的最大并发连接数;
max_bw      - 该组的最大带宽;
traffic     - 该组的总流量;
requests    - 该组的总请求数;
active      - 该组当前的并发连接数;
bandwidth   - 该组当前带宽。

5.高阶使用教程

5.1.map

map 指令是由 ngx_http_map_module 模块提供的,默认情况下安装 nginx 都会安装该模块。

map 的主要作用是创建自定义变量,通过使用 nginx 的内置变量,去匹配某些特定规则,如果匹配成功则设置某个值给自定义变量。 而这个自定义变量又可以作于他用。

Nginx map 使用详解

案例

# $args 是nginx内置变量,就是获取的请求 url 的参数
# 如果 $args 匹配到URL变量 debug, 那么 $my_param 的值会被设为 1 ,
# 如果 $args 一个都匹配不到 $my_param 就是default 定义的值,在这里就是 0
map $args $my_param {
    default 2;
    debug   1;
}
server {
    listen       8080;
    server_name  localhost;
    location / {
        root   html;
        index  $my_param.html;
    }
}

5.2.if

Nginx内置变量及if语句

使用案例。if 后面必须有一个空格。

# 1.返回状态
server {
    ## 自定义的变量为1的时候返回501
    if ($my_param = 1) {
        return 501;
    }
}	

# 2.流向不同的代理服务
location / {
    root    html;
    index   index.html index.htm index.php;
    proxy_redirect      off;
    proxy_set_header    X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header    X-Real-IP $remote_addr;
    proxy_set_header    Host $http_host;
    proxy_http_version  1.1;
    proxy_set_header    Connection "";
    
    if ($my_param = 1) {
        proxy_pass  http://127.0.0.1:8001;
        break;
    }
    if ($my_param = 2) {
        proxy_pass  http://127.0.0.1:8002;
        break;
    }
}

5.3.try_files

try_files是nginx中http_core核心模块所带的指令,主要是能替代一些rewrite的指令,提高解析效率

Module ngx_http_core_module

Nginx的try_files指令详解

示例一:
    location /demo/ {
        try_files $uri /demo/default.gif;
    }
说明:
    1、访问www.example.com/demo/123/321(文件不存在)时,此时看到的是default.gif图片,URL地址不变
    2、访问www.example.com/demo/123.png(文件存在)时,此时看到的是123.png图片,URL地址不变
总结:当images目录下文件不存在时,默认返回default.gif

示例二:
    location /demo/ {
        try_files $uri =403;
    }
说明:
    1、访问www.example.com/demo/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变
    2、访问www.example.com/demo/21.html(文件不存在)时,此时看到的是403状态,URL地址不变
总结:和示例一一样,只是将默认图片换成了403状态

示例三:
    location /demo/ {
        try_files $uri @ab;
    }
    location @ab {
        rewrite ^/(.*)$ https://blog.demo.com;
    }
说明:
    1、访问www.example.com/demo/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变
    2、访问www.example.com/demo/21.html(文件不存在)时,此时跳转到其他代理,URL地址改变
    总结:当文件不存在时,会去查找@ab值,此时在location中定义@ab内容

示例四:
    try_files $uri @pro;
    location @pro {
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass https://blog.demo.com;
    }
说明:
    1、访问www.example.com/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变
    2、访问www.example.com/post-3647.html(文件不存在)时,此时跳转到其他代理,URL地址不变
总结:当前服务器上文件不存在时,会进行反向代理

5.4.tcp参数优化

长连接优化

当我们使用浏览器访问nginx的时候,会发现一次请求之后会增加至少2个tcp连接。这是为什么呢?

查看tcp:

for i in {1..1000};do  netstat -tn | grep '8090' |awk '/^tcp/ {++S[$NF]} END {for(a in S) printf "%-15s\t%-5s", a, S[a] ; print "" }' && sleep 1;done
为什么是双倍: ` netstat -atnx grep ‘8090' `

接口被访问后,一方面 nginx需要和调用方建立tcp,另一方面,nginx需要和后端服务建立tcp,所有tcp数量就是双倍的并发数

# 协议                  本地地址                  远程地址                 状态
Proto   Recv-Q Send-Q   Local Address           Foreign Address         State
tcp        0      0     0.0.0.0:8090            0.0.0.0:*               LISTEN     
tcp        0      0     127.0.0.1:8090          127.0.0.1:59824         ESTABLISHED
tcp        0      0     127.0.0.1:59824         127.0.0.1:8090          ESTABLISHED
文件打开数量【不太准确,因为http短连接太快了,只能看到某个时刻的量】: ` lsof -p 7534 wc -l `

TCP的keepalive

[root@ ~]# sysctl -a  |grep tcp_keepalive
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75

参数解释:

  • tcp_keepalive_time 1200 ,tcp建立链接后1200 秒如果无数据传输,则会发出探活数据包
  • tcp_keepalive_probes 9 , 共发送9次
  • tcp_keepalive_intvl 75 ,每次间隔75秒

KeepAlive并不是默认开启的,在Linux系统上没有一个全局的选项去开启TCP的KeepAlive。需要开启KeepAlive的应用必须在TCP的socket中单独开启。

TCP socket也有三个选项和内核对应,通过setsockopt系统调用针对单独的socket进行设置:

  • TCPKEEPCNT: 覆盖 tcpkeepaliveprobes
  • TCPKEEPIDLE: 覆盖 tcpkeepalivetime
  • TCPKEEPINTVL: 覆盖 tcpkeepalive_intvl

前端长连接

http {
    # 设置客户端连接保持活动的超时时间,单位秒。在超过这个时间之后,Nginx会关闭该连接,默认值60
    # 使用keepalive必须使用下面两个指令,因为只有http1.1协议才支持keepalive,http1.0是不支持的
    keepalive_timeout 10;
    
    #在一个tcp连接上,最多执行多少个http请求,默认值100
    keepalive_requests 100;
}

后端长连接

upstream my_server{
    server 127.0.0.1:8080;
    keepalive 1000; # 这个很重要!缓存的连接个数
}
location / {
    proxy_http_version 1.1;              # 设置http版本为1.1
    proxy_set_header Connection "";      # 设置Connection为长连接(默认为no)
}  

内核长连接优化

参考《网络协议》文章中的优化

5.6.配置热更新

reload 并不是热更新,而是关闭旧worker进程后启动新worker进程。过程中会引起流量抖动,对长连接影响尤为明显。在网关的集群规模非常大时,更是不能随意的做 reload。

现在已经有很多主流应用选择采用长连接,HTTP 1.1 一般默认会使用 Keep-Alive 去保持长连接,后续 HTTP 2 以及 HTTP 3 也是如此, 随着网络协议的发展,未来使用长连接会变得更加普遍。而配置热更新天然对长连接非常友好。

如何解决这点呢?一是采用两层网关,即流量网关 + 业务网关;二是实现网关原生支持配置热更新

5.7.缓存优化

1.配置浏览器缓存时间。通过设置HTTP响应头中的Expires和Cache-Control字段,可以控制浏览器缓存的时间。

location ~* \.(jpg|jpeg|gif|png|css|js)$ {
    add_header Cache-Control "public, max-age=31536000"; # 设置静态资源的缓存时间为1年(单位:秒)
}

2.配置代理服务器缓存时间。如果Nginx作为反向代理服务器使用,可以通过设置proxy_cache_valid和proxy_cache_valid指令来控制代理服务器的缓存时间。

location / {
    proxy_pass http://backend; # 将请求转发到后端服务器
    proxy_cache mycache; # 启用缓存,命名为mycache
    proxy_cache_valid 200 302 60m; # 设置缓存有效时间为60分钟(单位:秒)
    proxy_cache_valid 404 1m; # 设置缓存有效时间为1分钟(单位:秒)
}

6.最佳实践

6.1.性能优化

一:服务器设置
1. 调整同时打开文件数量            ulimit -n 65535 。甚至更高
2. TCP最大连接数【最大并发数】      修改 /etc/security/limits.conf 和 sysctl.conf
    # limits.conf
    * soft nofile 65535
    * hard nofile 65535
    
    # sysctl.conf
    net.core.somaxconn = 65535                  # 系统最大连接数。默认1024
    net.ipv4.tcp_max_syn_backlog = 4096         # 队列大小
    net.ipv4.ip_local_port_range = 1024 65000   # 端口范围。tcp堆外提供连接时使用。
3. 不做TCP洪水抵御                
    net.ipv4.tcp_syncookies=0

二、nginx设置
1、工作进程与连接数配置
    1. worker_rlimit_nofile             文件打开数量,与ulimit -n一致
    2. worker_processes   auto          子进程数量,保持与CPU核数一致
    3. worker_connections 65535         指定单个工作进程的最大并发连接数,所有工作进程的连接数之和需小于系统的最大句柄打开数ulimit -n
        1).在作为反向代理时,根据经验值,预估最大连接数=worker_processes*worker_connections/4,各系统可参考此公式评估单机Nginx的最大承载并发
        2).在作为http服务时,根据经验值,预估最大连接数=worker_processes*worker_connections/2,各系统可参考此公式评估单机Nginx的最大承载并发 
    4. worker_priority -10              工作进程优先级(-20到20,数值越小优先级越高);
    5. worker_cpu_affinity auto         CPU亲和性绑定,避免进程在CPU间切换; 
2、事件驱动模型优化
events {
    1. use epoll                        强制使用epool(Linux最优选择)
    2. multi_accept on                  允许同时接受多个连接,默认off。
    3. worker_connections  65535        连接数配置
    4. accept_mutex off                 接受连接后立即禁用accpet锁    
}

3、HTTP核心配置优化
http {
    # 1.开启零拷贝函数。sendfile_max_chunk  1m; 设置最大分片大小为1MB。
    sendfile on;    
    tcp_nopush on;    
    tcp_nodelay on;        
    
    # 2.连接超时配置(复用连接,减少创建链接的次数。前端设置,与后端的服务也需要设置。)   
    keepalive_timeout 60;            # 如果是后端服务器的nginx。禁止 keepalive_timeout,可以有效降低句柄占用数量。
    keepalive_requests 10000;    
    client_header_timeout 10;    
    client_body_timeout 10;    
    send_timeout 10;        
    
    # 3.缓冲区大小优化    
    client_header_buffer_size 4k;    
    large_client_header_buffers 88k;    
    client_body_buffer_size 128k;    
    client_max_body_size 50m;        
    
    # 4.开启gzip压缩    
    gzip on;    
    gzip_vary on;    
    gzip_min_length 1000;    
    gzip_comp_level 6;    
    gzip_types        
        text/plain        
        text/css        
        text/javascript        
        application/javascript        
        application/json        
        application/xml;  
              
    # 4.安全
    server_tokens off  隐藏版本信息
    listen 443 ssl http2;        启用HTTPS并启用HTTP/2协议支持功能。
}


4、连接池和缓存优化
http {
    # 1. 连接池配置    
    upstream backend {        
        keepalive 300;        
        keepalive_requests 10000;        
        keepalive_timeout   60s;                
        
        server192.168.1.100:8080 weight=3 max_fails=3 fail_timeout=30s;        
        server192.168.1.101:8080 weight=2 max_fails=3 fail_timeout=30s;    
    }        
    
    # 2. 打开文件缓存    
    open_file_cache max=100000 inactive=20s;    
    open_file_cache_valid 30s;    
    open_file_cache_min_uses 2;   
    open_file_cache_errors on;        
    
    # 3.FastCGI缓存配置(适用于PHP)    
    fastcgi_cache_path /var/cache/nginx levels=1:2 keys_zone=fcgi:100m inactive=60m;    
    fastcgi_cache_key "$scheme$request_method$host$request_uri";    
    fastcgi_cache_use_staleerror timeout invalid_header http_500;
}

5、限流和防护配置
http {    
    # 1. 限速(时间窗口)    
    limit_req_zone $binary_remote_addr zone=api:10m     rate=100r/s;    
    limit_req_zone $binary_remote_addr zone=login:10m   rate=5r/s;        
    
    # 2. 限制连接数:限制服务 server_name  限制客户端 binary_remote_addr
    limit_conn_zone $binary_remote_addr zone=perip:10m;    
    limit_conn_zone $server_name        zone=perserver:10m;
    
    server {        
        # 3. 应用限流规则        
        limit_req zone=api burst=200 nodelay;        
        limit_conn perip 20;        
        limit_conn perserver 2000;                
        
        # 4.静态资源长缓存        
        location~* \.(jpg|jpeg|png|gif|ico|css|js)$ {            
            expires 1y;            
            add_header Cache-Control "public, immutable";        
        }                
        
        # 5.API接口优化        
        location /api/ {         
            # 限流(漏桶限流算法)   
            limit_req zone=api burst=50 nodelay;            
            proxy_pass http://backend;            
            proxy_http_version 1.1;            
            proxy_set_header Connection "";            
            proxy_connect_timeout 5s;            
            proxy_send_timeout 10s;            
            proxy_read_timeout10s;        
        }    
    }
}

# 压测基本性能
ab -c 10000 -n 150000 http://127.0.0.1/index.html    

# 监控
#!/bin/bash
# nginx_monitor.sh - Nginx性能监控脚本
echo"=== Nginx连接状态 ==="
ss -s

echo"=== 活跃连接数 ==="
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'

echo"=== Nginx进程状态 ==="
ps aux | grep nginx | grep -v grep

echo"=== 系统负载 ==="
uptime

echo"=== 内存使用情况 ==="
free -h

echo"=== Nginx访问统计 ==="
curl -s http://localhost/nginx_status


6.2.动态封禁IP

在 Nginx 中实现动态封禁 IP,可以有效保护网站或应用免受恶意请求、爬虫或 DDoS 攻击。 相比于传统的修改配置文件 + 重载 Nginx 的静态方式,动态封禁能够自动识别、实时阻断恶意 IP,大大提升安全性和运维效率。

  1. Fail2ban。监控 Nginx 日志,达到阈值后调用防火墙或修改配置封禁,配置简单,社区成熟,支持多种服务。
    • 缺点:依赖日志分析,有延迟,频繁重载影响性能
    • 适用场景:防暴力破解、扫描器、异常请求攻击
  2. Nginx Lua + Redis。使用 ngx_lua 在访问阶段查询 Redis 黑名单,性能高,实时生效,可分布式共享。
    • 缺点:需要 OpenResty/Lua,架构复杂
    • 适用场景:高并发场景,分布式共享黑名单,精细化策略
  3. Nginx 内置模块。limit_req_zone 和 limit_conn_zone 限制请求频率和并发,原生支持。
    • 缺点:仅限流/限连接,不能真正封禁
    • 适用场景:防 CC 攻击、防止单 IP 滥用

7.原理

7.1.启动原理

nginx是一个master进程和多个worker进程,worker进程是master进程的子进程。

  • master进程负责创建socket句柄、创建和管理worker进程、配置检测 和 配置热更新。
  • worker进程负责与客户端建立连接,并处理请求。

1. master 创建socket,那为什么是worker进程与客户端创建连接

前提:

  1. Linux中子进程可以继承父进程的socket 的 fd,可以监听并接收父进程socket的信息。
  2. Socket 的创建涉及到一系列的系统调用,如 创建:socket(), 绑定端口:bind(), 开启监听:listen(), 接收数据:accept() 等

nginx启动时

  1. master启动后会创建socket,并开启socket的监听。
  2. 然后master创建worker进程,继承了master的socket fd,开启监听,并调用accept开始接收数据。
  3. 没有请求时worker进程进入休眠状态;当客户端发送过来消息,内核会调用监听的回调唤醒worker进程进行处理。

2. 当一个请求过来时,如果判断有哪个worker处理

Nginx多进程连接请求/事件分发流程分析

惊群问题:当多个子进程监听并接受同一个socket fd后,当客户端发送请求时,唤醒所有worker进程去尝试与客户端的连接, 但是只会有一个worker进程会成功,其他尝试失败的进程会继续休眠,这种方式会很大程度长浪费系统资源,这就是惊群问题(羊群效应)

nginx是如何实现的:nginx 提供了accept_mutex特性,他是一种互斥锁。 因为worker进程创建之后,调用accept,如果底层使用的是epoll,就意味着每个worker都创建了一个epoll_fd来管理自己竞争到的connect_fd和公共的listen_fd; https://tengine.taobao.org/book/chapter_2.html#connection

accept_mutex的效果:当一个新连接到达时,如果激活了accept_mutex,那么多个Worker将以串行方式来处理,只有一个Worker会被唤醒,其他继续保持休眠状态; 如果没有激活accept_mutex,那么所有的Worker都会被唤醒,不过只有一个Worker能获取新连接,其它的Worker会重新进入休眠状态。

events {
	accept_mutex on
}

最贱实践:Nginx默认开启accept_mutex,是一种保守的选择。如果关闭了它,可能会引起一定程度的惊群问题, 表现为上下文切换增多(sar -w)或者负载上升,但是如果流量太大的话,为了保障吞吐量,可以尝试关闭。 Nginx作者给出的解释

注意:高版本内核的Linux,accept不存在惊群问题。

Nginx的重启过程

使用 nginx -s reload 或者 kill -HUP pid (master 进程id)

  1. 向master进程发送HUP信号。
  2. master进程收到HUP信号后开始校验配置文件语法的正确性。
  3. master进程尝试打开新的监听端口(如果配置文件中新增了服务端口)。
  4. master进程使用新配置启动新的worker子进程。
  5. master进程向老worker子进程发送QUIT信号。
  6. 老worker收到QUIT信号后,首先关闭监听端口,从而不会再接受新的连接,然后处理完当前连接后结束进程。

总结:先启动新worker再关闭老worker来保证服务不中断的。新worker启动后就可以接受新连接,而老worker只有在处理完当前连接后再结束。 本质就是TCP状态变化,但是什么是"处理完当前连接",因为TCP数据交互没有明确的结束点,所以在TCP进入CLOSE_WAIT状态,等待后端响应。

8.常见问题

1. nginx被人诟病问题有什么?

  1. nginx转发到后端服务的时候,会缓存tcp到连接池中。通常nginx会启动多个worker进程,这时每个worker中都会产生连接池。 导致连接复用率不高。当worker数量非常大的时候,不仅造成资源的浪费,也会增加创建连接的耗时,降低服务的吞吐量。

反向代理域名,域名ip变更后,nginx解析没有一起变更

如果后端服务使用域名而非 IP 地址,当域名解析的 IP 发生变化时,默认情况下 Nginx 不会自动更新解析结果,需要通过以下方式处理:

  1. 手动执行 reload:nginx -s reload,重启子进程。
  2. 动态解析方案(无需手动 reload,适合大规模流量的情况下)
http {
    # 指定DNS服务器(Google的公共DNS),valid=30s 表示缓存 30 秒
    resolver 8.8.8.8 8.8.4.4 valid=30s;
    resolver_timeout 5s;    

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://backend.example.com;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

文章信息

张雪随笔
master
Homepage
Issues
Download

This Software is under the terms of The Unlicense.