负载均衡与反向代理
在请求一个www.baidu.com网站时,浏览器首先会查询DNS服务器获取对应的IP,然后通过此IP访问对应的服务。
DNS域名解析负载均衡的优点:
- 将负载均衡的工作交给DNS,省去了网站管理维护负载均衡服务器的麻烦。
- 技术实现比较灵活、方便,简单易行,成本低,使用于大多数TCP/IP应用。
- 对于部署在服务器上的应用来说不需要进行任何的代码修改即可实现不同机器上的应用访问。
- 同时许多DNS还支持基于地理位置的域名解析,即会将用户分配到离他最近的服务器上以提升体验。
DNS域名解析的缺点:
5. DNS会有一定的缓存时间,故障后切换时间长,而且没有对后端服务进行心跳检查和失败重试的机制。
6. 不能够按服务器的处理能力来分配负载。DNS负载均衡采用的是简单的轮询算法,不能区分服务器之间的差异,不能反映服务器当前运行状态,所以其的负载均衡效果并不是太好。
7. 可能会造成额外的网络问题。为了使本DNS服务器和其他DNS服务器及时交互,保证DNS数据及时更新,使地址能随机分配,一般都要将DNS的刷新时间设置的较小,但太小将会使DNS流量大增造成额外的网络问题。
Nginx七层负载均衡
Nginx目前提供了HTTP(ngx_http_upstream_module)七层负载均衡,而1.9.0版本也开始支持TCP(ngx_stream_upstream_module)四层负载均衡。
二层负载均衡是通过改写报文的目标MAC地址为上游服务器MAC地址,源IP地址和目标IP地址是没有变的,负载均衡服务器和真实服务器共享同一个VIP,如LVS DR工作模式。
四层负载均衡是根据端口将报文转发到上游服务器(不同的IP地址+端口),如LVS NAT模式、HaProxy。
七层负载均衡是根据端口号和应用层协议如HTTP协议的主机名、URL,转发报文到上游服务器(不同的IP地址+端口),如HaProxy、Nginx
负载均衡比较重要的几个点:
- 上游服务器配置 :使用upstream server配置上游服务器。
- 负载均衡算法 :配置多个上游服务器时的负载均衡机制。
- 失败重试机制 :配置当超时或上游服务器不存活时,是否需要重试其他上游服务器。
- 服务器心跳检查 :上游服务器的健康检查/心跳检查。
Nginx提供的负载均衡可以实现上游服务器的负载均衡、故障转移、失败重试、容错、健康检查等,当某些上游服务器出现问题时可以将请求转到其他上游服务器以保障高可用,并可以通过OpenResty实现更智能的负载均衡,如将热点与非热点流量分离、正常流量与爬虫流量分离等。
反向代理
要给Nginx配置上游服务器,即负载均衡到的真实处理业务的服务器,通过在http指令下配置upstream即可。
upstream backend {
server 192.168.61.1:8080 weight=1;
server 192.168.61.1:8090 weight=2;
}
#配置proxy_pass来处理用户请求。
location / {
proxy_pass http://backend
}
当访问Nginx时,会将请求反向代理到backend配置的upstream server。
负载均衡算法
负载均衡用来解决用户请求到来时如何选择upstream server进行处理,默认采用的是round-robin(轮询)。同时也支持其他几种:
- round-robin: 轮询,默认负载均衡算法,即以轮询的方式将请求转发到上游服务器,通过配合weight配置可以实现基于权重的轮询。
- ip_hash:根据客户IP进行负载均衡,即把相同的IP分发到同一个upstream server。
upstream backend {
ip_hash;
server 192.168.61.1:8080 weight=1;
server 192.168.61.1:8090 weight=2;
}
- hash key [consistent]:对某一个key进行哈希或者使用一致性哈希算法进行负载均衡。
consistent_key动态指定一般通过Lua设置:
- least_conn:将请求转发到最少活跃连接的上游服务器。
Nginx商业版还提供了least_time,即基于最小平均响应时间进行负载均衡
失败重试
upstream server的配置:
upstream backend {
server 192.168.61.1:8080 max_fails=2 fail_timeout=10s weight=1;
server 192.168.61.1:8090 max_fails=2 fail_timeout=10s weight=2;
}
当fail_timeout时间内失败了max_fails次请求,则认为该上游服务器不可用/不存活,然后将摘掉该上游服务器,
fail_timeout时间后会再次将该服务器加入到存活上游服务器列表进行重试。
proxy_pass的配置:
location /test {
proxy_connect_timeout 5s;
proxy_read_timeout 5s;
proxy_send_timeout 5s;
proxy_next_upstream error timeout;
proxy_next_upstream_timeout 10s;
proxy_next_upstream_tries 2;
proxy_pass http://backend
add_header upstream_addr $upstream_addr
}
当遇到配置的错误时,会重试下一台上游服务器。
健康检查
Nginx对上游服务器的健康检查默认采用的是惰性策略,Nginx商业版提供了health_check进行主动健康检查。当然也可以集成nginx_upstream_check_module 模块来进行主动健康检查。
nginx_upstream_check_module支持TCP心跳和HTTP心跳来实现健康检查。
TCP心跳检查
upstream backend {
server 192.168.61.1:8080 weight=1;
server 192.168.61.1:8090 weight=2;
check interval=3000 rise=1 fall=3 timeout=2000 type=tcp;
}
- interval: 检测间隔时间,此处配置了每隔3s检测一次。
- fall: 检测失败多少次后,上游服务器被标识为不存活。
- rise: 检测成功多少次后,上游服务器被标识为存活,并可以处理请求。
- timeout: 检测请求超时时间配置
HTTP心跳检查
upstream backend {
server 192.168.61.1:8080 weight=1;
server 192.168.61.1:8090 weight=2;
check interval=3000 rise=1 fall=3 timeout=2000 type=http;
check_http_send "HEAD /status HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}
HTTP心跳检查有如下两个需要额外配置:
- check_http_send: 即检查时发的HTTP请求内容。
- check_http_expect_alive: 当上游服务器返回匹配的响应状态码时,则认为上游服务器存活。
HTTP动态负载均衡
我们可以发现,如果每次upstream列表有改动,都需要到服务器进行修改。
更好的做法是实现upstream服务的自动发现。
Consul是一款开源的分布式服务注册与发现系统,通过HTTP API可以使得服务注册、发现实现起来非常简单,它支持如下特性。
-
服务注册: 服务实现者可以通过HTTP API或DNS方式,将服务注册到Consul。
-
服务发现: 服务消费者可以通过HTTP API或DNS方式,从Consul获取服务的IP 和PORT。
-
故障检测: 支持如TCP、HTTP等方式的健康检查机制,从而当服务有故障时自动摘除。
-
K/V存储: 使用K/V存储实现动态配置中心,其使用HTTP长轮询实现变更触发和配置更改。
-
多数据中心: 支持多数据中心,可以按照数据中心注册和发现服务,即支持只消费本地机房服务,使用多数据中心集群还可以避免单数据中心的单点故障。
-
Raft算法: Consul使用Raft算法实现集群数据一致性。
通过Consul可以管理服务注册与发现,接下来需要有一个与Nginx部署在同一台机器的Agent来实现Nginx配置更改和Nginx重启功能。我们有Confd或者Consul-template两个选择,而Consul-template是Consul官方提供的,我们就选择它了。其使用HTTP长轮询实现变更触发和配置更改(使用Consul的watch命令实现)。也就是说,我们使用Consul-template实现配置模板,然后拉取Consul配置渲染模板来生成Nginx实际配置。
Consul+Consul-template配置:
Consul+OpenResty实现无reload动态负载均衡:
这个配置是定时轮询,不是走的长轮询,有一定的时延。有个解决方案,是在Nginx上暴露HTTP API,在配置Agent长轮询拉取,然后调用HTTP API推送到Nginx上,Agent可以部署在Nginx本机或者远程。
参考《亿级流量网站架构核心技术》