微服务都是分布式系统, 多个服务工作在一起齐心协力完成所需的工作, 事情不能交给一个人做, 所工作分担到多个微服务上是为负载均衡.
负载均衡策略
负载均衡 Load Balance 是最基本的分流策略, 将负载尽量均匀地分布到下游节点上, 但是如何分派, 是有讲究的.
大致有以下四种基本策略:
ROUND_ROBIN 均匀分配
周而复始地循环依次地选择一个个节点来分配负载, 保持每个节点具有相同的负载
它还有一个加强版, 可以为节点设置不同的权重LEAST_CONNECTIONS 最少连接
根据客户端建立的连接数选择节点, 连接数量最少的节点将被选中.LEAST_RESPONE_TIME 最短响应时间
根据服务器端对从客户端请求的响应时间长短, 响应最快的节点将被选中.SOURCE_IP 源地址哈希
根据客户端的IP地址计算哈希值,然后使用哈希值进行路由, 这可确保即使面对断开的连接,来自同一客户端的请求也始终会转到同一服务器。
上述四种负载均衡策略一般都会加上健康检查功能, 定时查询下游节点的健康状态, 如果没有响应或者响应错误则在分派候选名单剔除这个节点, 直到节点恢复健康
负载均衡方法
DNS
DNS 会将域名映射为 IP 地址, 假设你有三台 server, 域名(Domain Name)为 www.mysite.com, DNS(Domain Name System) 会根据你的注册信息按照轮转的方式返回这三台 server 列表, 例如
www.mysite.com
解析为:
192.168.1.1
192.168.1.2
192.168.1.3
这个列表可以是变化的, 每次请求把上次列表的第一个放到最后
www.mysite.com
解析为:
192.168.1.2
192.168.1.3
192.168.1.1
可以用 nslookup 或 dig 命令查询域名和它实际映射的服务地址
nslookup baidu.com
Server: 64.104.123.245
Address: 64.104.123.245#53
Non-authoritative answer:
Name: baidu.com
Address: 123.125.115.110
Name: baidu.com
Address: 220.181.57.216
dig 命令显示的内容更多一些, 还可以加上 +trace
选项来显示DNS所查询用到服务器路径
$ dig cisco.com
; <<>> DiG 9.8.3-P1 <<>> cisco.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 3992
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 3, ADDITIONAL: 6
;; QUESTION SECTION:
;cisco.com. IN A
;; ANSWER SECTION:
cisco.com. 5 IN A 72.163.4.161
;; AUTHORITY SECTION:
cisco.com. 1800 IN NS ns1.cisco.com.
cisco.com. 1800 IN NS ns3.cisco.com.
cisco.com. 1800 IN NS ns2.cisco.com.
;; ADDITIONAL SECTION:
ns1.cisco.com. 600 IN A 72.163.5.201
ns2.cisco.com. 1800 IN A 64.102.255.44
ns3.cisco.com. 1800 IN A 173.37.146.41
ns1.cisco.com. 1800 IN AAAA 2001:420:1101:6::a
ns2.cisco.com. 1800 IN AAAA 2001:420:2041:5000::a
ns3.cisco.com. 1800 IN AAAA 2001:420:1201:7::a
;; Query time: 169 msec
;; SERVER: 64.104.123.245#53(64.104.123.245)
;; WHEN: Sat Nov 10 18:24:42 2018
;; MSG SIZE rcvd: 229
DNS 作为负载均衡固然简单,可是 DNS 缺点也不少
DNS 是一个分级系统, 本地域名服务器若查不到相应记录, 会向上级域名服务器查询,查到结果后在本地缓存至 TTL 时间过期
DNS 也会在本地缓存, 客户端解析域名为IP 之后可能一直使用很长时间, 对于客户端的行为你无法控制
DNS 解析速度并不快, 你要是把 TTL 设置得太小,缓存很快过期, 性能问题就会凸显DNS 在实时检测分流, 为域名实时添加和删除服务器方面很不方便,即使你能很快地搞定你的内部 DNS 系统,你依然需要等待在下级域名服务器和客户端的缓存过期
所以 DNS 通常就作为第一级负载均衡方案, 采用简单的 Round Robbin 策略,映射到几个 VIP 上
DNS SRV
服务记录(SRV记录)是域名系统中的数据规范,其定义用于指定服务的服务器的位置,即主机名和端口号。 它在RFC 2782中定义,其类型代码为33. 某些Internet协议(如会话初始协议(SIP)和可扩展消息传递和在线协议(XMPP))通常需要网络元素支持SRV。
一个 SRV 的记录格式如下:
_service._proto.name. TTL class SRV priority weight port target.
- service: 服务名称,比如 sip, xmpp, 等等等 the symbolic name of the desired service.
- proto: 传输层的协议,TCP 或 UDP
- name: 与之相对应的有效域名, 以 . 结束
- TTL: 标准的 DNS 生存时间 (Time To Live)
- class: 标准的 DNS 类别 (这里总是 IN)
- priority: 目的服务器的优先级, 数值越低代表优先级越高
- weight: 权重, 相同优先级的一个相对权重值, 数值越大,权重越高
- port: 该服务所用的 TCP 或 UDP 的端口
- target: 提供该服务的规范的主机名称, 以 . 结尾
例如以下的 sip 服务的 DNS SRV
_sip._tcp.example.com. 86400 IN SRV 0 5 5060 sipserver.example.com.
常用命令
可以用 dig 和 nslookup 来查看 DNS SRV 的具体信息
$ dig _sip._tcp.example.com SRV
$ host -t SRV _sip._tcp.example.com
$ nslookup -querytype=srv _sip._tcp.example.com
$ nslookup
> set querytype=srv
> _sip._tcp.example.com
服务器端负载负载
局部与全局负载均衡
SLB(Server load balancing)是对集群内物理主机的负载均衡,而GSLB是对物理集群的负载均衡。
这里的负载均衡可能不只是简单的流量均匀分配,而是会根据策略的不同实现不同场景的应用交付。
GSLB(Global server load balancing) 是依赖于用户和实际部署环境的互联网资源分发技术,不同的目的对应着一系列不同的技术实现 - 基于DNS实现、基于重定向实现、基于路由协议实现。
如图所示
用户的客户端 Client 向 DNS server 查询 mysite.com 的地址
DNS server 返回域名所对应的服务器地址
2.1 用户向本级配置的本地DNS服务器发出查询请求,如果本地DNS服务器有该域名的缓存记录,则返回给用户,否则进行下一步;
2.2 本地DNS服务器进行递归查询,最终会查询到域名注册商处的授权DNS服务器
2.3 授权DNS服务器返回一条NS记录给本地DNS服务器。这条NS记录可能是指向随机一个GSLB设备的接口地址或者是所有GSLB设备的接口地址;
2.4 本地DNS服务器向其中一个GSLB地址发出域名查询请求,如果请求超时会向其它地址发出查询;
2.5 GSLB设备选出最优解析结果,返回一条A记录给本地DNS服务器。根据全局负载均衡策略设定的不同可能返回一个或多个VIP地址;
2.6 本地服务器将查询结果通过一条A记录返回给用户,并将缓存这条记录。
此例中由于client 和 数据中心 DC1 在一个区域, 响应更快, GSLB 选出 DC 1 的 Float VIP
用户连接 Float VIP, 它是一个飘移的虚拟地址, 当主集群健康时时候就分派请求到主集群 Primary LB 的负载均衡器地址, 否则就连到备份集群 Backup LB
数据中心1的主集群被选中, 请求分派到主集群 Primary LB 负载均衡器地址
主集群负载均衡器 Primary LB 将请求分派到一个选中的应用程序服务器 AppServer
负载均衡设备及软件
硬件一般选用的比较多的有 Citrix Netscalar, Cisco ACE, F5 等等, 性能强劲, 功能强大, 从传输层到应用层完美支持, 就是价格不菲.
软件方面就我所知, 开源的 HAProxy 和 Nginx 都很常见, 现在基于云端的负载均衡也渐渐多了起来
下面重点讲讲如下两个软件版本的负载均衡软件, 先用 Python Flask 写一个最简单的应用
vi app.py
from flask import Flask, Response
import optparse
app = Flask(__name__)
counter = 0
@app.route("/")
def hello():
global counter
counter = counter + 1
return Response("Hits: %d" % counter)
if __name__ == "__main__":
parser = optparse.OptionParser()
parser.add_option('-p', '--port',
action="store", dest="port",
help="listen port", default="5000")
options, args = parser.parse_args()
print('listen port:', options.port)
app.run("0.0.0.0", port=options.port, debug=True)
vi requirements.txt
Flask>=0.12.2
以 ubuntu 系统为例, 运行如下命令在 5001 和 5002 两个端口分别启动这个小服务
virtualenv -p python3 venv
pip install -r requirements.txt
python app.py 5001 &
python app.py 5002 &
下面我们在 ubuntu 系统上分别用 nginx 和 haproxy 来做负载均衡
Nginx
它是著名的 web server , 由于性能卓越, 也常用来做 HTTP 层的反向代理和负载均衡
安装
apt install nginx
配置
vi /etc/nginx/nginx.conf
# Nginx configuration
upstream wiki_app_server {
server 127.0.0.1:5001 weight=1 max_fails=2 fail_timeout=30s;
server 127.0.0.1:5002 weight=1 max_fails=2 fail_timeout=30s;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name localhost;
index index.html;
error_log /var/log/nginx/error.log;
access_log /var/log/nginx/access.log;
root /workspace/walter/wfnote/site;
location /wiki/
{
proxy_pass [http://wiki_app_server;](http://wiki_app_server)
proxy_set_header Host $host:$server_port;
proxy_set_header X-Forwarded-For $remote_addr;
}
}
启动
/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg
使用
访问 http://127.0.0.1
第一次请求, 显示 Hits: 1
第二次请求, 显示 Hits: 1
第三次请求, 显示 Hits: 2
第四次请求, 显示 Hits: 2
HAProxy
HAProxy 可以说是最流行的负载均衡软件, 它不同于 nginx , 专注于代理和流量分派, 在传输层和应用层都有比较好的支持
安装
apt install haproxy
配置
vi /etc/haproxy/haproxy.cfg
global
log 127.0.0.1 local0 notice
maxconn 2000
user haproxy
group haproxy
defaults
log global
mode http
option httplog
option dontlognull
retries 3
option redispatch
timeout connect 5000
timeout client 10000
timeout server 10000
listen appname 0.0.0.0:80
mode http
stats enable
balance roundrobin
option httpclose
option forwardfor
server lamp1 127.0.0.1:5001 check
server lamp2 127.0.0.1:5002 check
启动
/usr/sbin/haproxy -f /etc/haproxy/haproxy.cfg
使用
访问 http://127.0.0.1
第一次请求, 显示 Hits: 1
第二次请求, 显示 Hits: 1
第三次请求, 显示 Hits: 2
第四次请求, 显示 Hits: 2
客户端负载均衡
在客户端使用配置的若干个接入点, 在客户端使用轮询的策略进行访问, 比如Cassandra 或 Kafka 在启动时只需配置两三个接入点的地址, 之后会从服务器端拿到整个集群的地址, 在客户端根据预设的负载均衡策略来访问相应的节点.
Spring Cloud Ribbon 是 Netflix 开源的一个基于Http和TCP的客户端负载均衡工具,可与服务注册中心 Eureka 集成, 拿到注册的服务器地址列表后, 根据设定的规则进行客户端的负载均衡.