nginx安装及配置参考
2012-06-02 12:17:27 阿炯

本站赞助商链接,请多关照。 目录参考

1、源码安装-Source Repository
2、预编译包-Pre-Built Packages
3、简单配置优化Nginx
4、Nginx日志格式log_format
5、Nginx的ssl配置
6、理解和配置超时时间
7、location里的root、alias指令用法
8、修改Nginx源代码以实现记录慢查询
9、从源码编译nginx与新版本的openssl
10、Nginx作为静态资源服务器
11、Reload及其背后的信号


1、源码安装-Source Repository

Read-only Subversion repository: svn://svn.nginx.org/nginx

可以通过这个svn进行代码下载并安装,具体过程略,这里给其编译选项:
configure各项中文说明
--prefix=<path> -- Nginx安装路径。如果没有指定,默认为 /usr/local/nginx。 
--sbin-path=<path> -- Nginx可执行文件安装路径。只能安装时指定,如果没有指定,默认为<prefix>/sbin/nginx。 
--conf-path=<path> -- 在没有给定-c选项下默认的nginx.conf的路径。如果没有指定,默认为<prefix>/conf/nginx.conf。 
--pid-path=<path> -- 在nginx.conf中没有指定pid指令的情况下,默认的nginx.pid的路径。如果没有指定,默认为 <prefix>/logs/nginx.pid。 
--lock-path=<path> -- nginx.lock文件的路径。 
--error-log-path=<path> -- 在nginx.conf中没有指定error_log指令的情况下,默认的错误日志的路径。如果没有指定,默认为 <prefix>/logs/error.log。 
--http-log-path=<path> -- 在nginx.conf中没有指定access_log指令的情况下,默认的访问日志的路径。如果没有指定,默认为 <prefix>/logs/access.log。 
--user=<user> -- 在nginx.conf中没有指定user指令的情况下,默认的nginx使用的用户。如果没有指定,默认为 nobody。 
--group=<group> -- 在nginx.conf中没有指定user指令的情况下,默认的nginx使用的组。如果没有指定,默认为 nobody。 
--builddir=DIR -- 指定编译的目录 
--with-rtsig_module -- 启用 rtsig 模块 
--with-select_module --without-select_module -- 允许或不允许开启SELECT模式,如果 configure 没有找到更合适的模式,比如:kqueue(sun os),epoll (linux kenel 2.6+), rtsig(实时信号)或者/dev/poll(一种类似select的模式,底层实现与SELECT基本相 同,都是采用轮训方法) SELECT模式将是默认安装模式 
--with-poll_module --without-poll_module -- Whether or not to enable the poll module. This module is enabled by default if a more suitable method such as kqueue, epoll, rtsig or /dev/poll is not discovered by configure. 
--with-http_ssl_module -- 开启HTTP SSL模块,使NGINX可以支持HTTPS请求。这个模块需要已经安装了OPENSSL,在DEBIAN上是libssl 
--with-http_realip_module -- 启用 ngx_http_realip_module 
--with-http_addition_module -- 启用 ngx_http_addition_module 
--with-http_sub_module -- 启用 ngx_http_sub_module 
--with-http_dav_module -- 启用 ngx_http_dav_module 
--with-http_flv_module -- 启用 ngx_http_flv_module 
--with-http_stub_status_module -- 启用 “server status“ 页 
--without-http_charset_module -- 禁用 ngx_http_charset_module 
--without-http_gzip_module -- 禁用 ngx_http_gzip_module. 如果启用,需要 zlib 。 
--without-http_ssi_module -- 禁用 ngx_http_ssi_module 
--without-http_userid_module -- 禁用 ngx_http_userid_module 
--without-http_access_module -- 禁用 ngx_http_access_module 
--without-http_auth_basic_module -- 禁用 ngx_http_auth_basic_module 
--without-http_autoindex_module -- 禁用 ngx_http_autoindex_module 
--without-http_geo_module -- 禁用 ngx_http_geo_module 
--without-http_map_module -- 禁用 ngx_http_map_module 
--without-http_referer_module -- 禁用 ngx_http_referer_module 
--without-http_rewrite_module -- 禁用 ngx_http_rewrite_module. 如果启用需要 PCRE 。 
--without-http_proxy_module -- 禁用 ngx_http_proxy_module 
--without-http_fastcgi_module -- 禁用 ngx_http_fastcgi_module 
--without-http_memcached_module -- 禁用 ngx_http_memcached_module 
--without-http_limit_zone_module -- 禁用 ngx_http_limit_zone_module 
--without-http_empty_gif_module -- 禁用 ngx_http_empty_gif_module 
--without-http_browser_module -- 禁用 ngx_http_browser_module 
--without-http_upstream_ip_hash_module -- 禁用 ngx_http_upstream_ip_hash_module 
--with-http_perl_module -- 启用 ngx_http_perl_module 
--with-perl_modules_path=PATH -- 指定 perl 模块的路径 
--with-perl=PATH -- 指定 perl 执行文件的路径 
--http-log-path=PATH -- 指定access log 文件的路径 
--http-client-body-temp-path=PATH -- 指定http客户端请求缓存文件存放的目录 
--http-proxy-temp-path=PATH -- 指定http反向代理缓存文件存放的目录 
--http-fastcgi-temp-path=PATH -- 指定http fastCGI缓存文件存放的目录 
--without-http -- 禁用 HTTP server 
--with-mail -- 启用 IMAP4/POP3/SMTP 代理模块 
--with-mail_ssl_module -- 启用 ngx_mail_ssl_module 
--with-cc=PATH -- 指定 C 编译器的路径 
--with-cpp=PATH -- 指定 C 预处理器的路径 
--with-cc-opt=OPTIONS -- Additional parameters which will be added to the variable CFLAGS. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-cc-opt=“-I /usr/local/include“. If we are using select() and it is necessary to increase the number of file descriptors, then this also can be assigned here: --with-cc-opt=“-D FD_SETSIZE=2048″. 
--with-ld-opt=OPTIONS -- Additional parameters passed to the linker. With the use of the system library PCRE in FreeBSD, it is necessary to indicate --with-ld-opt=“-L /usr/local/lib“. 
--with-cpu-opt=CPU -- 为特定的 CPU 编译,有效的值包括:pentium, pentiumpro, pentium3, pentium4, athlon, opteron, amd64, sparc32, sparc64, ppc64 
--without-pcre -- 禁止 PCRE 库的使用。同时也会禁止 HTTP rewrite 模块。在 “location“ 配置指令中的正则表达式也需要 PCRE 。 
--with-pcre=DIR -- 指定 PCRE 库的源代码的路径。 
--with-pcre-opt=OPTIONS -- 设置pcre库的源代码路径 
--with-md5=DIR -- 设置MD5库的源代码 路径 
--with-md5-opt=OPTIONS -- MD5库的额外编译选项 
--with-md5-asm -- MD5汇编源码 
--with-sha1=DIR -- sha1库的 源代码 路径 
--with-sha1-opt=OPTIONS -- sha1库的 额外编译选项 
--with-sha1-asm -- 使用sha1 汇编源码 
--with-zlib=DIR -- zlib库的源代码路径 
--with-zlib-opt=OPTIONS -- zlib库的编译选项 
--with-zlib-asm=CPU -- zlib库针对CPU优化,值有: pentium, pentiumpro 
--with-openssl=DIR -- openssl库的源代码路径 
--with-openssl-opt=OPTIONS -- openssl编译选项 
--with-debug -- 启用调试日志 
--add-module=PATH -- 增加第三方模块所在的路径 
在不同版本间,选项可能会有些许变化,使用 ./configure --help 命令来检查。

