编译安装

可以自己上nginx官方网站下载安装包,解压之后用下面命令编译安装:

1
2
3
4
./configure --prefix='/opt/nginx' --with-http_ssl_module --with-http_gzip_static_module \
--with-http_stub_status_module --with-http_v2_module --with-stream

make && make install

安装过程中涉及到相应的依赖环境下载。安装完成之后,使用/opt/nginx/sbin/nginx -v查看安全情况。

常规配置

Nginx是一个很强大的高性能Web和反向代理服务,它具有很多非常优越的特性。这里介绍下Nginx的常用配置方法。

下面看一段Nginx的常用配置:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
user root root;
worker_processes 5;

error_log  /home/xxx/_logs_/nginx_acc_err.log notice;
pid        /home/xxx/tmp/nginx_api.pid;

worker_rlimit_nofile    500000;
events {
    use epoll;
    worker_connections  500000;
}

http {
    include             /opt/nginx/conf/mime.types;
    default_type        application/octet-stream;

    tcp_nopush          on;
    keepalive_timeout   30;
    tcp_nodelay         on;

    gzip on;
    gzip_min_length     1024;
    gzip_buffers        4 8k;
    gzip_types          text/plain text/css text/javascript application/javascript;
    gzip_comp_level     9;
    gzip_proxied        any;

    port_in_redirect            off;
    access_log                  off;
    rewrite_log                 off;
    client_max_body_size        6m;
    client_body_buffer_size     128k;
    client_header_buffer_size   4k;

    # 公用证书
    ssl_certificate     /home/xxx/certificate/xxx.new.crt;
    ssl_certificate_key /home/xxx/certificate/xxx.nopwd.key;

    # -----------------------------
    # 维护中...
    #server {
    #    listen  80;
    #    root    /home/xxx/www/;
    #    error_page 500 502 503 504 = /index.html;
    #    error_page 403 404 = /index.html;
    #}

    # -----------------------------
    # 负载均衡 [API]
    upstream up_user_center_keep_session {
        server          10.10.11.31:11122;
        server          10.10.11.31:11122 backup;
    }
    server {
        listen          80;
        listen          443 ssl;
        server_name     sss.yoursite.com;

        location / {
            proxy_pass  http://up_user_center_keep_session/;
            include     _nc_proxy_env.conf;
        }
    }
    # -------------------------------
    # 服务器配置片段
    include _nc_part_proxy_server.conf;
    include _nc_part_apis.conf;
    include _nc_part_apis_gm.conf;
}

Nginx片段的示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
log_format gm_main2 '[$time_local] $remote_addr $remote_user $request $upstream_addr '
					'[$request_time] '
                    '[$request_length $bytes_sent $body_bytes_sent] ';

upstream up_api_gm_et {
    ip_hash;
    server          10.10.11.11:11111 weight=1;
    server          10.10.11.12:11111 weight=3;
}
server {
    listen          80;
    listen          443 ssl;
    server_name     site.yoursite.com;
    access_log      /home/xxx/_logs_/nginx_acc_gm_et.log gm_main;

    ssl_certificate     /home/xxx/certificate/xxx.new.crt;
    ssl_certificate_key /home/xxx/certificate/xxx.nopwd.key;
    location / {
        proxy_pass  http://up_api_gm_et/;
        include     _nc_proxy_env.conf;
    }
}

nginx可以热更新,修改好配置文件之后,热重启:

nginx -t
# 结果显示ok和success没问题,证明修改没有问题。便可重启
nginx -s reload

gzip压缩

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# 打开gzip压缩
gzip on;

# 不压缩临界值,大于1K的才压缩,一般不用改
gzip_min_length 1k;

# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流,这里设置以16k为单位的4倍申请内存
gzip_buffers 4 16k;

# 默认为http 1.1,现在99.99%的浏览器基本上都支持gzip解压了,所有无需设置此项
# gzip_http_version 1.0;

# gzip压缩比,1 最小处理速度最快,9 最大但处理最慢(传输快但比较消耗cpu)
gzip_comp_level 2;

# 要压缩的文件类型,注意"text/html"类型无论是否指定总是会被压缩的
gzip_types text/plain application/x-javascript text/css application/xml 
text/javascript application/javascript application/x-httpd-php image/jpeg image/gif image/png;

# on的话会在Header里增加"Vary: Accept-Encoding",给代理服务器用的,有的浏览器支持压缩,
# 有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩
# 我这里的浏览器肯定支持gzip压缩,所以就不开启此功能了
gzip_vary off;

# IE6对Gzip不怎么友好,不给它Gzip压缩了
gzip_disable "MSIE [1-6]\.";

参考阅读:

https://www.404bugs.com/details/1079434655764893696

error_log

Nginx的错误信息是调试Nginx服务的重要手段,属于核心功能模块(ngx_core_module)的参数,该参数的名字为error_log,可以放在Main区块中全局配置,也可以放在不同的虚拟主机中单独记录虚拟主机的错误信息。

1
2
3
4
5
6
7
# 关键字      日志文件   错误日志级别
error_log    <FILE>    <LEVEL>;

