NGINX Recap

使用配置

讨论的使用场景: - 反向代理 - 负载均衡 - 服务路由 - 静态站点 - 文件服务器 - 前后端跨域访问

资源链接: - NGINX配置教程:NGINXConfig DigitalOcean - NGINX文档:在线文档-nginx

基本使用命令行:

1
2
3
4
5
6
7
8
ginx -s stop       # 快速关闭Nginx,可能不保存相关信息,并迅速终止web服务。
nginx -s quit # 平稳关闭Nginx,保存相关信息,有安排的结束web服务。
nginx -s reload # 因改变了Nginx相关配置,需要重新加载配置而重载。
nginx -s reopen # 重新打开日志文件。
nginx -c filename # 为 Nginx 指定一个配置文件,来代替缺省的。
nginx -t # 测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
nginx -v # 显示 nginx 的版本。
nginx -V # 显示 nginx 的版本,编译器版本和配置参数。

反向代理

nginx.conf 文件如下。值得注意的地方有 sendfile、tcp_nopush、tcp_nodelay 选项,和 location 以及 upstream 的配置。配置文件一般在 nginx/conf/conf.d/ 目录下。

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
#运行用户
#user user_name;

#启动进程,通常设置成和cpu的数量相等
worker_processes 4;

#全局错误日志
error_log ~/test/nginx-1.10.1/logs/error.log;
error_log ~/test/nginx-1.10.1/logs/notice.log notice;
error_log ~/test/nginx-1.10.1/logs/info.log info;

#PID文件,记录当前启动的nginx的进程ID
pid ~/test/nginx-1.10.1/logs/nginx.pid;

#工作模式及连接数上限
events {
worker_connections 1024; # 单个后台worker process进程的最大并发链接数
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
#设定mime类型(邮件支持类型),类型由mime.types文件定义
include ~/test/nginx-1.10.1/conf/mime.types;
default_type application/octet-stream;

#设定日志
log_format main '[$remote_addr] - [$remote_user] [$time_local] "$request"
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log ~/test/nginx-1.10.1/logs/access.log main;
rewrite_log on;

#sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy)来传输文件,对于普通应用,一般设为 on
#如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度.
sendfile on;

#启用 TCP NOPUSH 选项时,数据包将立即发送给客户端,而不会等待 TCP 的缓冲区填充或达到最大传输单元(MTU)的大小。这有助于降低网络延迟,提高响应速度。
#当需要减少网络带宽使用或减轻服务器负载时,可设为 off
#tcp_nopush on;

#连接超时时间
keepalive_timeout 120;

#禁用 Nagle 算法。Nagle 算法的目的是通过在发送数据之前进行数据缓冲,以提高网络利用率。它将小的数据块合并成更大的数据包,减少发送的数据包数量,从而减少网络上的传输开销。
#启用 tcp_nodelay 可以减少数据传输的延迟,但可能会增加网络带宽的使用。
tcp_nodelay on;

#gzip压缩开关
#gzip on;

keepalive_timeout 60; # 客户端连接保持会话超时时间
client_header_buffer_size 4k; # 客户端请求头部的缓冲区大小,一般为系统分页大小
open_file_cache max=102400 inactive=20s; # 打开文件缓存数量,inactive 删除不活跃文件
open_file_cache_valid 30s; # 多久检查一次有效缓存
open_file_cache_min_uses 1; # inactive 最少使用次数
client_header_timeout 15; # 设置请求头的超时时间
client_body_timeout 15; # 设置请求体的超时时间
reset_timedout_connection on; # 关闭不响应的客户端连接
send_timeout 15; # 客户端响应超时时间
server_tokens off; # 关闭在错误页面中的nginx版本数字
client_max_body_size 10m; # 上传文件大小限制

#=======================================
#设定实际的服务器列表
upstream actual_server_1 {
server 127.0.0.1:8089;
}
#=======================================

#HTTP/HTTPS服务器
server {
#监听80端口,用于HTTP协议
listen 80;

#==========================================
#用于 HTTPS 部分
#443 用于 HTTPS
#listen 443 ssl;

#ssl证书用于 HTTPS
#ssl证书文件位置(常见证书文件格式为:crt/pem)
#ssl_certificate cert.pem;
#ssl证书key位置
#ssl_certificate_key cert.key;

#ssl配置参数(选择性配置)
#ssl_session_cache shared:SSL:1m;
#ssl_session_timeout 5m;
#数字签名,此处使用MD5
#ssl_ciphers HIGH:!aNULL:!MD5;
#ssl_prefer_server_ciphers on;

#location / {
# root /root;
# index index.html index.htm;
#}
#==========================================

#定义使用www.xx.com访问
server_name www.helloworld.com;

#首页
index index.html

#指向webapp的目录
root ~/code/webapp;

#编码格式
charset utf-8;

#代理配置参数
proxy_connect_timeout 180;
proxy_send_timeout 180;
proxy_read_timeout 180;
proxy_set_header Host $host;
proxy_set_header X-Forwarder-For $remote_addr;

#反向代理的路径(和upstream绑定),location 后面设置映射的路径
location / {
proxy_pass http://actual_server_1;
}

#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
root ~/code/webapp/views;
#过期30天,静态文件不怎么更新,过期可以设大一点,如果频繁更新,则可以设置得小一点。
expires 30d;
}

#设定查看Nginx状态的地址
location /NginxStatus {
stub_status on;
access_log on;
auth_basic "NginxStatus";
auth_basic_user_file conf/htpasswd;
}

#禁止访问 .htxxx 文件
location ~ /\.ht {
deny all;
}

#错误处理页面(可选择性配置)
error_page 404 /404.html;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
}