2、预编译包-Pre-Built Packages
For automatic updates of the pre-built Linux packages it is possible to configure the yum repository for RHEL/CentOS, or the apt repository for Debian/Ubuntu.

To set up the yum repository for the RHEL/CentOS, pick up and install the corresponding nginx-release package from the list below, containing the yum config and the public key necessary to check signed RPMs:

RHEL 5
http://nginx.org/packages/rhel/5/noarch/RPMS/nginx-release-rhel-5-0.el5.ngx.noarch.rpm

RHEL 6
http://nginx.org/packages/rhel/6/noarch/RPMS/nginx-release-rhel-6-0.el6.ngx.noarch.rpm

CentOS 5
http://nginx.org/packages/centos/5/noarch/RPMS/nginx-release-centos-5-0.el5.ngx.noarch.rpm

CentOS 6
http://nginx.org/packages/centos/6/noarch/RPMS/nginx-release-centos-6-0.el6.ngx.noarch.rpm

Then run the following command:
yum install nginx

As an alternative, a repository config can be added manually. Create the file named /etc/yum.repos.d/nginx.repo with the following contents:
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/OS/OSRELEASE/$basearch/
gpgcheck=0
enabled=1


Replace “OS“ with “rhel“ or “centos“, depending on the distribution used, and “OSRELEASE“ with “5“ or “6“, for 5.x or 6.x versions, respectively. 

For Debian 6 append the following contents to /etc/apt/sources.list:
deb http://nginx.org/packages/debian/ squeeze nginx
deb-src http://nginx.org/packages/debian/ squeeze nginx


Then run the following commands:
apt-get update
apt-cache show nginx
apt-get install nginx


3、简单配置优化Nginx
修改 /etc/nginx/nginx.conf,下面只列出修改的部分。
#根据CPU 核心processes,VPS下几个核心几个processes,独立服务器可x2 
worker_processes 4; 

#启用epoll 
worker_rlimit_nofile 51200; 
events { 
worker_connections 51200; 
use epoll; 
}

#参数调整 
sendfile on; 
tcp_nopush on; 
tcp_nodelay on; 
server_tokens off; 
keepalive_timeout 50; 
server_names_hash_bucket_size 128; 
client_header_buffer_size 32k; 
large_client_header_buffers 4 32k; 
client_max_body_size 50m; 

#fastcgi优化 
fastcgi_connect_timeout 300; 
fastcgi_send_timeout 300; 
fastcgi_read_timeout 300; 
fastcgi_buffer_size 64k; 
fastcgi_buffers 4 64k; 
fastcgi_busy_buffers_size 128k; 
fastcgi_temp_file_write_size 256k; 
 
#开启gzip并优化 
gzip on; 
gzip_min_length 1k; 
gzip_buffers 4 16k; 
gzip_http_version 1.0; 
gzip_comp_level 2; 
gzip_types text/plain application/x-javascript text/css application/xml; 
gzip_vary on; 


4、Nginx日志格式log_format

nginx日志相关指令主要有两条,一条是log_format,用来设置日志格式,另外一条是access_log,用来指定日志文件的存放路径、类型、缓存大小等,一般放在nginx的默认主配置文件/etc/nginx/nginx.conf 。

nginx的log_format有很多可选的参数用于标示服务器的活动状态,默认的是:
'$remote_addr – $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

如果要记录更详细的信息需要自己修改log_format,具体可设置的参数格式及说明如下:
参数说明示例
$remote_addr客户端地址219.227.111.111
$remote_user客户端用户名称
$time_local访问时间和时区18/Jul/2018:17:00:01 +0800
$request请求的URI和HTTP协议“GET /article-10000.html HTTP/1.1“
$http_host请求地址,即浏览器中你输入的地址(IP或域名)www.us.com
198.198.120.17
$statusHTTP请求状态200
$upstream_statusupstream状态200
$body_bytes_sent发送给客户端文件内容大小1547
$http_refererurl跳转来源http://www.freeoa.net/
$http_user_agent用户终端浏览器等信息“Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; SV1; GTB7.0; .NET4.0C;
$ssl_protocolSSL协议版本TLSv1
$ssl_cipher交换数据中的算法RC4-SHA
$upstream_addr后台upstream的地址,即真正提供服务的主机地址10.20.30.40:80
$request_time整个请求的总时间0.165
$upstream_response_time请求过程中,upstream响应时间0.002


5、Nginx的ssl配置

# from https://cipherli.st/
# and https://raymii.org/s/tutorials/Strong_SSL_Security_On_nginx.html

ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
ssl_ciphers "EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH";
ssl_ecdh_curve secp384r1;
ssl_session_cache shared:SSL:10m;
ssl_session_tickets off;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
add_header Strict-Transport-Security "max-age=63072000; includeSubdomains; preload";
add_header X-Frame-Options DENY;
add_header X-Content-Type-Options nosniff;
ssl_dhparam /etc/ssl/certs/dhparam.pem;