# 关键字:其中关键字error_log不能改变
# 日志文件:可以指定任意存放日志的目录
# 错误日志级别:常见的错误日志级别有[debug | info | notice | warn | error | crit | alert | emerg]
#	级别越高记录的信息越少;生产场景一般是 warn | error | crit 这三个级别之一

注意:不要配置info等级较低的级别,会带来大量的磁盘I/O消耗。

error_log参数的标签段位置:

main, http, server, location

log_format

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
# log_format格式是我们定义好一种日志的格式,并起个名字便于引用。

$binary_remote_addr 	 客户端请求的IP地址
$remote_addr:			记录客户端的ip地址;$http_x_forwarded_for也是这个意思
$remote_user:			记录客户端用户的名称;
$time_local:			访问时间及时区;
$request:				请求的URL与HTTP协议;
$status:				记录请求状态
$body_bytes_sent:			记录发送给客户端文件主体内容大小;
$http_referer:				用来记录从那个页面链接访问过来的;
$http_user_agent:			记录客户端浏览器的相关信息
$bytes_sent: 				客户端发送的字节数
$request_length:			客户端请求的长度
$http_host:					客户端请求的地址,即浏览器中你输入的地址(IP或域名)
$upstream_status:			upstream状态
$upstream_addr:				后台upstream的地址,即真正提供服务的主机地址 
$request_time: 				整个请求的总时间 
$upstream_response_time:	请求过程中,upstream响应时间 
$request_body:				POST数据

负载均衡

第一种:加权轮询

按服务器的性能给予不同权重,本例是1:3分配。不加的话,默认权重都是1。

1
2
3
4
upstream up_api_gm_et {
    server          10.10.11.11:11111 weight=1 fail_timeout=20s;
    server          10.10.11.12:11111 weight=3 fail_timeout=20s;
}

第二种:ip_hash轮询

给服务器加不加权重都行。默认权重也都是1。

1
2
3
4
5
upstream up_api_gm_et {
    ip_hash;
    server          10.10.11.11:11111 fail_timeout=30s;
    server          10.10.11.12:11111 fail_timeout=30s;
}

设置代理超时:

proxy_connect_timeout 60s; # 连接超时 默认为60秒

定义用于建立与代理服务器的连接超时。应当注意的是,此超时通常不能超过75秒。

proxy_read_timeout 60s; # 读取超时 默认为60秒

定义了从代理服务器读取响应超时。只是在两个连续的读取操作之间设置超时,而不是为整个响应的传输设置超时时间。如果代理服务器在这段时间内不发送任何东西,连接关闭。

proxy_send_timeout 60s; # 发送超时 默认为60秒

设置发送到代理服务器的请求的超时。只是在两个连续的写操作之间设置超时,而不是为整个请求的传输设置超时时间。如果代理服务器在这段时间内没有收到任何东西,连接关闭。

另外:max_fails和fail_timeout —— 这俩是关联的,如果某台服务器在fail_timeout时间内出现了max_fails次连接失败,那么nginx就会认为那个服务器已经挂掉,从而在 fail_timeout时间内不再去查询它,fail_timeout的默认值是10s,max_fails的默认值是1(这意味着一发生错误就认为服务器挂掉),如果把max_fails设为0则表示把这个检查取消。举个例子:server 192.168.1.11 max_fails=3 fail_timeout=30s; 这表示,如果服务器192.168.1.11在30秒内出现了3次错误,那么就认为这个服务器工作不正常,从而在接下来的30秒内nginx不再去访问这个服务器。

其它参数配置

1
2
3
4
5
6
7
upstream up_api_gm_et {
    ip_hash;
    server          10.10.11.11:11111;
    server          10.10.11.12:11111 weight=2; (默认为1,weight越大负载的权重就越大)
    server          10.10.11.13:11111 down; (单前的server暂时不参与负载)
    server          10.10.11.14:11111 backup; (非backup机器down或者忙的时候,请求backup机器)
}

ip_hash只对IP地址的前三段取hash值,然后选择后台服务器处理。所有同样的网段ip/24过来的请求,会分发到同一个后台服务器。

根据我的实验,总结几个结论如下:

  • ip_hash条件下后端服务器的配置顺序对分发是有意义的,某个客户端请求的IP会被算出一个服务器的索引,这个索引和upstream中服务器的顺序相关。
  • fail_timeout=10s默认值是10s,Nginx通过策略找到后台服务器之后,如果这台服务器彻底失去响应,会自动选择其它后端服务器转发请求。而且在接下来的fail_timeout时间之内,都不会将请求转发给失联的服务器。超过这个时间之后才会再次尝试这台失联服务器。
  • 如果后端服务器只是响应慢,默认timeout是60s,那么这段时间请求就卡死了,超时之前不会自动选取下一台服务器处理。建议把proxy_read_timeout适当调小尽快让下一台服务器处理请求。

这里有一个参考示例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
upstream up_pg_agent_test_backup_api {
    ip_hash;
    server          127.0.0.1:5111 fail_timeout=20;
    server          127.0.0.1:5112 fail_timeout=20;
}
server {
    listen          8019;
    location / {
        #proxy_connect_timeout 30;
        proxy_read_timeout 10;
        #proxy_send_timeout 30;
        proxy_pass  http://up_pg_agent_test_backup_api/;
    }
}

