Nginx 代理与缓存
Nginx可用于反向代理,客户端向代理服务器发出请求,代理服务器封装重新请求报文并向后端服务器发出请求,再由代理服务器响应客户端,并且在一定条件下能够缓存URL,以减小Upstream Server的压力。同时,反向代理服务器也实现了隐藏后端服务器的效果。
Nginx作为代理服务器时工作在应用层与传输层,能够对http请求报文进行分类、修改、转发、以及调度Upstream Server或服务器组,以实现负载均衡之目的。Nginx通过理解报文Header中的Method
,URL
,Version
,Host
,Connection
等信息以实现报文的分类,并封装成一个新的请求报文,如在应用层修改请求文本Connection为Keepalive
,如在传输层修改目标端口为8080
等。新的报文由代理服务器向后端服务器发出请求。为了解决后端服务器记录的是真实的客户端IP地址,Nginx会增加一个X-Forward-For
项。
Nginx作为代理服务器具有缓存功能,客户端首次请求时那些被允许缓存的URL将以key/value的hash值存放在层级目录结构中,客户端再次请求同一URL时,如果请求报文的Header中Method是GET或HEAD时,将促使代理服务器查询缓存,并且直接响应给客户端,极大地释放后端服务器的压力。当缓存空间满时或缓存过期时将使用LRU算法进行清理,为了避免清理的缓存依然是有效的,响应报文中的status为304,代理服务器将为其续期。
状态码:304,not modify
如果客户端发送的是一个条件验证(Conditional Validation)请求,则web服务器可能会返回HTTP/304响应,这就表明了客户端中所请求资源的缓存仍然是有效的,也就是说该资源从上次缓存到现在并没有被修改过。条件请求可以在确保客户端的资源是最新的同时避免因每次都请求完整资源给服务器带来的性能问题。
【问题一】:URL是否允许被缓存,缓存多长时间,以及压缩资源如何缓存?
由后端服务器告知代理服务器,如果包用户含敏感信息则不允许缓存;响应报文的Header中包含了缓存控制信息,包括缓存是否被允许、缓存有效期等,Vary中包含了压缩信息。
【问题二】:缓存命中率不高的问题?
缓存不被命中时将导致代理服务器不仅要查询缓存,还要再次向后端服务器请求,造成额外的开销。
【问题三】:缓存过期时是否清理?
代理服务器根据缓存有效期清理缓存时需要考虑后端服务器相应的资源是否发生改变,如果未发生改变则不必要清理缓存。通常Nginx清理缓存时会询问后端服务器是否可以清理缓存,如果响应报文中的status为304则表示缓存依然有效,此时不必清理该URL的缓存。
【问题四】:后端服务器如何记录客户端IP?
代理服务器作为后端服务器的客户端,导致后端服务器无法记录真实客户端的IP,因此Nginx通过增加一个自己的Header,并增加客户端IP地址到X-Forward-For中,因此后端服务器能够记录真实客户端IP。多级代理服务器发现有X-Forward-For首部则不修改一级代理封装的报文,直接转发给后端服务器。
【问题五】:代理服务器如何把同一URL的请求始终分发至同一台服务器?
方法一是对URL做hash再取模(服务器数量)得出非常固定的服务器调度方式,但是某一服务器故障时会取模hash算法失效,无法实现URL与主机的对应。
方法二是对服务器ip地址和URL一致性hash,并取模于32,相当于每台服务器分布在一个(2^32)哈希环上,按照转圈的方法把URL的hash值定位到某一服务器上。如果圈上就近的服务器故障,则向下一台服务器请求。
【问题六】:一致性哈希算法导致部分服务器负载不均衡的问题
由于hash环有2^32位取值,如果服务器之圈定的范围较大,就会造成该服务器偏斜,为了实现均衡,可对每台服务器做多次不同salt
的hash计算,以虚拟服务器的方式分布在hash环上。
【问题七】:CDN之间代理的问题
为了提高各地区的响应速度,通常会购买多个CDN服务。CDN则向LVS集群发出请求,LVS负载均衡Nginx代理服务器集群,代理服务器在应用层封装新的请求报文至Varnish缓存服务器集群,如果缓存服务器未命中URL资源,则最终向后端Web服务器集群请求相应的资源。这是根据客户端请求的资源类型、客户端的位置、客户端使用的网络提供商等信息构建的网络拓扑,目的就是增强Web服务的性能,提高用户的访问速度。
面对复杂的服务器集群结构,通常使用弹性计算的方式实现动态扩展各分支,也就是所谓的云服务或虚拟化方式。
【问题八】:如何上线新应用
按照灰度模型跟新迭代应用:
不能影响用户访问当前可用的服务,新应用必须保证无重大bug,上线新应用时要保留旧版本应用,以防不测;
用户量访问少的时间,一部分一部分地下线服务器,发布完成再上线,直到之后一台服务器上线完成;
如果新应用出现问题,应能够快速回滚至旧应用,通过软链接方式快速实现;
缓存控制(ngx_http_proxy_module)
【1】proxy_pass URL;
位于location, if in location, limit_except中
注意:
-
proxy_pass后的路径不带uri时会将location的uri传递给后端主机;
-
proxy_pass后的路径是一个uri时会将location的uri替换为proxy_pass的uri;
-
如果location定义其uri时使用正则表达式的模式,则proxy_pass之后必须不能使用uri;
location /uri/ {
proxy_pass http://host[:port]; #http://host/uri
proxy_pass http://host/new_rui/ #http://host/new_uri/
}
location ~|~* PATTERN {
proxy_pass http://HOST;
}
配置nginx代理的URI资源类型
直接代理至后端服务器可在 / 这个location中proxy;但一般分类不同的资源代理至不同的服务器,如php代理至一台单独的服务器。
vi default.conf
#前端服务器处理默认url,不代理 /
location / {
root /usr/share/nginx/html; #注释与否都想
#proxy_pass http://192.168.22.2/;
}
#代理uri至后端主机
location /admin/ {
proxy_pass http://192.168.22.2/; #替换uri
}
#资源的动静分离
location ~* \.(jpg|jpeg|gif|png)$ {
proxy_pass http://192.168.22.2; #不允许加 / 否则syntax errror
}
location ~* \.(php|phps)$ {
proxy_pass http://192.168.22.3; #不允许加 / 否则syntax errror
}
【2】proxy_set_header field value;设定发往后端主机的请求报文首部的值
默认代理服务为请求者,后端主机记录的访问者全是代理服务器。
Nginx增加X-Forwarded-For
记录客户端IP,二层代理能够获取到$proxy_add_x_forwarded_for
的值,因此不再添加X-Real-IP
值
增加X-Forwarded-For值
server {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
#后端httpd
vi httpd.conf
LogFormat 中用 %{X-Real_IP} 替换 %h
【3】proxy_cache zone | off;调用path定义在磁盘中的cache zone,开启或关闭proxy的缓存,在http, server, location定义
【4】proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
key的值为URL的hash值,value为URL资源内容的hash值,两者之间通过对应关系快速完成URL的查找与组合。
【5】proxy_cache_key string;缓存中用作key的内容,默认为完整的请求URL,但是http与https不能共享缓存。默认值为proxy_cache_key $scheme$proxy_host$request_uri;
可只用后面一截使之更加适用。
【6】proxy_cache_valid [code …] time;定义对特定响应码的响应内容的缓存时长;
启用proxy缓存URL资源
vi nginx.conf
http{
proxy_cache_path /var/cache/nginx/proxy_cache levels=1:2:1 keys_zone=pxy_zone:20m max_size=1g;
}
vi default.conf
location ~* \.(jpg|jpeg|gif|png)$ {
proxy_pass http://192.168.22.2; #不允许加 / 否则syntax errror
proxy_cache pxy_zone;
proxy_cache_key $request_uri; #不对scheme分类,更通用
proxy_cache_valid 200 301 302 30m;
proxy_cache_valid any 1m;
}
【7】proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off …;
允许代理服务器用缓存短暂直接响应客户端,当代理服务器与后端服务器的通信出现以上的错误响应码时。
【8】proxy_connect_timeout time;与后端服务器通信的超时时长,默认为60s,最大不超过75s;同proxy_read_timeout;proxy_send_timeout
Defines a timeout for establishing a connection with a proxied server. It should be noted that this timeout cannot usually exceed 75 seconds.避免响应为502 bad gateway
:
【9】proxy_cache_methods GET | HEAD | POST …;默认GET、HEAD方法的请求才查缓存。
【10】proxy_buffers number size;内存缓冲优化性能,同proxy_buffering;proxy_buffer
-
proxy_buffers 8 4k|8k;
【11】proxy_hide_header field;指明对客户端隐藏的herder
封装首部(ngx_http_headers_module)
The ngx_http_headers_module module allows adding the “Expires” and “Cache-Control” header fields, and arbitrary fields, to a response header.
由代理服务器向客户端响应的header中添加自定义首部,或修改首部的值
【1】add_header name value [always];在header中添加自定义首部响应给客户端。
如添加这是由代理服务器响应的信息。
-
add_header X-Via $server_addr;
-
add_header X-Accel $server_name;
响应报文首部添加代理信息
default.conf
server {
add_herder X-Via $server_addr;
add_herder X-Accel $server_name;
}
#客户端浏览器显示的Header中
X-Via:10.1.0.6
X-Accel
【2】expires [modified] time;expires epoch | max | off;定义Expire或Cache-Control首部的值,以实现修改原始服务器定义的缓存时长的目的;
如带Cookie的图片不能被缓存,只有修改为public的才能缓存,通过修改过private为public即可被缓存。
代理调度模块(ngx_http_upstream_module)
The ngx_http_upstream_module module is used to define groups of servers that can be referenced by the proxy_pass, fastcgi_pass, uwsgi_pass, scgi_pass, and memcached_pass directives.
定义服务器组,不再是代理给单一的服务器,以实现负载均衡。代理时目标为定义的组名。定义组时不能定义协议,只是ip地址的组合。
【1】upstream name { … }
定义后端服务器组,会引入一个新的上下文,定义在http{}中;
upsteam websrv {
server
server
……
}
【2】server address [parameters];
在upstream中定义后端服务器成员,以及相关参数
address:
-
unix:/PATH/TOSOME_SOCK_FILE
-
IP[:port]
-
HOSTNAME[:PORT]
paramenter:
-
weight
=number
sets the weight of the server, by default, 1. -
max_fails
=number
失败尝试的次数,超过尝试的server将被标记为不可用 -
fail_timeout
=time
将服务器标记为不可用状态的超时累积时长,默认10s -
max_conns
=number
当前最大并发连接数 -
backup
将server标记为“备用”,所有在线的服务器不可用时将启用 -
down
标记为down,灰色更新
【3】least_conn;最少连接调度算法,当server拥有不同的权重时其为wlc;
【4】least_time header | last_byte;最短平均响应时长和最少连接调度算法;
【4】ip_hash;原地址hash调度方法,将同一客户端ip的请求始终发往同一server,实现持久连接;
【4】hash key [consistent];对指定的key的hash表实现请求的调度,此处的key为文本、变量或二者的组合;可使用consistent做一致性hash,默认为取模hash;作为缓存服务器应该打开consistent
,避免取模hash错乱。Embedded Variables有许多可做hash的变量。
对请求报文hash的作用是将请求分类,发往缓存服务器,以提高命中率;
-
hash $request_uri consistent;对请求为key,内容作为value的hash,将始终发往同一后端服务器
-
hash $remote_addr;同一ip始终发往一后端服务器
-
hash $cookie_name;来自同一Browser的访问发往同一后端服务器,以实现基于cookie的
session sticky
【5】keepalive connections;定义代理服务器与upstream servers长连接的个数,使用长连接以应对并发时的资源不足,但每个worker进程不能有太多的长连接,需手动定义空闲的长连接个数。
【6】health_check [parameters];
定义对后端主机的健康状态检测机制;只能用于location
上下文;
可用参数:
-
interval=time:检测频率,默认为每隔5秒钟;
-
fails=number:判断服务器状态转为失败需要检测的次数;
-
passes=number:判断服务器状态转为成功需要检测的次数;
-
uri=uri:判断其健康与否时使用的uri;
-
match=name:基于指定的match来衡量检测结果的成败;
-
port=number:使用独立的端口进行检测;
仅Nginx Plus有效;
【7】 match name { … }
Defines the named test set used to verify responses to health check requests.
定义衡量某检测结果是否为成功的衡量机制;
专用指令:
-
status:期望的响应码;
status CODE
status ! CODE
… -
header:基于响应报文的首部进行判断
header HEADER=VALUE
header HEADER ~ VALUE
… -
body:基于响应报文的内容进行判断
body ~ “PATTERN”
body !~ “PATTERN”
仅Nginx Plus有效;
RR轮询Upstream Server
vi /etc/nginx/nginx.conf
http{
#定义Upstream服务器组
upstream websrv{
server 192.168.22.2;
server 192.168.22.3:8080;
}
}
default.conf
location / {
proxy_pass http://websrv; #root失效,proxy_pass优先级高
}
#测试:默认调度算法为RR
WRR加权轮询Upstream Server
http{
upstream websrv{
server 192.168.22.2 weight=2; #2:1
server 192.168.22.3:8080;
}
}
自动识别不可用主机
http{
upstream websrv{
server 192.168.22.2 weight=2 max_fails=2;
server 192.168.22.3:80;
}
}
#手动停用第一台服务器
设置备用主机
http{
upstream websrv{
server 192.168.22.2 weight=2 max_fails=2;
server 192.168.22.3:80;
server 192.168.22.3:8080 backup;
}
}
#stop或标记为down
#server 192.168.22.2 down;
传输层反代(ngx_stream_core_module)
模拟传输层反代tcp或udp协议的服务,相当于工作在传输层的lvs调度器;
stream{
单独的stream段,先注释http段;
}
【1】stream { … }
【2】listen address:port [ssl] [udp] [proxy_protocol] [backlog=number] [bind] [ipv6only=on|off] [reuseport] [so_keepalive=on|off|[keepidle]:[keepintvl]:[keepcnt]];
传输层反代ssh服务配置
vi nginx.conf
#注释http段,保留inclued;
include /etc/nginx/conf.d/*.conf;
vi conf.d/sshsrv.conf
stream{
upstream sshsrv {
server 192.168.22.2:22;
server 192.168.22.3:22;
least_conn;
}
server {
listen 10.1.253.6:8520;
proxy_pass sshsrv;
}
}
#分别测试几种不同的调度算法:
least_conn;
least_time;
ip_hash;
hash $remote_addr;
hash $request_uri consistent; #一致性hash
hash $cookie_name;
总结
Nginx作为反代服务器调度后端的缓存服务器是一种常用的应用层负载均衡方案,其对URL和后端服务器IP做多salt的一致性hash算法以及针对不同URL的分类请求能够有效解决大量并发时的负载均衡问题。此外,Nginx反代时开启缓存功能可以大大减小后端服务器的压力。Nginx不仅仅是个轻量化的Web服务器,更为强大的代理调度功能体现出Nginx的卓越性能。著名的开源项目Tengine、OpenResty都是基于Nginx的改进和扩展,尤其是支持动态语言脚本Lua使得Nginx异常强大。
原创文章,作者:helloc,如若转载,请注明出处:http://www.178linux.com/55657