nginx学习笔记

概念及认识

Nginx的优点

  • 高并发、高性能兼具
  • 可扩展性好
  • 高可靠性
    运行数年无需重启
  • 热部署
    可以在不停止服务的情况下升级nginx
  • BSD许可证

概念

进程模型

多进程模型(默认): 一个master进程管理多个worker进程,且通常设定worker进程数量与CPU核数相等(进程间切换代价最小)

事件模型

select/poll/epoll

Nginx如何处理请求

Nginx如何处理请求
    结合多进程机制和异步机制
    Nginx启动后产生一个主进程,主进程执行一系列工作后产生多个工作进程。
    主进程主要进行nginx配置文件解析、数据结构初始化、模块配置注册、网络监听生成、工作进程生成和管理等工作。
    工作进程用于接收和处理客户端请求,是nginx服务器提供服务的主体。
    工作进程使用异步非阻塞方式(单线程),可处理多个客户端请求

Web请求处理方式
    多进程方式
        服务器每接受到一个客户端时,由服务器主进程生成子进程和客户端建立连接进行交互,直到连接断开,该子进程结束。
        优点: 设计和实现相对简单,各子进程相互独立,不受干扰
        缺点: 开销大
    多线程方式
        服务器每当接收到一个客户端时,由服务器主进程派生一个线程和客户端进行交互
        优点: 开销小
        缺点: 多个线程间内存共享,彼此相互影响,开发过程中不可避免要由开发者自己对内存进行管理,增加了出错的风险。
    异步方式
        同步阻塞
        同步非阻塞
        异步阻塞
        异步非阻塞

