负载均衡用于从“upstream”模块定义的后端服务器列表中选取一台服务器接受用户的请求,一个最基本的upstream模块是这样的,模块内的server是服务器列表:
- #动态服务器组
- upstream dynamicserver {
- server 172.16.44.47:9001; #tomcat 1
- server 172.16.44.47:9002; #tomcat 2
- server 172.16.44.47:9003; #tomcat 3
- server 172.16.44.47:9004; #tomcat 4
- }
在upstream模块配置完成后,要让指定的访问反向代理到服务器列表
- #其他页面反向代理到tomcat容器
- location ~.*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- }
这就是最基本的负载均衡实例,但这不足以满足实际需求;目前Nginx服务器的upstream模块支持6种方式的分配。
完整配置文件如下
- upstream dynamicserver {
- server 192.168.64.1:9001; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
- server {
- server_name www.itcast.com;
- default_type text/html;
- charset utf-8;
-
- location ~ .*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- }
- }
参数 | 描述 |
---|---|
server | 反向服务地址 加端口 |
weight | 权重 |
fail_timeout | 与max_fails结合使用。 |
max_fails | 设置在fail_timeout参数设置的时间内最大失败次数,如果在这个时间内,所有针对该服务器的请求都失败了,那么认为该服务器会被认为是停机了 |
max_conns | 允许最大连接数 |
fail_time | 服务器会被认为停机的时间长度,默认为10s |
backup | 标记该服务器为备用服务器,当主服务器停止时,请求会被发送到它这里。 |
down | 标记服务器永久停机了 |
slow_start | 当节点恢复,不立即加入 |
在这里,只详细说明Nginx自带的负载均衡策略。
负载策略 | 描述 |
---|---|
轮询 | 默认方式 |
weight | 权重方式 |
ip_hash | 依据ip分配方式 |
least_conn | 最少连接方式 |
fair(第三方) | 响应时间方式 |
url_hash(第三方) | 依据URL分配方式 |
最基本的配置方法,上面的例子就是轮询的方式,它是upstream模块默认的负载均衡默认策略,每个请求会按时间顺序逐一分配到不同的后端服务器。
- #动态服务器组
- upstream dynamicserver {
- server 192.168.64.1:9001; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
注意
配置示例
- upstream dynamicserver {
- server 192.168.64.1:9001; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
-
- server {
- server_name www.test.com;
- default_type text/html;
- charset utf-8;
-
- location ~ .*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- }
- }
权重方式,在轮询策略的基础上指定轮询的几率
- #动态服务器组
- upstream dynamicserver {
- server 192.168.64.1:9001 weight=2; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
weight参数用于指定轮询几率,weight的默认值为1,;weight的数值与访问比率成正比,比如Tomcat 7.0被访问的几率为其他服务器的两倍。
注意
指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话,这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题
- upstream dynamicserver {
- ip_hash; #保证每个访客固定访问一个后端服务器
- server 192.168.64.1:9001 weight=2; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
注意
把请求转发给连接数较少的后端服务器,轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高,这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
- upstream dynamicserver {
- least_conn; #把请求转发给连接数较少的后端服务器
- server 192.168.64.1:9001 weight=2; #tomcat 1
- server 192.168.64.1:9002; #tomcat 2
- server 192.168.64.1:9003; #tomcat 3
- server 192.168.64.1:9004; #tomcat 4
- }
现在对外服务的网站,很少只使用一个服务节点,而是部署多台服务器,上层通过一定机制保证容错和负载均衡。
为了方便理解,使用了以下配置进行分析(proxy_next_upstream
没有特殊配置)
- upstream dynamicserver {
- server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Server A
- server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
- }
max_fails=3 fail_timeout=60s
代表在60
秒内请求某一应用失败3
次,认为该应用宕机,后等待60
秒,这期间内不会再把新请求发送到宕机应用,而是直接发到正常的那一台,时间到后再有请求进来继续尝试连接宕机应用且仅尝试1
次,如果还是失败,则继续等待60
秒...以此循环,直到恢复
模拟后端异常的方式是直接将对应服务关闭,造成 connect refused 的情况,对应
error
错误。
在最初始阶段,所有服务器都正常,请求会按照轮询方式依次转发给 AB 两个 Server 处理。假设这时 A 节点服务崩溃,端口不通,则会出现这种情况:
如果在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:
请求 1 转到 B 异常,此时所有线上节点异常,会出现:
请求 2 依次经过 AB 均无法正常处理, 触发 no live upstreams
报错,返回 502 错误
有时候我们系统出现500
等异常的情况下,希望nginx能够到其他的服务器进行重试,我们可以配置那些错误码才进行重试。在nginx的配置文件中,proxy_next_upstream项定义了什么情况下进行重试,官网文档中给出的说明如下
- Syntax: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ...;
- Default: proxy_next_upstream error timeout;
- Context: http, server, location
默认情况下,当请求服务器发生错误或超时时,会尝试到下一台服务器,还有一些其他的配置项如下:
错误状态 | 描述 |
---|---|
error | 与服务器建立连接,向其传递请求或读取响应头时发生错误; |
timeout | 在与服务器建立连接,向其传递请求或读取响应头时发生超时; |
invalid_header | 服务器返回空的或无效的响应; |
http_500 | 服务器返回代码为500的响应; |
http_502 | 服务器返回代码为502的响应; |
http_503 | 服务器返回代码为503的响应; |
http_504 | 服务器返回代码504的响应; |
http_403 | 服务器返回代码为403的响应; |
http_404 | 服务器返回代码为404的响应; |
http_429 | 服务器返回代码为429的响应(1.11.13); |
non_idempotent | 通常,请求与 非幂等 方法(POST,LOCK,PATCH)不传递到请求是否已被发送到上游服务器(1.9.13)的下一个服务器; 启用此选项显式允许重试此类请求; |
off | 禁用将请求传递给下一个服务器。 |
这里面我们配置了500
等错误的时候会进行重试
- upstream dynamicserver {
- server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #tomcat 1
- server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #tomcat 2
- }
-
- server {
- server_name www.itcast.com;
- default_type text/html;
- charset utf-8;
-
- location ~ .*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- #下一节点重试的错误状态
- proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
- }
- }
在正常的情况下如果
500
错误会直接出现异常页面,现在我们加入了以上500
重试策略,重试错误的流程和上面流程是一样的
Nginx 支持设置备用节点,当所有线上节点都异常时启用备用节点,同时备用节点也会影响到失败重试的逻辑,因此单独列出来介绍。
upstream 的配置中,可以通过
backup
指令来定义备用服务器,其含义如下
- upstream dynamicserver {
- server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Service A
- server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
- server 192.168.64.1:9003 backup; #backup
- }
-
- server {
- server_name www.itcast.com;
- default_type text/html;
- charset utf-8;
-
- location ~ .*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- #下一节点重试的错误状态
- proxy_next_upstream error timeout http_500 http_502 http_503 http_504;
- }
- }
在最初始阶段,所有服务器都正常,请求会按照轮询方式依次转发给 AB 两个节点处理。当只有 A 异常的情况下,与上文没有 backup 服务器场景处理方式一致,这里就不重复介绍了。
假设在 A 的屏蔽期还没结束时,B 节点的服务也崩溃,端口不通,则会出现:
请求 1 转到 B 处理,异常,此时所有线上节点异常,会出现:
请求 2 再依次经过 A、B 节点异常,转到 backup 处理,两者 fails 都达到 max_fails:
假设 AB 的屏蔽期都还没结束时,C 节点的服务也崩溃,端口不通,则会出现
请求 1 转到 C 异常,此时所有节点(包括 backup)都异常,会出现:
no live upstreams
报错,返回 502 错误请求 2 依次经过 AB 节点异常,重试到 C 异常,最终结果如上个步骤,返回 502 错误
默认配置是没有做重试机制进行限制的,也就是会尽可能去重试直至失败,Nginx 提供了以下两个参数来控制重试次数以及重试超时时间
proxy_next_upstream_tries
:设置重试次数,默认 0
表示无限制,该参数包含所有请求 upstream server 的次数,包括第一次后之后所有重试之和;proxy_next_upstream_timeout
:设置重试最大超时时间,默认 0
表示不限制,该参数指的是第一次连接时间加上后续重试连接时间,不包含连接上节点之后的处理时间- upstream dynamicserver {
- server 192.168.64.1:9001 fail_timeout=60s max_fails=3; #Server A
- server 192.168.64.1:9002 fail_timeout=60s max_fails=3; #Server B
- }
-
- server {
- server_name www.itcast.com;
- default_type text/html;
- charset utf-8;
-
- location ~ .*$ {
- index index.jsp index.html;
- proxy_pass http://dynamicserver;
- # 表示重试超时时间是3s
- proxy_connect_timeout 3s;
- #表示在 6 秒内允许重试 3 次,只要超过其中任意一个设置,Nginx 会结束重试并返回客户端响应
- proxy_next_upstream_timeout 6s;
- proxy_next_upstream_tries 3;
- }
- }
- server {
- listen 10086;
- server_name www.test.com;
-
- location / {
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- proxy_set_header X-NginX-Proxy true;
- proxy_redirect off;
- }
-
- location /data/ {
- alias '/usr/local/data/';
- #这里是重点,就是代理这个文件夹
- expires 7d;
- }
- }
访问 http://localhost:10086/data/下面的资源就是访问/usr/local/data文件夹的资源
- server {
- listen 80;
- server_name www.itcast.com;;
-
- location / {
- proxy_pass http://127.0.0.1:8080;
- index index.html index.htm .jsp;
- }
- }
- server {
- listen 80;
- server_name www.itcast.com;
-
- if ( $host ~ (.*).itcast.com){
- set $domain $1;##记录二级域名值
- }
- #是否允许请求带有验证信息
- add_header Access-Control-Allow-Credentials true;
- #允许跨域访问的域名,可以是一个域的列表,也可以是通配符*
- add_header Access-Control-Allow-Origin *;
- #允许脚本访问的返回头
- add_header Access-Control-Allow-Headers 'x-requested-with,content-type,Cache-Control,Pragma,Date,x-timestamp';
- #允许使用的请求方法,以逗号隔开
- add_header Access-Control-Allow-Methods 'POST,GET,OPTIONS,PUT,DELETE';
- #允许自定义的头部,以逗号隔开,大小写不敏感
- add_header Access-Control-Expose-Headers 'WWW-Authenticate,Server-Authorization';
- #P3P支持跨域cookie操作
- add_header P3P 'policyref="/w3c/p3p.xml", CP="NOI DSP PSAa OUR BUS IND ONL UNI COM NAV INT LOC"';
- if ($request_method = 'OPTIONS') {##OPTIONS类的请求,是跨域先验请求
- return 204;##204代表ok
- }
- }
通过Referer实现防盗链比较基础,仅可以简单实现方式资源被盗用,构造Referer的请求很容易实现
场景:由于图片链接可以跨域访问,所以图片链接往往被其他网站盗用,从而增加服务器负担;
解决方案:nginx可以通过valid_referers配置进行防盗链配置
指定合法的来源'referer', 他决定了内置变量$invalid_referer的值,如果referer头部包含在这个合法网址里面,这个变量被设置为0,否则设置为1. 需要注意的是:这里并不区分大小写的.
*.google.com
的域名请求访问资源- # 需要防盗的后缀
- location ~* \.(jpg|jpeg|png|gif|bmp|swf|rar|zip|doc|xls|pdf|gz|bz2|mp3|mp4|flv)$
- #设置过期时间
- expires 30d;
- # valid_referers 就是白名单的意思
- # 支持域名或ip
- # 允许ip 192.168.0.1 的请求
- # 允许域名 *.google.com 所有子域名
- valid_referers none blocked 192.168.0.1 *.google.com;
- if ($invalid_referer) {
- # return 403;
- # 盗链返回的图片,替换盗链网站所有盗链的图片
- rewrite ^/ https://site.com/403.jpg;
- }
- root /usr/share/nginx/img;
- }