Nginx反向代理&负载均衡
思维导图:
1. 反向代理
1.1 ngx_http_proxy_module
1)proxy_pass URL;
建议proxy_pass后面的路径不带URI,这样会将location中的uri直接传递给后端主机,否则location中的uri会被proxy_pass的uri所覆盖。
server { ... server_name www.hgzerowzh.com; location /uri/ { proxy_pass http://10.0.0.201:80; } ... }
2)proxy_set_header field value;
- proxy_set_header X-Real-IP $remote_addr;
- 将客户端的真实IP地址通过添加一个http首部字段X-Real-IP的方式传递给后端主机
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- 每一级代理都附在前一级代理的后面,每个IP用逗号隔开
其他模块:
- proxy_pass_header field;
- 允许nginx把后端服务器发来的报文中的某些字段直接发送给客户端
- proxy_pass_request_headers on|off;
- 允许nginx把请求报文中的某些首部直接传送给后端服务器
3)proxy_cache_path;
proxy_cache_path /data/nginx/cache levels=1:1:1 keys_zone=pcache:10m max_size=2g;
- path的路径若不存在,则会自动创建
- levels:存储的目录层级
- keys_zone:指定保存哈希表的空间大小
- max_size:磁盘空间的最大使用量
- inactive:缓存的最大非活动时长,默认是10minute
4)proxy_cache zone | off;
- proxy_cache pcache;
- 指明要调用的缓存
- 这里调用的缓存名称就是proxy_cache_path的keys_zone中指定的缓存
5)proxy_cache_key string;
- 指定缓存中用于“键”的内容,以下是默认值,但也需要手动定义出来
proxy_cache_key $scheme$proxy_host$request_uri;
6)proxy_cache_valid [code...] time;
- 定义各类不同内容能缓存多长时间
# 这里定义了缓存路径 proxy_cache_path /var/cache/nginx/proxy_cache levels=1:1:1 keys_zone=pxycache:20m max_size=1g; # 定义在需要调用缓存功能的配置段,例如server{...}; proxy_cache pxycache; # 使用的缓存 proxy_cache_key $request_uri; # 指定缓存中用于“键”的内容 proxy_cache_valid 200 302 301 1h; # 这里表示200,302,301的响应码可以缓存1h proxy_cache_valid any 1m; # 余下其他的缓存1m
7)proxy_cache_use_stale
- 作用:
- 如果代理服务器与后端服务器通信的过程中发生错误(如掉线),确定在哪些情况下用代理服务器上过期的缓存做应答,默认是off的。
- 语法:
- proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off;
proxy_cache_use_stale http_502;
8)proxy_cache_methods GET | HEAD | POST ... ;
- proxy_cache_methods GET | HEAD | POST …;
- 指明对哪一种请求方法调用缓存,默认是GET和HEAD
- 多个请求方法用空格隔开
- proxy_cache_min_uses NUM;
- 缓存内部的缓存项在指定的时间内(10分钟,proxy_cache_path中默认定义了10分钟),至少要访问多少次才定义为活动项,没达到就被清理
- 默认是1次
9)proxy_hide_header field;
- 指明从代理服务器到客户端的响应不传递哪些字段(隐藏某些首部的信息)
- 默认情况下,从代理服务器到客户端的响应,nginx不传递头字段“date”,“server”,“x-pad”,“x-accel-...”
10)proxy_connect_timeout time;
- 定义面向服务器端一次的连接超时时间(代理服务器与后端服务器连接的超时时间),它是指定建立连接的,tcp三次握手的。
- 默认是60s,最长为75s(不能超过75s)
11)proxy_read_timeout time;
- 连接建立后,后端服务器响应的超时时间,默认为60秒(这里的超时时间指的是两次重传之间的时间间隔)
- 表示某次后端数据传递给代理服务器后,如果60秒内代理服务器和后端服务器没有任何数据传输,则关闭连接。目的是避免代理服务器和后端服务器一致保持连接而不断开占用资源。
12)proxy_send_timeout time;
- 向服务端发送请求报文的超时时间
- 即代理服务器向后端服务器发请求的超时时间,如果超过这个时间,就关闭连接
1.2. ngx_http_headers_module
1)add_header name value [always];
- 向由代理服务器响应给客户端的响应报文添加自定义首部,或修改指定首部的值
示例:
add_header X-Via $server_addr;
add_header X-Accel $server_name;
1.3 ngx_http_fastcgi_module
- FPM只是众多支持FastCGI协议中的一种
1)fastcgi_pass address;
- 作用
- 指明fastcgi server的地址
location ~* \.php$ { fastcgi_pass 10.0.0.202:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /data/apps/$fastcgi_script_name; include fastcgi_params; # 这里要导入fastcgi的配置文件 }
2)fastcgi_index name;
- 指定fastcgi默认的主页资源
3)fastcgi_param parameter value[if_not_empty];
- 设置一个参数传递给fastcgi服务器
location ~* \.php$ { root /usr/local/nginx/html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name; include fstcgi_params; }
- 将uri中的status、ping字符传递给后端主机,以查看后端主机的状态信息。
location ~* ^(status|ping)$ { include fastcgi_params; fastcgi_pass 127.0.0.1:9000; fastcgi_param SCRIPT_FILENAME $fastcgi_script_name; }
4)fastcgi_cache_path path;
- 定义fastcgi的缓存,缓存位置为磁盘上的文件系统,由path所指定的路径来定义
- 注意:fastcgi_cache_path选项要定义在http块中
fastcgi_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
5)fastcgi_cache zone | off;
- 指明调用指定的缓存空间来缓存数据
- fastcgi_cache one;
6)fastcgi_cache_key string;
- 定义用作缓存项的key字符串
- 和proxy_cache_key类似
7)fastcgi_cache_methods GET | HEAD | POST ...;
- 为哪些请求方法使用缓存,各个选项之间用空格隔开
- 和proxy_cache_methods类似
8)fastcgi_cache_min_uses number;
- 缓存空间中的缓存项在inactive定义的非活动时间内至少要被访问到此处指定的次数方可被认作活动项
9)fastcgi_cache_valid [code ... ] time;
- 定义不同的响应码各自的缓存时长
http { fastcgi_cache_path /var/cache/nginx/fastcgi_cache levels=1:2:1 keys_zone=fcgi:20m inactive=120s; ... server { ... location ~* \.php$ { fastcgi_pass 192.168.10.11:9000; fastcgi_index index.php; ... fastcgi_cache fcgi; fastcgi_cache_key $request_uri; fastcgi_cache_valid 200 302 10m; fastcgi_cache_valid 301 1h; fastcgi_cache_valid any 1m; ... } } }
10)fastcgi_keep_conn on | off;
- 默认情况下,代理服务器和FastCGI server连接时都是短连接,响应后立即关闭
- 如果开启这个参数,nginx会告诉FastCGI server连接建立后不要立即关闭,一直存在
1.4 配置概览
1)反向代理配置概览
location /uri/ { proxy_pass http://hgzerowzh.com:80; # 将匹配uri的内容代理给指定的uri } location ~|~* /uri/ { proxy_pass http://host; proxy_set_header X-Real-IP $remote_addr; # 设定发往后端主机的请求报文的请求首部的值 # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # proxy_pass_header field; # 允许nginx把后端服务器其发来报文中的某些首部直接发送给客户端 # proxy_pass_request_headers on|off; # 是否允许将客户端的请求报文的某些字段发往后端主机 # 指定proxy_cache的路径、缓存结构、缓存名称及大小,磁盘最大使用量,这个参数需要在http标签内定义 # proxy_cache_path /var/cache/nginx/proxy_cache levels=1:1:1 keys_zone=pxycache:20m max_size=1g; proxy_cache pxycache; # 使用之前指定的那个缓存,pxycache proxy_cache_key $scheme$proxy_host$request_uri; # 缓存的键 proxy_cache_valid 200 302 301 1h; # 对特定响应码内容的缓存时长 proxy_cache_valid any 1m; # 对余下的所有响应吗的缓存时长 proxy_cache_use_stale http_502; # 后端服务器处于什么状态时,能够使用本地过期缓存 proxy_cache_methods GET HEAD POST; # 对哪一种请求方法调用缓存,默认是GET和HEAD # proxy_cache_min_users NUM; # 缓存内部的缓存项的最小命中次数,小于它将会被清理,默认10分钟,命中1次 ################使用上面几个就差不多了#################### # proxy_hide_header field; # 代理服务器到客户端的响应将隐藏某些首部信息 proxy_connect_timeout 60s; # 代理服务器和后端服务器连接的超时时间 proxy_read_timeout 60s; # 建立连接后,后端传输数据给代理服务器, # 如果60s内代理服务器和后端服务器没有任何传输,则关闭此链接 proxy_send_timeout # 代理服务器向后端服务器发请求的超时时间 }
2)fastcgi代理动态请求配置概览
http { # 指定fastcgi代理缓存的位置、结构、缓存空间名称及大小、非活动时长 fastcgi_cache_path /var/cache/nginx/fastcgi_cache levels=1:2:1 keys_zone=fcgi:20m inactive=120s; server { location ~* \.php$ { fastcgi_pass 192.168.10.11:9000; # 指定后端fastcgi服务器的位置 fastcgi_index index.php; # 指定默认主页 fastcgi_param SCRIPT_FILENAME /data/apps/$fastcgi_script_name; include fastcgi_params; # 以下和proxy_cache中的含义一样 fastcgi_cache fcgi; # 调用指定的缓存空间来缓存数据 fastcgi_cache_key $scheme$proxy_host$request_uri; fastcgi_cache_valid 200 302 10m; fastcgi_cache_valid 301 1h; fastcgi_cache_valid any 1m; fastcgi_cache_methods GET HEAD POST; fastcgi_cache_min_users NUM; # 缓存空间中的缓存项在inactive定义的非活动时间内至少要被访问到此处所指定的次数方可被认作活动项 fastcgi_keep_conn on; # 默认情况下,代理服务器和FastCGI server连接时都是短连接,响应后立即关闭 # 如果开启这个参数,nginx会告诉FastCGI server连接建立后不要立即关闭,一直存在 } } }
2. 负载均衡
2.1 ngx_http_upstream_module模块
1)upstream name {...}
- 配置后端负载均衡组,只能在http上下文中定义
upstream httpdsrvs {
server 10.0.0.202:80;
server 10.0.0.203:80;
}
2)server address [parameters];
- 作用:定义后端主机
- address的表示格式
- unix:/path/to/sock_file
- IP[:PORT]
- HOSTNAME[:PORT]
- parameters
- weight=NUM 权重,默认为1
- max_fails=NUM 失败尝试最大次数,默认是1次,如果将其设置为0,则不作健康状态检测
- fail_timeout=TIME 将服务器标记为不可用状态的超时时间,默认是10s
- max_conns=NUM 最大并发连接数
- backup 将服务器标记为“备用”
- down 标记为“不可用”
upstream backend { server backend.example.com weight=5; server 127.0.0.1:8080 max_fails=3 fail_timeout=5s; # 这里表示检查3次,一次5秒钟 server unix:/tmp/backend3; # unix sock的通信方式,跟本机上服务的的套接字通信 server backup1.example.com backup; }
3)least_conn;
- 放置于upstream中
- 最少连接调度算法(lc算法)
- 当server拥有不同的权重时其为wlc
4)ip_hash;
- 源地址hash调度方法
- 等价于hash $remote_addr;
- 来自同一个IP地址的请求始终定位到同一个主机,类似LVS的SH算法
- 如果使用了ip_hash,就不能使用back了,因为ip_hash已经是绑定了,backup就无效了
5)hash key [consistent];
- 取模法:
- 对URL做哈希计算后和后端服务器权重总和取模
- 缺点是若后端服务器权重总和发生变化,原结果全部失效(雪崩效应)
- 一致性哈希算法(加了consistent):
- 解决了取模法中服务器离线导致的雪崩效应
- 哈希环偏斜的解决方法:在每个权重上多次加盐后哈希
6)keepalive connections
- 为每个worker进程保留的空闲的长连接数
- 可以让代理服务器与后端服务器保持连接,在一定时间内对同一后端服务器的连接都用同一个套接字(同一个端口)
- 这里指定的就是代理服务器的每个worker可以与后端服务器组可以维持多少个长连接(省去了三次握手的过程而节省大量时间)
- keepalive 32;
7)配置示例
upstream websrvs { #ip_hash; # 源地址绑定,相当于hash $remote_addr hash $request_uri consistent; server 10.0.0.202:80 weight=5 max_conns=1000 max_fails=3 fail_timeout=3; server 10.0.0.203:80 weight=10 max_fail=2 fail_timeout=3; #server 10.0.0.204:80 backup; keepalive 32; } server { listen 80; server_name www.hgzerowzh.com; location / { root /data/nginx/html; proxy_pass http://websrvs; # 注意这里要加上协议 }
- 为了得知请求的uri被绑定要哪台后端服务器上,引入了取模法和一致性哈希算法,推荐使用一致性哈希算法。
- 哈希环偏斜的问题的解决方案就是在每个权重上多次撒盐后哈希,以在哈希环上占据更多的位置。
2.2 负载均衡的思路
- 后端主机的调度方式:轮询、加权轮询、最小连接数调度
- 根据指定的变量哈希后的结果来查缓存:请求的URI、远程主机的IP地址等
- 健康检查策略:超时时间、检查次数
- 最大负载的设置
- 将某个后端主机定义为“备用”或“下线”状态
2.3 ngx_stream_core_module模块
- 这个模块1.9.0版本才开始引入,且默认不编译,只有在编译时指定 --with-stream参数才会有
- 模拟反代基于tcp或者udp的服务连接,即工作于4层(传输层)的反代或调度器
1)stream {...}
- 定义stream相关的服务器(四层代理)
- Context:main
stream { upstream sshsrvs { hash $remote_addr consistent; server 192.168.22.2:22 weight=5; server 192.168.22.3:22; server example.com:12345 backup; least_conn; } server { listen 10.1.0.6:22022; # 代理监听的本地主机和端口 proxy_pass sshsrvs; # 指定的主机或主机组(upstream模块名) proxy_timeout 3s; # 代理连接的读写连续两次操作之间的超时时长 proxy_connect_timeout 1s; # 设置nginx与被代理服务器尝试建立连接的超时时间,默认为60s } }
2)listen
- 指定监听的端口(默认为tcp协议)
- listen address:port [ssl] [udp] [proxy_protocol] [backlog=number] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
3. 反向代理扩展
3.1 SSL在集群环境中的使用
nginx反向代理工作在七层,而LVS的NAT工作在三层和四层。
工作在7层意味着代理服务器在接收请求和代理转发请求时都要占据套接字,所以在大并发的场景中,套接字数(端口数)将成为瓶颈。
而LVS的NAT工作在三层和四层,不需要考虑这些情况,甚至两段通信过程中tcp协议的序列号都一样,所以可以突破套接字数量的限制。
要特别注意的是SSL工作在传输层和会话层之间,所以在LVS架构场景中,在调度器上做SSL是不成立的,因为调度器是工作在三层和四层上的,无法识别SSL。
所以,若负载均衡场景中要使用SSL,则有以下几种方案:
- LVS的调度器将请求调度到后面的nginx负载均衡器上,在nginx负载均衡器上做https,而负载均衡器与后端主机的通信则使用http协议。
- 这带来的问题是LVS的调度器要对nginx负载均衡器做源地址绑定,因为ssl会话有一定的缓存时长。而做地址绑定就意味着将降低负载效果。
- 但是这样做的好处就在于突破了高并发场景中套接字数量的限制。
- 直接不用LVS,而改用nginx进行调度,在nginx调度器上对https进行卸载,与后端服务器的通信则使用http协议。
- 这样做是比较合适的
- 但是缺陷就在于nginx在高并发场景中将受限于套接字数量的上限。
3.2 反向代理的缓存
在反向代理中,并非所有的请求都要使用去缓存,如POST、PUT、DELETE这类请求就无需也没有必要去缓存。缓存如GET、HEAD这类请求则是非常有必要的。
但是要注意的是,也绝非所有的GET和HEAD请求都是应该缓存的。如个人的用户的session、cookie和用户的账号密码等就不应该缓存,而公共的资源就需要缓存。
另外还需要考虑的是协议版本的兼容性,如http1.0和http1.1就不一定兼容。
关于动态内容的缓存,若在服务端生成以后变化频率小的内容,则可以缓存,则随时都会变化的内容,则不应该缓存。
缓存在内存中体现为URL请求做哈希计算后保存的哈希表,而在磁盘上则体现为以URL哈希后结果的十六进制字符构成的目录结构,此二者相互对应。
当请求来到时,代理服务器将根据请求的URL哈希后的结果去比对保存在内存中的结构,若命中则去到对应的磁盘路径中去加载对应的文件,再封装成响应报文返回给客户端。
所以反向代理的缓存不过是用磁盘IO来替代了一部分网络IO。
还需要注意的是,缓存在内存中的哈希表,需要我们来定义需要作为键的部分(如协议、域名、URI、端口等)。
另外,对于缓存来说,默认是用LRU算法来清理缓存,那么我们就需要对缓存清理的策略进行定义,如多长时间内命中次数小于多少次则对这部分内容进行清理。
在特殊场景下,我们还需要手动对部分缓存进行清理(缓存修剪),这部分功能nginx社区版不支持,需要用varnish来进行扩展。
若后端服务器宕机,我们还需要决策是否把代理服务器缓存的内容返回给客户端,但是这部分缓存是不新鲜的。
作为用户,是可以指定服务器不要用缓存在响应的(如使用shift+F5来强刷,重新加载资源)。
3.3 LNAMP和LNAMMP架构
3.3.1 LNAMP架构模型
通常,Nginx如果想要处理php动态内容,则就需要通过fastcgi协议将动态内容请求转发给后端fastcgi Server(FPM Server)。
但对于fastcgi Server来说,既要接收前端nginx发来的请求,又要对动态内容进行加载运行,性能可能不是很理性。
这时,我们可以不使用fastcgi Server,而改用httpd+php模块加载的方式来处理nginx转发过来的动态内容,性能可能还优于fastcgi Server的方式。
这样就形成了LNAMP的架构模型:Nginx在前端负责对静态内容的响应,而动态内容则反代给后面的httpd搭载php模块来进行处理。
3.3.2 LNAMMP架构模型
一般在任何场景中,MySQL(数据库)都是最慢的一环,那么php在运行动态程序时,可以把从MySQL中加载的数据缓存下来以加速响应。
如果不使用MySQL自己的缓存的话,则可以使用旁挂式的缓存服务器Memcached(内存级的缓存,只支持键值数据)。
现在则一般使用Redis来取代,因为其可以对数据持久存储,且支持更多的数据类型。
我们可以把php从MySQL中检索出来的数据缓存在一个专门的缓存服务器Memcached上,以后若再要查此数据,就可以在这个Memcached上去找,而不用再去查MySQL。
3.4 关于动静分离
前端专门用一台Nginx做反代,静态内容反代到一台Nginx Server_01 上,由Server_01来响应静态内容,而动态内容则反代给Nginx Server_02,由Nginx Server_02 通过fastcgi协议连接fastcgi Server对动态内容进行处理后响应给客户端。这样就实现了动静分离。
要注意的是,Nginx Server_01 和 Nginx Server_02 上都要有对应的资源文件,或者可以直接两者都用同样的包含所有静态和动态资源的内容。
当使用fastcgi Server(FPM)时,关于session保存位置的设置可以到fpm的配置文件中去指定。会话是保存在内存中的,但是每隔一段时间都会将其写入到磁盘上,以防会话丢失。