系统命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
------------------------启动与关闭-------------------------------
# 启动服务
nginx
# 重启服务
nginx -s reload
* -s reload 表示发送(signal)信号reload
# 快速关闭服务
nginx -s stop
------------------------配置检测----------------------------------
# 测试配置文件是否正确
nginx -t
nginx -t -c /usr/local/nginx/conf/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
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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
# 配置允许启动nginx的用户及用户组 nobody表示所有用户 user user [group]
# user nobody;
# 配置工作进程数 通常设置等同于cpu核心数(auto会自动做到这一点)
worker_processes auto;
#配置每个worker进程可以打开的最大文件数,默认值为操作系统的限制。设置后可以处理比"ulimit -a "更多的文件,把这个值设置高点就不会有"too many open files"问题
worker_rlimit_nofile 65535;
#配置错误文件存放路径 包含debug|info|notice|warn|error|crit|alert|emerg错误等级 需要写权限
error_log /Users/sunxiangke/project/logs/nginx/error.log error;
#配置nginx进程pid存放路径 nginx守护进程方式运行,需要以文件形式存储当前程序的主进程号
# pid /usr/local/openresty/run/nginx.pid;
events {
#配置事件驱动模型 select|poll|kqueue|epoll mac下使用kqueue linux下使用epoll
use epoll;
#一个worker进程在同一时间接受尽可能多的新连接
multi_accept on;
#配置每个worker进程同时开启的最大连接数
#注意该连接数也包含与代理服务器等的连接数
#理论上每台nginx 服务器的最大连接数为worker_processes*worker_connections
worker_connections 65535;
}
http {
#关闭错误页面中nginx版本号,这对安全有一定好处
server_tokens off;
#配置网络媒体资源类型文件
include mime.types;
default_type application/octet-stream;
#定义日志格式main
#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 logs/access.log main;
#配置允许sendfile方式传输文件,降低copy次数,提升服务器本地读写性能
sendfile on;
#在一个数据包中发送所有头文件,而不是一个接一个的发送(sendfile开启后有效 这是TCP_CORK在BSD下的实现)
tcp_nopush on;
#及时发送数据不要做缓存处理(关闭Tcp Nagle算法)
tcp_nodelay on;
#配置连接超时时间 与用户建立会话连接后,nginx可以保持这些连接一段时间
keepalive_timeout 35;
client_header_buffer_size 4k; #客户端请求行的缓冲大小,如果超过这个大小,则会匹配large_client_header_buffers的策略
large_client_header_buffers 8 8k; #设置读取大的客户端请求头的缓冲区大小及数量
server_names_hash_bucket_size 128;
client_max_body_size 8m; #客户端body大小,超过报413错误
client_header_timeout 30s; #传输客户端请求头超时时间
client_body_timeout 30s; #传输客户端请求体超时时间
#fastcgi优化
fastcgi_connect_timeout 30s; #fastcgi连接超时时间
fastcgi_send_timeout 30s; #fastcgi发送超时时间
fastcgi_read_timeout 30s; #fastcgi读取超时时间
fastcgi_buffer_size 16k; #fastcgi缓冲区大小,该缓冲区用于读取fastcgi服务器响应信息的第一部分信息
fastcgi_buffers 64 16k; #设置缓冲区的大小和数量,缓冲区被用于读取fastcgi服务器的响应信息
fastcgi_busy_buffers_size 128k;
fastcgi_max_temp_file_size 0; #当fastcgi响应内容大小超过fastcgi_buffer_size和fastcgi_buffers设定的大小,则会被存放为磁盘临时文件。0表示禁用
#gzip_min_length设置对数据启用压缩的最少字节数。如果一个请求小于1000字节,我们最好不要压缩它,因为压缩这些数据会降低处理此请求的速度。这个大小是通过"Content-Length"头来确定的
gzip on;
gzip_min_length 1k;
# cache informations about file descriptors, frequently accessed files
# can boost performance
#open_file_cache max为缓存中元素的最大个数,当超过该个数时使用LRU策略移除元素 inactive为元素的超时时间
open_file_cache max=65535 inactive=30s;
open_file_cache_valid 30s;
open_file_cache_min_uses 3;
server {
listen 80;
#listen *:80 default_server;
server_name www.myserver.com *.myserver.com;
root /path/to/workdir/;
#设定主页 顺序查找$document_root/app/cache/index.php $document_root/appcache/index.html...
#try_files /app/cache/ $uri @fallback;
#index index.php index.html index.htm;
#location [=|~|~*|^~] uri {...}
# = 普通字符精确匹配
# ^~ 普通字符匹配,停止匹配其他location
# ~ 正则模糊匹配
# ~* 正则无视大小写模糊匹配
location / {
#匹配所有请求,因为所有请求都是以/开头的
#但是更长字符匹配或正则匹配会优先匹配
}
location = / {
#只匹配 /
}
location ^~ /images/ {
#匹配任何以 /images/开始的请求,并停止匹配其他location
}
location ~* \.(gif|jpg|jpeg)$ {
#匹配以gif|jpg|jpeg结尾的请求
#但所有/images/目录的请求由^~ /images/下配置处理
}
#错误页面
#error_page error_code url|uri;
#error_page 404 http://baidu.com;
#error_page 404 /404.html;
#location = /404.html {
# root path/to/workdir;
#}
#基于IP的访问控制
#location / {
# allow 192.168.1.1;
# deny all;
#}
#静态文件缓存 (浏览器) d h m
location ~ \.(jpg|jpeg|png|gif)$ {
#设定过期时间1天
expires 1d;
break;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
include servers/*;
include upstream.conf;
}

URL重写

if

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
if($condition){...}
通location,if可以使用 =|~|~*|!~|!~* 操作符
if($request_method = POST){
return 405;
}
if($http_user_agent ~ MSIE){
...
}
-f|!-f 判断文件是否存在
-d|!-d 判断目录是否存在
-e|!-e 判断目录或文件是否存在
-x|!-x 判断文件是否可执行

break/return

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
break
中断当前作用域中的其他nginx配置
return
完成对请求的处理
return [text]
return code URL;
return URL;
例:
if($slow){
set $id $1;
break;
limit_rate 10k; #处在break指令之后,配置无效
}
return 302 https://www.baidu.com;
return https://www.baidu.com/; #302跳转简写
return 200 "U got me."; #后者将作为body返回

rewrite

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
语法: rewrite regex replacement [flag]
regex只对URI进行匹配 http://www.baidu.com/source?a=1&b=2 其中的URI是/source
flag
last 停止在当前location中处理接受到的uri,并将重写后的uri作为新的uri重新在server块中执行,为重写后的uri提供了转入其他location的机会
break 重写uri为一个新的uri,在本块中继续处理,不会讲新的uri转向其他location块
redirect 临时重定向,状态码为302
permanent 永久重定向,状态码为301
相关配置
rewrite_log on #配置开启/关闭URL重写日志的输出功能 默认为off,记录到error_log指令配置的文件中
set variable value
:
1.域名跳转
server_name jump.myweb.name;
rewrite ^/ http://www.myweb.info/;
2.多域名跳转
server_name jump.myweb.name jump.myweb.info
if($host ~ myweb\.info){
rewrite ^(.*) jump.myweb.name$1 permanent;
}
3.独立域名
server{
listen 80;
server_name bbs.myweb.name;
rewrite ^(.*) http://www.myweb.name/bbs$1 last;
break;
}
server {
liste 81;
server_name home.myweb.name;
rewrite ^(.*) http://www.myweb.name/home$1 last;
break;
}
4.
location / {
rewrite ^(/myweb/.*)/media/(.*)$ $1/mp3/$2.mp3 last;
}

upstream

upstream

通过upstream实现负载均衡和冗余配置
语法: upstream name {…}
调度方式

  • 轮询(默认) 每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除
  • weight 指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况
  • ip_hash(不可开启weight) 每个请求按访问ip的hash结果分配,每个访客固定访问一个后端服务器
  • fair (需开启upstream_fair模块) 按响应时间分配请求,响应时间短的优先分配
  • url_hash(需开启hash模块) 每个url定向到同一台服务器

设备状态

  • down表示当前server暂时不参与负载
  • weight默认为1,weight越大,负载的权重就越大
  • max_fails:一定时间内允许请求失败的次数,默认为1。当超过该次数后,返回proxy_next_upstream模块定义的错误
  • fail_timeout: 失败/超时时间,默认10秒。2个作用: 1.在该时间内max_fails次失败后,服务器不可用 2.不可用的时间
  • backup: 备机,nginx不会给它转发任何请求。只有当其他节点全部无法连接的时候,nginx才会启用这个节点。 一旦有可用的节点恢复服务,该节点则不再使用,又进入后备状态。
1
server 192.168.100.104:80 weight=4 max_fails=2 fail_timeout=30s; //30秒内失败超过2次,则服务停止30

server

用于设置组内的服务器
语法: server address [parameters];

1
2
3
4
5
6
# 用法:
upstream backend {
server backend1.example.com weight =5; # 域名
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s; # ip
server unix:/tmp/backend3; # unix domain socket
}

例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[反向代理服务器]
location / {
root html;
index index.php index.html index.htm;
proxy_pass http://backend;
}
upstream backend {
ip_hash; #该指令用于保持会话,将某客户端多次请求指向同一台服务器;ip_hash不能与server指令的weight变量一起使用;ip_hash是根据客户端的ip进行分配服务器,此时nginx应该是最前端的服务器,这样才能获取到客户端的ip
server myback1.proxy.com #此时由反向代理服务器传过来的请求http_host为backend(upstream名) 主机不能接uri
server myback2.proxy.com #在myback1.proxy.com服务器上server中不指定server_name或者指定为backend
}
[后端服务器myback1.proxy.com]
...
server {
server_name backend; #对应下游反向代理服务器设定的upstream名
....
}

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
$args
$content_type
$content_length
$document_root
$document_uri
$host
$http_user_agent
$http_cookie
$limit_rate
$remote_addr
$remote_port
$remote_user
$request_body_file
$request_body_file
$request_method
$request_filename
$request_uri
$query_string
$scheme
$server_protocol
$server_addr
$server_name
$server_port
$uri

docker lbdemo

demo

场景

反向代理

理解代理与反向代理

代理
此处输入图片的描述

反向代理
此处输入图片的描述

配置

1
2
3
4
5
[nginx.conf]
location / {
proxy_pass http://192.168.100.100; #代理后端服务器
proxy_set_header X-Real-IP $remote_addr; #后端服务器通过X-Real-IP头获取客户端地址
}

负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
[nginx.conf]
upstream backend { #设定后端服务器组backend
ip_hash; #设定负载均衡算法为ip_hash,解决session共享问题
server http://backend1.com;
server http://backend2.com;
}
server {
server_name backend.com;
location / {
proxy_pass http://backend; #将backend.com的请求反向代理至backend服务器组
proxy_set_header X-Real-IP $remote_addr;
}
}
[backend1.com 上的nginx.conf]
server { #后端服务器组中的服务器设定处理来自backend1.com的请求
server_name backend;
location .....
}
[backend2.com 设定同backend1.com]
...

高性能API

openresty

性能调优

开启reuse port

为什么reuseport可以提升性能

FAQ

1.为什么MAC通过brew安装的nginx不能自动启动,必须用sudo brew restart nginx才行

由于系统限制非root用户不能启动1024以下端口

好用的日志格式

1
log_format main '$remote_addr - $remote_user [$time_local] "$request" $http_referer $status $body_bytes_sent $request_time';

为什么apache性能不如nginx

apache架构存在局限性,apache使用操作系统进程间切换的特性,一个进程服务一个连接,这带来两个问题,一是无法启动过多的进程,二是大量进程间切换成本很高

参考资料

Nginx快速入门