server {
    listen                  80  default_server;
    listen          443 ssl;
    server_name     www.freeoa.net;
    root            /data/none;
    index           index.psp index.html index.htm;
    
    ###ssl settings start
    ssl_protocols                   TLSv1 TLSv1.1 TLSv1.2;
    ssl_certificate                 conf/server.pem;
    ssl_certificate_key             conf/server.key;
    ssl_session_cache               shared:SSL:10m;
    ssl_session_timeout             10m;
    ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;
    ssl_prefer_server_ciphers       on;
    ###ssl settings end


性能问题,通过https访问Nginx一般会比http访问慢30%(https方式访问主要是耗Nginx服务器的cpu)通过下面的方法来优化:Nginx默认使用DHE算法来产生密匙,该加密算法效率很低。可以通过如下命令删掉kEDH算法。
ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;

解决Nginx添加OpenSSL模块编译时报错问题

这种报错情况多发生在升级过系统自带的openssl软件的情况:--with-http_ssl_module

/bin/sh: line 2: ./config: No such file or directory
make[1]: *** [/usr/local/ssl/.openssl/include/openssl/ssl.h] Error 127
make[1]: Leaving directory `/usr/local/src/nginx-1.26.1'
make: *** [build] Error 2

出错是因为Nginx在编译时并不能在/usr/local/ssl/.openssl/ 这个目录找到对应的文件,其实在/usr/local/ssl/这个目录可以发现这个目录下是没有.openssl目录的,因此修改Nginx编译时对openssl的路径选择就可以解决这个问题了。

解决方案:
打开nginx源文件下的/usr/local/src/nginx-1.x.y/auto/lib/openssl/conf文件找到这么一段代码:

CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBDL"

修改成以下代码:
CORE_INCS="$CORE_INCS $OPENSSL/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libcrypto.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBDL"

然后再进行Nginx的编译安装即可。


nginx配置及测试https

1、配置
server {
    listen    80  default_server;
    listen    443 ssl;
    server_name     sec.freeoa.net sme.freeoa.net;
    root    /data/none;
    index    index.psp index.html index.htm;

    ###ssl settings start
    ssl_protocols                   TLSv1.1 TLSv1.2;
    ssl_certificate                 /usr/local/nginx/conf/server.pem;
    ssl_certificate_key             /usr/local/nginx/conf/server.key;
    ssl_session_cache               shared:SSL:10m;
    ssl_session_timeout             10m;
    ssl_ciphers ALL:!kEDH!ADH:RC4+RSA:+HIGH:+EXP;
    ssl_prefer_server_ciphers       on;
    ###ssl settings end


2、性能比较:
通过https访问Nginx一般会比http访问慢30%(https方式访问主要是耗Nginx服务器的cpu)通过下面实验验证。

nginx后端挂了5台java服务器,java服务器中有个简单的java程序,从redis缓存随机读取一个value值输出到前端(挂的java服务器越多,对nginx压力越大)。压测nginx,3000并发,一共请求30000次,返回结果都是200的情况下进行对比。

实验结果:
A、服务器负载对比:
https访问,服务器cpu最高可以达到20%,而http的访问,服务器cpu基本在1%左右;无论那种访问,nginx服务器负载、内存都不高;
    
B、nginx吞吐量对比(qps):
https访问,30000次请求花了28s;(是http的3倍)
http访问,30000次请求花了9s;

统计qps时,每次清空nginx日志,然后加压。

注:不能持续加压,否则无限加大压力后往往是后端java服务出现瓶颈,导致返回给nginx的响应变慢,从而使得nginx压力变小。


6、理解和配置超时时间

用来设置请求资源和服务器返回的时间,保证一个请求占用固定时间,超出后报504超时,这样可以保证一个请求占用过长时间。

fastcgi主要参数:
使用nginx服务器如果遇到timeou情况时可以如下设置参数,使用fastcgi:
fastcgi_connect_timeout 75;  链接
fastcgi_read_timeout 600;  读取
fastcgi_send_timeout 600;  发请求

这两个选项:
fastcgi_read_timeout是指fastcgi进程向nginx进程发送response的整个过程的超时时间。
fastcgi_send_timeout是指nginx进程向fastcgi进程发送request的整个过程的超时时间。

这两个选项默认都是秒(s),可以手动指定为分钟(m),小时(h)等。

其他常用参数以及参数说明:
keepalive_timeout  600;连接超时时间:1分钟,具体时间可以根据请求(例如后台导入)需要的时间来设置
proxy_connect_timeout 600;1分钟
proxy_read_timeout 600;1分钟
Nginx 处理的每个请求均有相应的超时设置。如果做好这些超时时间的限定,判定超时后资源被释放,用来处理其他的请求,以此提升 Nginx 的性能。

keepalive_timeout

HTTP 是一种无状态协议,客户端向服务器发送一个 TCP 请求,服务端响应完毕后断开连接。如果客户端向服务器发送多个请求,每个请求都要建立各自独立的连接以传输数据。

HTTP 有一个 KeepAlive 模式,它告诉 webserver 在处理完一个请求后保持这个 TCP 连接的打开状态。若接收到来自客户端的其它请求,服务端会利用这个未被关闭的连接,而不需要再建立一个连接。

KeepAlive 在一段时间内保持打开状态,它们会在这段时间内占用资源,占用过多就会影响性能。Nginx 使用 keepalive_timeout 来指定 KeepAlive 的超时时间(timeout)。指定每个 TCP 连接最多可以保持多长时间。Nginx 的默认值是 75 秒,有些浏览器最多只保持 60 秒,所以可以设定为 60 秒。若将它设置为 0,就禁止了 keepalive 连接。

# 配置段: http, server, location
keepalive_timeout 60s;


client_body_timeout

指定客户端与服务端建立连接后发送 request body 的超时时间。如果客户端在指定时间内没有发送任何内容,Nginx 返回 HTTP 408(Request Timed Out)。
# 配置段: http, server, location
client_body_timeout 20s;

客户端向服务端发送一个完整的 request header 的超时时间。如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。
# 配置段: http, server, location
client_header_timeout 10s;


send_timeout

服务端向客户端传输数据的超时时间。
# 配置段 : http, server, location
send_timeout 30s;

客户端连接nginx超时值,建议5s内。

接收客户端header超时, 默认60s, 如果60s内没有收到完整的http包头, 将返回408。


Nginx超时配置参数说明:

keepalive_timeout
语法:keepalive_timeout timeout [ header_timeout ]
默认值:75s
上下文:http server location
说明:第一个参数指定了与client的keep-alive连接超时时间。服务器将会在这个时间后关闭连接。可选的第二个参数指定了在响应头Keep-Alive: timeout=time中的time值。这个头能够让一些浏览器主动关闭连接,这样服务器就不必要去关闭连接了。没有这个参数,nginx不会发送Keep-Alive响应头(尽管并不是由这个头来决定连接是否“keep-alive”)。keepalive时间,默认75s,通常keepalive_timeout应该比client_body_timeout大。

两个参数的值可并不相同
注意不同浏览器怎么处理“keep-alive”头
MSIE和Opera忽略掉"Keep-Alive: timeout=<N>" header.
MSIE保持连接大约60-65秒,然后发送TCP RST
Opera永久保持长连接
Mozilla keeps the connection alive for N plus about 1-10 seconds.

proxy_connect_timeout
语法:proxy_connect_timeout time
默认值:60s
上下文:http server location
说明:该指令设置与upstream server的连接超时时间,有必要记住,这个超时不能超过75秒。

这个不是等待后端返回页面的时间,那是由proxy_read_timeout声明的。如果你的upstream服务器起来了,但是hanging住了(例如,没有足够的线程处理请求,就会把请求放到请求池里稍后处理),那么这个声明是没有用的,由于与upstream服务器的连接已经建立了。

proxy_read_timeout
语法:proxy_read_timeout time
默认值:60s
上下文:http server location
说明:该指令设置与代理服务器的读超时时间。它决定了nginx会等待多长时间来获得请求的响应。这个时间不是获得整个response的时间,而是两次reading操作的时间。

client_header_timeout
语法:client_header_timeout time
默认值:60s
上下文:http server
说明 指定等待client发送一个请求头的超时时间(例如:GET / HTTP/1.1).仅当在一次read中,没有收到请求头,才会算成超时。如果在超时时间内,client没发送任何东西,nginx返回HTTP状态码408(“Request timed out”)。接收客户端body超时, 默认60s, 如果连续的60s内没有收到客户端的1个字节, 返回408。

client_body_timeout
语法:client_body_timeout time
默认值:60s
上下文:http server location
说明:该指令设置请求体(request body)的读超时时间。仅当在一次readstep中,没有得到请求体,就会设为超时。超时后,nginx返回HTTP状态码408("Request timed out")

lingering_timeout
语法:lingering_timeout time
默认值:5s
上下文:http server location
说明:lingering_close生效后,在关闭连接前,会检测是否有用户发送的数据到达服务器,如果超过lingering_timeout时间后还没有数据可读,就直接关闭连接;否则,必须在读取完连接缓冲区上的数据并丢弃掉后才会关闭连接。可以理解为TCP连接关闭时的SO_LINGER延时设置,默认5s。

resolver_timeout
语法:resolver_timeout time
默认值:30s
上下文:http server location
说明:该指令设置DNS解析超时时间,域名解析超时时间,默认30s。

proxy_send_timeout
语法:proxy_send_timeout time
默认值:60s
上下文:http server location
说明:这个指定设置了发送请求给upstream服务器的超时时间。超时设置不是为了整个发送期间,而是在两次write操作期间。如果超时后,upstream没有收到新的数据,nginx会关闭连接。

proxy_upstream_fail_timeout(fail_timeout)
语法:server address [fail_timeout=30s]
默认值:10s
上下文:upstream
说明:Upstream模块下 server指令的参数,设置了某一个upstream后端失败了指定次数(max_fails)后,该后端不可操作的时间,默认为10秒。


发送数据至客户端超时时间, 默认60s, 如果连续的60s内客户端没有收到1个字节, 连接关闭。

Syntax: send_timeout time;
Default: send_timeout 60s;
Context: http, server, location
Sets a timeout for transmitting a response to the client. The timeout is set only between two successive write operations,not for the transmission of the whole response. If the client does not receive anything within this time, the connection is closed.

nginx与upstream server的连接超时时间。

Syntax: proxy_connect_timeout time;
Default: proxy_connect_timeout 60s;
Context: http, server, location
Defines a timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.

nginx接收upstream server数据超时时间, 默认60s, 如果连续的60s内没有收到1个字节, 连接关闭。

Syntax: proxy_read_timeout time;
Default: proxy_read_timeout 60s;
Context: http, server, location
Defines a timeout for reading a response from the proxied server. The timeout is set only between two successive read operations,not for the transmission of the whole response. If the proxied server does not transmit anything within this time, the connection is closed.

nginx发送数据至upstream server超时时间, 默认60s, 如果连续的60s内没有发送1个字节, 连接关闭。

Syntax: proxy_send_timeout time;
Default: proxy_send_timeout 60s;
Context: http, server, location
Sets a timeout for transmitting a request to the proxied server. The timeout is set only between two successive write operations,not for the transmission of the whole request. If the proxied server does not receive anything within this time, the connection is closed.


当使用nginx作为反向代理时,为了支持长连接,需要注意这两点:
从client到nginx的连接是长连接
从nginx到后端server的连接是长连接


1、保持与client的长连接

默认情况下,nginx已经自动开启了对client连接的keep alive支持(同时client发送的HTTP请求要求keep alive)。一般场景可以直接使用,但是对于一些比较特殊的场景,还是有必要调整个别参数(keepalive_timeout和keepalive_requests)。
http {
    keepalive_timeout  120s 120s;
    keepalive_requests 10000;
}

1)、keepalive_timeout
语法:
keepalive_timeout timeout [header_timeout];
第一个参数:设置keep-alive客户端连接在服务器端保持开启的超时值(默认75s);值为0会禁用keep-alive客户端连接;
第二个参数:可选、在响应的header域中设置一个值"Keep-Alive: timeout=time";通常可以不用设置;

注:keepalive_timeout默认75s,一般情况下也够用,对于一些请求比较大的内部服务器通讯的场景,适当加大为120s或者300s;

2)、keepalive_requests:
keepalive_requests指令用于设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100。这个参数的真实含义,是指一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。

大多数情况下当QPS(每秒请求数)不是很高时,默认值100凑合够用。但对于一些QPS比较高(比如超过10000QPS,甚至达到30000,50000甚至更高)的场景,默认的100就显得太低。

简单计算一下,QPS=10000时,客户端每秒发送10000个请求(通常建立有多个长连接),每个连接只能最多跑100次请求,意味着平均每秒钟就会有100个长连接因此被nginx关闭。同样意味着为了保持QPS,客户端不得不每秒中重新新建100个连接。因此,就会发现有大量的TIME_WAIT的socket连接(即使此时keep alive已经在client和nginx之间生效)。因此对于QPS较高的场景,非常有必要加大这个参数,以避免出现大量连接被生成再抛弃的情况,减少TIME_WAIT。

2、保持与后端服务器的长连接

为了让nginx和后端server(常称为upstream)之间保持长连接,典型设置如下:(默认nginx访问后端都是用的短连接(HTTP 1.0),一个请求来了,Nginx 新开一个端口和后端建立连接,后端执行完毕后主动关闭该链接)。

http {
    upstream  BACKEND {
        server   192.168.0.1:8080  weight=1 max_fails=2 fail_timeout=30s;
        server   192.168.0.2:8080  weight=1 max_fails=2 fail_timeout=30s;
        keepalive 300;//这个很重要
    }
server {
        listen 8080 default_server;
        server_name "";
        location /  {
            proxy_pass http://BACKEND;
            proxy_set_header Host  $Host;
            proxy_set_header x-forwarded-for $remote_addr;
            proxy_set_header X-Real-IP $remote_addr;
            add_header Cache-Control no-store;
            add_header Pragma  no-cache;
            proxy_http_version 1.1; //这两个参数也最好设置
            proxy_set_header Connection "";
        }
    }
}

1)、location中有两个参数需要设置
http {
    server {
        location /  {
            proxy_http_version 1.1; //这两个参数也最好设置
            proxy_set_header Connection "";
        }
    }
}

HTTP协议中对长连接的支持是从1.1版本之后才有的,因此最好通过proxy_http_version指令设置为"1.1";而"Connection" header应该被清理。清理的意思,大致是清理从client过来的http header,因为即使是client和nginx之间是短连接,nginx和upstream之间也是可以开启长连接的。这种情况下必须清理来自client请求中的"Connection" header。

2)、upstream中的keepalive设置
此处keepalive的含义不是开启、关闭长连接的开关;也不是用来设置超时的timeout;更不是设置长连接池最大连接数。官方解释:
The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(设置到upstream服务器的空闲keepalive连接的最大数量)

When this number is exceeded, the least recently used connections are closed. (当这个数量被突破时,最近使用最少的连接将被关闭)

It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open.(特别提醒:keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量)

我们先假设一个场景: 有一个HTTP服务,作为upstream服务器接收请求,响应时间为100毫秒。如果要达到10000 QPS的性能,就需要在nginx和upstream服务器之间建立大约1000条HTTP连接。nginx为此建立连接池,然后请求过来时为每个请求分配一个连接,请求结束时回收连接放入连接池中,连接的状态也就更改为idle。我们再假设这个upstream服务器的keepalive参数设置比较小,比如常见的10。

A)、假设请求和响应是均匀而平稳的,那么这1000条连接应该都是一放回连接池就立即被后续请求申请使用,线程池中的idle线程会非常的少,趋进于零,不会造成连接数量反复震荡。

B)、显示中请求和响应不可能平稳,我们以10毫秒为一个单位,来看连接的情况(注意场景是1000个线程+100毫秒响应时间,每秒有10000个请求完成),我们假设应答始终都是平稳的,只是请求不平稳,第一个10毫秒只有50,第二个10毫秒有150:

下一个10毫秒,有100个连接结束请求回收连接到连接池,但是假设此时请求不均匀10毫秒内没有预计的100个请求进来,而是只有50个请求。注意此时连接池回收了100个连接又分配出去50个连接,因此连接池内有50个空闲连接。

然后注意看keepalive=10的设置,这意味着连接池中最多容许保留有10个空闲连接。因此nginx不得不将这50个空闲连接中的40个关闭,只留下10个。再下一个10个毫秒,有150个请求进来,有100个请求结束任务释放连接。150 - 100 = 50,空缺了50个连接,减掉前面连接池保留的10个空闲连接,nginx不得不新建40个新连接来满足要求。

C)、同样如果假设相应不均衡也会出现上面的连接数波动情况。

造成连接数量反复震荡的一个推手,就是这个keepalive 这个最大空闲连接数。毕竟连接池中的1000个连接在频繁利用时,出现短时间内多余10个空闲连接的概率实在太高。因此为了避免出现上面的连接震荡,必须考虑加大这个参数,比如上面的场景如果将keepalive设置为100或者200,就可以非常有效的缓冲请求和应答不均匀。

总结:keepalive 这个参数一定要小心设置,尤其对于QPS比较高的场景,推荐先做一下估算,根据QPS和平均响应时间大体能计算出需要的长连接的数量。比如前面10000 QPS和100毫秒响应时间就可以推算出需要的长连接数量大概是1000,然后将keepalive设置为这个长连接数量的10%到30%。也可以直接设置为keepalive=1000之类的,一般都能满足需要。

3、当出现大量TIME_WAIT的情况

1)、导致 nginx端出现大量TIME_WAIT的情况有两种:
keepalive_requests设置比较小,高并发下超过此值后nginx会强制关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx出现TIME_WAIT)

keepalive设置的比较小(空闲数太小),导致高并发下nginx会频繁出现连接数震荡(超过该值会关闭连接),不停的关闭、开启和后端server保持的keepalive长连接;

2)、导致后端server端出现大量TIME_WAIT的情况:
nginx没有打开和后端的长连接,即没有设置proxy_http_version 1.1和proxy_set_header Connection ""从而导致后端server每次关闭连接,高并发下就会出现server端出现大量TIME_WAIT。


7、location里的root、alias指令用法

nginx指定文件路径有两种方式root和alias,这两个指令的使用方法和作用域:
[root]
语法:root path
默认值:root html
配置段:http、server、location、if

[alias]
语法:alias path
配置段:location

root与alias主要区别在于nginx如何解释location后面的uri,这会使两者分别以不同的方式将请求映射到服务器文件上。
root的处理方式是:root路径+location路径
alias的处理方式是:使用alias路径替换location路径

alias是一个目录别名的定义,root则是最上层目录的定义。还有一个重要的区别是alias后面必须要用'/'结束,否则会找不到文件的,而root则可有可无,alias目录和root目录是有区别的:
1)alias指定的目录是准确的,即location匹配访问的path目录下的文件直接是在alias目录下查找的;alias在使用正则匹配时,必须捕捉要匹配的内容并在指定的内容处使用。

2)root指定的目录是location匹配访问的path目录的上一级目录,这个path目录一定要是真实存在root指定目录下的;

3)使用alias标签的目录块中不能使用rewrite的break(具体原因不明);alias只能位于location块中。alias指定的目录后面必须要加上"/"符号!

4)alias虚拟目录配置中,location匹配的path目录如果后面不带"/",那么访问的url地址中这个path目录后面加不加"/"不影响访问,访问时它会自动加上"/";但是如果location匹配的path目录后面加上"/",那么访问的url地址中这个path目录必须要加上"/",访问时它不会自动加上"/";如果不加"/",访问就会失败!
    
5)root目录配置中,location匹配的path目录后面带不带"/",都不会影响访问。

6)一般情况下,在nginx配置中的建议是:在location /中配置root目录;在location /path中配置alias虚拟目录。

root实例:
location ^~ /t/ {
    root /www/root/html/;
}

如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/t/a.html的文件。

alias实例:
location ^~ /t/ {
 alias /www/root/html/new_t/;
}

如果一个请求的URI是/t/a.html时,web服务器将会返回服务器上的/www/root/html/new_t/a.html的文件。注意这里是new_t,因为alias会把location后面配置的路径丢弃掉,把当前匹配到的目录指向到指定的目录。

举例说明(比如nginx配置的域名是www.freeoa.net):

示例一
location /jhon/ {
    alias /home/www/jhon/;
}

在上面alias虚拟目录配置下,访问http://www.freeoa.net/jhon/a.html实际指定的是/home/www/jhon/a.html。

注意:alias指定的目录后面必须要有"/",即/home/www/jhon/不能改成/home/www/jhon

上面的配置也可以改成root目录配置,这样nginx就会去/home/www/jhon下寻找http://www.freeoa.net/jhon的访问资源,两者配置后的访问效果是一样的!
location /jhon/ {
    root /home/www/;
}

示例二
上面的例子中alias设置的目录名和location匹配访问的path目录名一致,这样可以直接改成root目录配置,那要是不一致呢?
location /web/ {
    alias /home/www/html/;
}

访问http://www.freeoa.net/web的时候就会去/home/www/html/下寻找访问资源。这样的话,还不能直接改成root目录配置。如果非要改成root目录配置,就只能在/home/www下将html->web(做软连接),如下:
location /web/ {
    root /home/www/;
}

# ln -s /home/www/web /home/www/html //即保持/home/www/web和/home/www/html内容一直


8、修改Nginx源代码以实现记录慢查询

一些商业产品中会有url接口响应时间,实时汇总显示功能。可以理解为web接口的慢查询,与sql的慢查询有异曲同工之妙。

1)、其实nginx本身就带有接口响应时间的功能,只不过还需要改造下,比如说单独记录超过1000ms(1秒)的响应,并写入数据库中。要注意的是并不建议将记录直接写入数据库中,因为数据库可能会成为nginx的负担,间接写入即可。需要简单修改下log模块,涉及文件 ngx_http_log_module.c 通常位于nginx-1.17.9/src/http/modules/ngx_http_log_module.c

大约在838行, 找到ngx_http_log_request_time函数并修改如下:
static u_char *
ngx_http_log_request_time(ngx_http_request_t *r, u_char *buf,ngx_http_log_op_t *op){
    ngx_time_t *tp;
    ngx_msec_int_t ms;
    time_t t = time(NULL);
    struct tm *loc_time = localtime(&t);
    tp = ngx_timeofday();
    u_char slow_log[2048];
    memset(slow_log, 0, sizeof(slow_log));
    ms = (ngx_msec_int_t) ((tp->sec - r->start_sec) * 1000 + (tp->msec - r->start_msec));
    ms = ngx_max(ms, 0);
    ngx_sprintf(slow_log, "%04d/%02d/%02d %02d:%02d:%02d %V %V?%V waste time %T.%03M\n",
            loc_time->tm_year + 1900, loc_time->tm_mon + 1, loc_time->tm_mday,
            loc_time->tm_hour, loc_time->tm_min, loc_time->tm_sec,
            &r->headers_in.server, &r->uri, &r->args, (time_t) ms / 1000,
            ms % 1000);
    int logfd;
    if ((logfd = open("/var/log/nginx/nginx_slow.log", O_RDWR | O_CREAT | O_APPEND,S_IRUSR | S_IWUSR)) == -1) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"can not open file:logfile\n");
    }
    char Server_name[256];
    const char *server_name = "%.*s";
    memset(Server_name, 0, sizeof(Server_name));
    snprintf((char *) Server_name, sizeof(Server_name), server_name,r->headers_in.server.len, r->headers_in.server.data);
    /* 只记录大于1秒的并且域名不是grafana.freeoa.net  */
    if (ms > 1000 && strcmp("grafana.freeoa.net", Server_name) != 0)
        write(logfd, slow_log, strlen((char *)slow_log));
    close(logfd);
    return ngx_sprintf(buf, "%T.%03M", (time_t) ms / 1000, ms % 1000);
}

之后再编译即可

# ./configure --prefix=/usr/local/nginx1.17.9
# make -j4 ; make install

#mkdir -p /var/log/nginx; chmod -R 640 /var/log/nginx

简单配置如下:
server {
    listen *:80;
    server_name slow.freeoa.net;
    location / {
        proxy_set_header   Host    $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   REMOTE-HOST $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://127.0.0.1:3000;
    }
}
 
启动nginx即可,如果接口响应时间超过1秒,那么将会在慢日志文件(/var/log/nginx/nginx_slow.log)中就会有记录了。但这只是将慢查询记录而已,我们还需要排序以及可示化的展示。记录也可以直接写入mysql但我并没有这么做,为什么不直接写入呢,原因是如果mysql如果响应慢则会影响nginx的响应,而写入磁盘就不会出现这种情形了。接下来就简单实现下把数据同步写入到数据库中。

2)、首先建用于同步写入记录的表

 CREATE TABLE `nginx_slow` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `dt` datetime NOT NULL,
  `server_name` varchar(255) NOT NULL,
  `url` varchar(255) NOT NULL,
  `used_time` decimal(11,3) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

之后实现一个同步数据的工具,这里用shell即可实现,代码如下:
#!/bin/bash
#    insert_mysql.sh
set -x
if [ $# != 1 ]
  then
 echo "Usage insert_mysql.sh /var/log/nginx/nginx_slow.log"
  exit 1
fi
tail -n 1 -f ${1}|while read var
do
    value=`echo $var|awk '{print $3}'`
    value2=`echo $var|awk '{print $4}'`
    value3=`echo $var|awk '{print $7}'`
    echo "$value $value2 $value3"
    mysql -h 127.0.0.1 -usuper -pxxxxxxxxx -e "use nginx; INSERT INTO nginx_slow( dt, server_name, url, used_time) VALUES ( now(), '${value}', '${value2}', '$value3');"
done

运行脚本就可以同步写入数据了:
# ./insert_mysql.sh /var/log/nginx/nginx_slow.log

建议后台运行
# nohub ./insert_mysql.sh /var/log/nginx/nginx_slow.log >> /dev/null 2>&1 &

3)、慢查询的展示

最后在grafana里面配置下当天接口慢查询TOP 20即可,相关语句:
select n.url,avg(n.used_time) 平均响应时长
from nginx_slow n
WHERE n.`dt` > curdate()
group by 1 order by 2 desc limit 20

或者

select n.server_name,n.url,avg(n.used_time) 平均响应时长,count(1) 调用次数
from nginx_slow n
WHERE n.`dt` > curdate()
group by 1,2 order by 3 desc limit 50


9、从源码编译nginx与新版本的openssl

The compiler environment GCC g+ + to develop library, advance installed, here you have it installed by default. The debian platform compiler can use the following command:
apt-get install build-essentialapt-get install libtool

CentOS platform compiler environment using the following command
Install make:
yum -y install gcc automake autoconf libtool make

Install g++:
yum install gcc gcc-c++

相关的依赖:PCRE,ZLIB,SSL

make: *** No rule to make target build, needed bydefault. Stop../configure: error: SSL modules require the OpenSSL library.You can either do not enable the modules, or install the OpenSSL libraryinto the system, or build the OpenSSL library statically from the source with nginx by using --with-openssl=<path> option.

According to the installation or fourth step method Debian
apt-get install openssl libssl-dev

CentOS
yum -y install openssl openssl-devel


先查看一下原来的nginx需要哪些依赖库
# ldd /apps/nginx/sbin/nginx
...
    libssl.so.10 => /lib64/libssl.so.10 (0x00007f9c480ff000)
    libcrypto.so.10 => /lib64/libcrypto.so.10 (0x00007f9c47c9c000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f9c47a86000)
    libperl.so => /usr/lib64/perl5/CORE/libperl.so (0x00007f9c476f8000)
...

# file /lib64/libssl.so.10
/lib64/libssl.so.10: symbolic link to `libssl.so.1.0.2k'

由于openss 1.0.2已经很旧了,为了尽可能的满足除开nginx的需要外,同时也满足像openssh此类的软件对这年openssl基础软件的依赖,决定另外安装最新稳定的openssl1.1.1。

/apps/nginx/sbin/nginx -V

得到原来的编译参数,做为新版本nginx的基础参数,指定了新的openssl安装路径(--with-openssl)。

--prefix=/apps/nginx --with-http_ssl_module --user=nginx --group=nginx --with-http_gzip_static_module --with-http_stub_status_module --with-stream --with-pcre --with-http_perl_module --with-file-aio --without-http_limit_conn_module --add-module=/apps/soft/echo-nginx-module-0.62 --with-openssl=/opt/openssl

--prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_gzip_static_module --with-http_stub_status_module --with-stream --with-pcre --with-http_perl_module --with-file-aio --with-http_realip_module --add-module=/apps/soft/echo-nginx-module-0.62 --with-openssl=/opt/openssl


[root@freeoa nginx-1.18.0]# make -j2
make -f objs/Makefile
make[1]: Entering directory `/root/soft/nginx-1.18.0'
cd /opt/openssl/include \
&& if [ -f Makefile ]; then make clean; fi \
&& ./config --prefix=/opt/openssl/include/.openssl no-shared no-threads  \
&& make \
&& make install_sw LIBDIR=lib
/bin/sh: line 2: ./config: No such file or directory
make[1]: *** [/opt/openssl/include/.openssl/include/openssl/ssl.h] Error 127
make[1]: Leaving directory `/root/soft/nginx-1.18.0'
make: *** [build] Error 2

根据报错信息可知出错是因为nginx在编译时并不能在'/opt/openssl/include/.openssl/'这个目录找到对应的文件,事实上在这个目录下是没有.openssl的,因此就需要修改nginx编译时对openssl的路径选择就可以解决了。解决方案:
打开nginx源文件下的nginx-version/auto/lib/openssl/conf文件,找到这么一段代码:
CORE_INCS="$CORE_INCS $OPENSSL/.openssl/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/.openssl/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/.openssl/lib/libcrypto.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBDL"

修改成以下代码:
CORE_INCS="$CORE_INCS $OPENSSL/include"
CORE_DEPS="$CORE_DEPS $OPENSSL/include/openssl/ssl.h"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libssl.a"
CORE_LIBS="$CORE_LIBS $OPENSSL/lib/libcrypto.a"
CORE_LIBS="$CORE_LIBS $NGX_LIBDL"


然后再继续nginx的编译安装即可,下面的输出可见的openssl的引用。

objs/addon/src/ngx_http_echo_module.o \
objs/addon/src/ngx_http_echo_util.o \
...
objs/addon/src/ngx_http_echo_request_info.o \
objs/addon/src/ngx_http_echo_subrequest.o \
objs/addon/src/ngx_http_echo_foreach.o \
objs/ngx_modules.o \
-ldl -lpthread -lcrypt -lpcre /opt/openssl/lib/libssl.a /opt/openssl/lib/libcrypto.a -ldl -lpthread -lz \
-Wl,--enable-new-dtags -Wl,-rpath,/usr/lib64/perl5/CORE -fstack-protector -L/usr/lib64/perl5/CORE -lperl -lresolv -lnsl -ldl -lm -lcrypt -lutil -lpthread -lc \
-Wl,-E
sed -e "s|%%PREFIX%%|/apps/nginx|" \
    -e "s|%%PID_PATH%%|/apps/nginx/logs/nginx.pid|" \
    -e "s|%%CONF_PATH%%|/apps/nginx/conf/nginx.conf|" \
    -e "s|%%ERROR_LOG_PATH%%|/apps/nginx/logs/error.log|" \
    < man/nginx.8 > objs/nginx.8
make[1]: Leaving directory `/root/soft/nginx-1.18.0'

编译完成后看一下相应的编译参数:
# /apps/nginx/sbin/nginx -V
nginx version: nginx/1.18.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC)
built with OpenSSL 1.1.1h  22 Sep 2020
TLS SNI support enabled
configure arguments: --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ssl_module --with-http_gzip_static_module --with-http_stub_status_module --with-stream --with-pcre --with-http_perl_module --with-file-aio --with-http_realip_module --add-module=/root/soft/echo-nginx-module-0.62 --with-openssl=/opt/openssl

看一下它的ldd情况中有没有引用到新版本的openssl库:
...
    libcrypt.so.1 => /lib64/libcrypt.so.1 (0x00007f53de98d000)
    libpcre.so.1 => /lib64/libpcre.so.1 (0x00007f53de72b000)
    libz.so.1 => /lib64/libz.so.1 (0x00007f53de515000)
    libperl.so => /usr/lib64/perl5/CORE/libperl.so (0x00007f53de187000)
...

可以发现没有libssl相关的链接库,是什么原因呢?

原因在上文有所提及,指定(非系统默认)编译过的openssl,会被静态编译进nginx二进制文件中,同时会发现它的硬盘占用会比使用系统默认链接库大1-2M。

10、Nginx作为静态资源服务器

Nginx是一个轻量、高性能的服务器,平时除了可以作为反向代理服务器外,还可以将其作为一个静态资源服务器:发布静态资源,比如文件、图片等多媒体文件供他人下载。

资源目录(root)

定义好root资源目录,该目录作为Nginx搜索文件的根目录,比如访问资源 example.zip,Nginx会在root目录下搜索该文件。定义root目录的方式如下:
server {
    root /www/data;
    location / {
    }

    location /images/ {
    }

    location ~ \.(mp3|mp4) {
        root /www/media;
    }
}

root指令用来定义资源目录,它可以配置在http{}、server{}和location{}配置上下文中。如上所示,定义了服务器默认的搜索目录为/www/data,当访问/images/目录时,Nginx会在/www/data/images/目录下搜索文件。但是如果访问的文件后缀是mp3或mp4,则会在/www/media/目录下搜索文件。因为location指令的root指令会覆盖server的root指令。

默认情况下,如果访问一个/结尾(目录)的资源,Nginx会去查找文件index.html,比如访问/images/,Nginx会去查找/www/data/images/index.html文件。一般可能会有一堆资源,比如在/www/data/images/目录中可能存在成百上千的图片,这时候可以让Nginx自动生成index.html并返回给客户端(不会落盘):
location /images/ {
    autoindex on;
}

'autoindex on'指令将打开自动生成index.html功能,当访问/images/时,Nginx会自动生成一个index.html文件,列出/www/data/images/目录下的所有文件。如果遇到目录,它也会支持递归访问。除了index.html作为默认的索引文件外还可以自定义索引文件:
location / {
    index index.$geo.html index.htm index.html;
}

它将按顺序查找索引文件,如果找到则返回,否则返回404。Nginx在搜索文件时,如果文件不存在,它还会尝试内部重定向到其他的location(如果通过index组装起来的资源路径能够命中其他的location的话),比如:
location / {
    root /data;
    index index.html index.php;
}

location ~ \.php {
    fastcgi_pass localhost:8000;
    #...
}

如果访问/path/,且/data/path/index.html不存在,但/data/path/index.php存在对应的location,Nginx会尝试内部重定向到location ~ \.php,然后交给fastcgi处理。

尝试多种选择

try_files指令可以检查指定的文件或目录是否存在,如果不存在则触发内部重定向,或者返回错误码。

server {
    root /www/data;
    location /images/ {
        try_files $uri /images/default.gif;
    }
}

如上所示,如果访问/images/目录下的文件不存在,Nginx会行部重定向到最后一个参数的/images/default.gif文件。也可以用=code的方式来返回错误码,而不是内部重定向:
location / {
    try_files $uri $uri/ $uri.html =404;
}

如上所示,如果访问的文件不存在,Nginx会尝试查找uri/目录,如果还是不存在,会尝试查找uri.html文件,如果还是不存在,返回404错误码。内部重定向也支持命名location:
location / {
    try_files $uri $uri/ @backend;
}

location @backend {
    proxy_pass http://backend.example.com;
}

如上所示,如果访问的文件不存在,Nginx会尝试查找$uri/目录,如果还是不存在,会尝试查找@backend命名的location,然后交给proxy_pass处理。

优化性能

打开sendfile、tcp_nopush
location /mp3 {
    sendfile            on;
    tcp_nopush          on;
    #...
}

对于发送文件来说,sendfile可以提高性能并减低cpu的消耗,tcp_nopush则可以减少网络延迟。

11、Reload及其背后的信号

先从常见的的重置说起:
nginx -s reopen  重新打开日志文件。
nginx -s reload 平滑的重启,配置重载。

reload 命令加载修改后的配置文件,命令下达后发生如下事件:
1.Nginx的master进程检查配置文件的正确性,若是错误则返回错误信息,nginx继续采用原配置文件进行工作(因为worker未受到影响)

2.Nginx启动新的worker进程,采用新的配置文件

3.Nginx将新的请求分配新的worker进程

4.Nginx等待以前的worker进程的全部请求已经都返回后,关闭相关worker进程

5.重复上面过程,知道全部旧的worker进程都被关闭掉。

所以在重启之后,master的进程号不变,worker的进程号会改变。

平滑重启与Reload简介
Nginx的平滑重启和Reload机制允许管理员在不中断当前服务的情况下,重新加载或更新Nginx的配置文件。这种机制的核心在于Nginx的主从进程模型,其中主进程负责管理和控制工作进程,而工作进程则负责实际处理客户端请求。

1.平滑重启(Smooth Restart):当Nginx接收到特定的系统信号时,主进程会重新加载配置文件,并启动新的工作进程。同时旧的工作进程会继续处理当前连接,直到所有连接处理完毕后再优雅退出。这种方式保证了服务的连续性,避免了因重启导致的服务中断。

2.Reload(重新加载):Reload操作实际上是一种特殊的平滑重启,它专门用于重新加载Nginx的配置文件。通过发送HUP(SIGHUP)信号给Nginx主进程,可以实现配置文件的即时更新。

平滑重启与Reload的步骤
检查配置文件:在重启或重新加载配置之前,务必检查Nginx的配置文件(nginx.conf)是否有语法错误。可以使用nginx -t命令进行测试,该命令会检查配置文件的语法并返回结果。
nginx -t -c /path/to/nginx.conf

如果配置文件有错误,Nginx会输出具体的错误信息,此时需要修正错误后再进行下一步操作。

发送信号:通过向Nginx主进程发送系统信号来触发平滑重启或Reload操作。
1.平滑重启:可以使用kill命令发送USR2信号(虽然通常使用nginx -s reload命令进行Reload操作,但USR2信号用于平滑升级,此处仅作说明)。然而,在实际应用中,我们更推荐使用nginx -s reload命令来触发平滑重启,因为它更为直观和方便。

2.Reload:使用nginx -s reload命令或向Nginx主进程发送HUP信号(SIGHUP)。这个命令会通知Nginx主进程重新加载配置文件,并启动新的工作进程来处理新的连接。

nginx -s reload
# 或者
kill -HUP $(cat /path/to/nginx.pid)

注意:/path/to/nginx.pid是Nginx主进程的PID文件路径,该文件通常位于Nginx安装目录下的logs目录中。

观察日志:在重启或重新加载过程中,应密切关注Nginx的错误日志和访问日志,以便及时发现并处理可能出现的问题。
验证结果:重启或重新加载完成后,应验证Nginx是否按预期运行,并检查新的配置是否已生效。

平滑重启与Reload的优势
服务连续性:在重启或重新加载过程中,Nginx能够继续处理当前连接,避免了服务中断。
用户体验:由于服务不会中断,用户几乎感受不到任何影响,从而保证了良好的用户体验。
灵活性:管理员可以根据需要随时更新Nginx的配置文件,而无需担心服务中断的问题。

Nginx的平滑重启与Reload机制是Nginx运维中的一项重要功能,它确保了服务的连续性和稳定性。通过掌握这一机制,管理员可以更加灵活地管理Nginx服务,提高系统的可用性和用户体验。但也不是没有副作用。上文提及了用系统的kill指令为其发送信号来控制进程的一个例程,下面就说说与信号相关的信息。

kill 命令传送信号给nginx的master进程,注意是发送给master进程:
TERM、INIT:强制退出,当前的请求不执行完成就退出,等价于 ./nginx -s stop
QUIT:优雅退出,等待请求执行完成后退出,等价于 ./nginx -s quit
HUP:重载配置文件,用新的配置文件启动新的work进程并优雅的关闭旧的work进程,等价于./nginx -s reload
USR1:重开日志,等价于./nginx -s reopen
USR2:平滑的升级nginx,拉起一个新的nginx主进程,同时做到不停止旧的nginx主进程
WINCH:优雅的关闭worker进程,发送一个WINCH信号给master进程,告知其优雅的关闭worker进程(一般不常用)

示例:
kill -TERM 5916            #强制停止nginx,等价于 ./nginx -s stop
kill -INT 7019            #强制停止nginx,等价于 ./nginx -s stop
kill -QUIT 7073            #优雅的退出nginx(master进程),等价于 ./nginx -s quit
kill -HUP 7075            #重载配置文件,等价于 ./nginx -s reload,会重新拉起新的worker进程
kill -USR1 7220            #重开日志,等价于 ./nginx -s reopen
kill -WINCH 7222            #优雅的关闭worker进程
kill -USR2 7220            #平滑启动nginx进程

再次注意:信号都是发送给nginx的master进程的。