情况一:

如果我们把5112端口阻塞掉,永远不返回任何内容(模拟访问量巨大,一台服务器已经无法准时返回结果了)。再访问8019端口(如果恰好当前IP先被ip_hash匹配到5112端口),大家猜结果会如何?

  1. 第一次请求被阻塞10s之后直接返回5111对应的值。
  2. 之后的每一次都能正常返回5111的对应内容,而且这个正常的过程持续20s
  3. 接下来就开始重复1了。

为什么呢?因为proxy_read_timeout=10意味着10秒以后nginx会自动完成后台负载均衡服务器的切换,都不用等正在阻塞中的5112。

情况二:

如果5112直接离线了,会如何表现呢?

  1. 第一次不用阻塞,直接返回5111对应的值。
  2. 之后的每一次都能正常返回5111的对应内容,而且这个正常的过程持续20s
  3. 接下来就开始重复1了。

为什么呢?因为nginx第一次肯定也会试图把处理请求转发给5112,可是马上就知道5112已经挂了,他会自动把请求转发下一个负载服务器。

情况三:

如果5111和5112都挂掉了,结果很简单,所有的请求都返回502 Bad Gateway

参考文章:

https://www.pianshen.com/article/3042395077/

https://www.cnblogs.com/lc0605/p/10444086.html

限流控制

限流不会提升性能,但使用好限流手段却可保障服务的稳定性、可靠性,使服务更为的健壮。再有限流很难比较好的解决业务逻辑上的问题,为了扛住用户的抢购,还是需要业务逻辑上实现类似排队的算法。

Nginx官方版本限制IP的连接和并发分别有两个模块:

  • ngx_stream_limit_conn_module 用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 “leaky bucket”
  • ngx_http_limit_req_module 用来限制同一时间连接数,即并发限制

什么是漏桶算法?

我们假设系统是一个漏桶,当请求到达时,就是往漏桶里“加水”,而当请求被处理掉,就是水从漏桶的底部漏出。水漏出的速度是固定的,当“加水”太快,桶就会溢出,也就是“拒绝请求”。从而使得桶里的水的体积不可能超出桶的容量。主要目的是控制数据注入到网络的速率,平滑网络上的突发流量。漏桶算法提供了一种机制,通过它,突发流量可以被整形以便为网络提供一个稳定的流量。

请求限制

请求限制的功能来自于 ngx_http_limit_req_module 模块。使用它需要首先在 http 配置段中定义限制的参照标准和状态缓存区大小。

看看limit_req_zone,举个例子:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
upstream up_pg_agent_test_backup_api {
    ip_hash;
    server          127.0.0.1:5111 weight=1 fail_timeout=30;
    server          127.0.0.1:5112 weight=1 fail_timeout=30;
}

# 定义了一个 limit_req 缓冲区(容器)规则,请求频率限制为每秒 1 个请求(nr/s)
limit_req_zone $binary_remote_addr zone=limit_cd:10m rate=1r/s;

server {
    listen  8020;
    access_log      /home/xxx/_logs_/nginx_agent_backup.log nginx_agent_backup;

    location / {
        # nodelay 不延迟处理
        # burst 是配置超额处理,可简单理解为队列机制
        # 上面配置同一个 IP 每秒只能发送一次请求(1r/s),这里配置了缓存3个请求,
        # 就意味着同一秒内只能允许 4 个任务响应成功,其它任务请求则失败(503错误)
        limit_req zone=limit_req burst=3 nodelay;

        proxy_connect_timeout   20;
        proxy_read_timeout      20;
        proxy_send_timeout      20;
        proxy_pass  http://up_pg_agent_test_backup_api/;
        include     _nc_proxy_env.conf;
    }
}

我把5112设计成阻塞,不执行render,你猜会如何。在客户端执行5此请求,你会发现5个请求都原地打转,直到超时,所有请求到另外一路正常返回结果。但也只是前2个请求返回结果,后三个全部显示503 Service Temporarily Unavailable

下面是Nginx错误日志:

image-20210115102510357

并发限制

Nginx 并发限制的功能来自于 ngx_http_limit_conn_module 模块,跟请求配置一样,使用它之前,需要先定义参照标准和状态缓存区。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# 定义了一个 limit_conn 缓冲区(容器)
limit_conn_zone $binary_remote_addr zone=limitconn:10m;
server {
    listen  8021;
    location / {
        # 每个 IP 只允许一个连接
        limit_conn limitconn 1;
        # 限制传输速度(如果有N个并发连接,则是 N * limit_rate)
        limit_rate 1024k;

        proxy_connect_timeout   20;
        proxy_read_timeout      20;
        proxy_send_timeout      20;
        proxy_pass  http://up_pg_agent_test_backup_api/;
    }
}

还有很多参数可以查阅Nginx官方文档:

http://nginx.org/en/docs/http/ngx_http_limit_conn_module.html

http://nginx.org/en/docs/http/ngx_http_limit_req_module.html

(完)