负载均衡

nginx.conf 文件如下。

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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
http {
#设定mime类型,类型由mime.type文件定义
include /etc/nginx/mime.types;
default_type application/octet-stream;

#设定日志格式
access_log /var/log/nginx/access.log;

#设定负载均衡的服务器列表 以下多选一进行配置
# -- 加权轮询方式
upstream load_balance_server {
#weigth参数表示权值,权值越高被分配到的几率越大
server 192.168.1.11:80 weight=5;
server 192.168.1.12:80 weight=1;
server 192.168.1.13:80 weight=6;
}

# -- 轮询
upstream load_balance_server {
server 192.168.1.11:80 ;
server 192.168.1.12:80 ;
server 192.168.1.13:80 ;
}

# -- 最少连接数
upstream load_balance_server {
least_conn;

server 192.168.1.11:80 ;
server 192.168.1.12:80 ;
server 192.168.1.13:80 ;
}

# -- 加权最少连接数
upstream load_balance_server {
least_conn;

server 192.168.1.11:80 weight=5;
server 192.168.1.12:80 ;
server 192.168.1.13:80 ;
}

# -- IP hash
upstream load_balance_server {
ip_hash;

server 192.168.1.11:80 ;
server 192.168.1.12:80 ;
server 192.168.1.13:80 ;
}

# -- uri 普通hash
upstream load_balance_server {
hash $request_uri;

server 192.168.1.11:80 ;
server 192.168.1.12:80 ;
server 192.168.1.13:80 ;
}

#HTTP服务器
server {
listen 80;

server_name www.helloworld.com;

#对所有请求进行负载均衡请求
location / {
root /root; #定义服务器的默认网站根目录位置
index index.html index.htm; #定义首页索引文件的名称
proxy_pass http://load_balance_server ;#请求转向load_balance_server 定义的服务器列表

#以下是一些反向代理的配置(可选择性配置)
#proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
#后端的Web服务器可以通过X-Forwarded-For获取用户真实IP
proxy_set_header X-Forwarded-For $remote_addr;
proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
proxy_read_timeout 90; #后端服务器响应时间(代理接收超时)
proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
proxy_buffers 4 32k; #proxy_buffers缓冲区,假设网页平均在32k以下的话
proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2)
proxy_temp_file_write_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传输

client_max_body_size 10m; #允许客户端请求的最大单文件字节数
client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数
}
}
}

多端口应用配置

nginx.conf 文件如下,通过 URL 将请求路由到正确的服务所在端口上。

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
http {
#一些基本配置
#...

upstream product_server {
server www.helloworld.com:8081;
}

upstream admin_server {
server www.helloworld.com:8082;
}

upstream finance_server {
server www.helloworld.com:8083;
}

server {
#...

#默认指向product的server
location / {
proxy_pass http://product_server;
}

location /product/ {
proxy_pass http://product_server;
}

location /admin/ {
proxy_pass http://admin_server;
}

location /finance/ {
proxy_pass http://finance_server;
}
}
}

静态站点

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
worker_processes  4;

events {
worker_connections 1024;
}

http {
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

gzip on;
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript image/jpeg image/gif image/png;
gzip_vary on;

server {
listen 80;
server_name www.static.com;

location / {
root /app/dist; # 静态资源所在目录
index index.html; # 请求返回 index.html
}
}
}

简易文件服务器

1
2
3
4
5
6
7
8
9
10
11
autoindex on;            # 显示目录
autoindex_exact_size on; # 显示文件大小
autoindex_localtime on; # 显示文件时间

server {
charset utf-8,gbk; # 编码支持
listen 9050 default_server;
listen [::]:9050 default_server;
server_name _;
root /share/fs; # 共享文件服务的根路径
}

前后端跨域方案

首先新建配置文件 solution.conf

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
# allow origin list
set $ACAO '*';

# set single origin
if ($http_origin ~* (www.helloworld.com)$) {
set $ACAO $http_origin;
}

if ($cors = "trueget") {
add_header 'Access-Control-Allow-Origin' "$http_origin";
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
}

if ($request_method = 'OPTIONS') {
set $cors "${cors}options";
}

