微服务之负载均衡
微服务都是分布式系统, 多个服务工作在一起齐心协力完成所需的工作, 事情不能交给一个人做, 所工作分担到多个微服务上是为负载均衡.
负载均衡策略
负载均衡 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 集成, 拿到注册的服务器地址列表后, 根据设定的规则进行客户端的负载均衡.