if ($request_method = 'GET') {
set $cors "${cors}get";
}

if ($request_method = 'POST') {
set $cors "${cors}post";
}
再配置服务器配置文件:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# ...

upstream front_server {
server www.helloworld.com:9000;
}

upstream api_server {
server www.helloworld.com:8080;
}

server {
listen 80;
server_name www.helloworld.com;

location ~ ^/api/ {
include solution.conf.conf; # include 新建的配置文件
proxy_pass http://api_server;
rewrite "^/api/(.*)$" /$1 break;
}

location ~ ^/ {
proxy_pass http://front_server;
}
}

NGINX设计与优化小结

  1. 关闭 Nagle 算法:关闭为小数据包设计的 Nagle 算法,一般 HTTP 协议包普遍比较大

  2. 开启 cork 算法:尽量让数据包达到一个 MTU 大小再发送,提高网络有效负载,发送大量数据时有效。

  3. 发送文件 sendfile : 直接将内核态读取的数据包发送到内核协议栈数据通路。

  4. 开启 TCP_DEFER_ACCEPT 选项:让套接字在有数据可读时,才生成建立连接的套接字,提高效率。

  5. 使用 accept4 : 建立非阻塞连接,减少一次系统调用。

  6. 使用 TCP_QUICKACK: 将 ACK 与 数据包 一起发送,每次 recv 后需要重新设置。

  7. 使用用户态锁:nginx没有使用系统的信号量锁,而是使用了“共享内存+原子操作__sync_bool_compare_and_swap”实现的用户态锁。这样每次加锁解锁操作都不需要进入内核态,极大的提高了锁的性能。

  8. 避免惊群:如果多个 worker 同时监听一个套接字,nginx 使用锁保证只有一个 woker 在监听和accept。加入一个队列后,先释放锁,再处理连接请求。

  9. 减少 keepalive 时间:操作系统的 keepalive 原理是发送一个长度为 0 的tcp数据包,等待对端的 ACK 相应。如果有响应则认为连接时 alive 的。系统默认时间太长。但是一般如果时长连接设计,会在应用层设计心跳。

  10. 自建内存池:比如一个 HTTP 连接对应一个内存池,连接结束整个释放内存池。

  11. master 时间管理:master 每 100ms 将多种时间格式化字符串写入共享内存,worker 不需要单独格式化,直接读取共享内存。

  12. 提高CPU缓存利用率:setpriority 提高进程优先级。sched_setaffinity 绑定CPU提高CPU缓存的命中率。

  13. 内存管理优化:使用 tcmalloc 替代 glibc 中的 malloc/free

  14. 内核参数优化: > fs.file-max = 999999 # 进程可以同时打开的最大句柄数量 > > net.ipv4.tcp_max_tw_buckets = 6000 # 系统允许TIME_WAIT套接字数量的最大值不应太大 > net.ipv4.ip_local_port_range = 1024 65000 # 允许系统打开的端口范围 > net.ipv4.tcp_tw_recycle = 1 # time wait快速回收 > net.ipv4.tcp_tw_reuse = 1 # 允许将TIME WAIT sockets重新用于新的TCP连接 > net.ipv4.tcp_keepalive_time = 30 # TCP发送keepalive消息的频率 > net.ipv4.tcp_syncookies = 1 # 开启SYN Cookies,当SYN等待队列溢出时,cookies来处理 > net.ipv4.tcp_max_syn_backlog = 262144 # TCP三次握手建立阶段接受SYN请求队列的最大长度 > net.core.netdev_max_backlog = 262144 # 允许送到数据缓存队列的数据包的最大数目 > > net.ipv4.tcp_rmem = 10240 87380 12582912 # TCP接受缓存窗口的最小值、默认值、最大值 > net.ipv4.tcp_wmem = 10240 87380 12582912 # TCP发送缓存窗口的最小值、默认值、最大值 > > net.core.rmem_default = 6291456 # 内核套接字接受缓存区默认的大小 > net.core.wmem_default = 6291456 # 内核套接字发送缓存区默认的大小 > > net.core.rmem_max = 12582912 # 内核套接字接受缓存区的最大大小 > net.core.wmem_max = 12582912 # 内核套接字发送缓存区的最大大小 > > net.core.somaxconn = 40960 # 系统中每一个端口最大的监听队列的长度,对防止拒绝服务 DoS 攻击也会有所帮助 > > ### sysctl -p 应用配置

  15. 事件处理模型:multi_accept on 时,多个 worker 会按串行方式来处理连接;off 时,worker 使用并行模式,一个连接唤醒所有 worker,这种模式使用与连接数较少的低负载场景。

  16. 支持热加载:nginx -s reload 时,master 进程校验配置并打开新的监听端口,然后启动新的 worker 子进程,接着向旧的 worker 发送停止信号。旧 worker 处理完请求,或者在 worker_shert_dump 时间之后会关闭进程。


本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!