微服务连接:Subset 子集划分算法
1.大型数据中心(集群)的连接管理
在数据中心(集群)内部,后端的所有服务一般均以长连接的方式进行通信,常见的通信协议有 HTTP,RPC 等,连接建立之后就会一直保持,直到某一方服务进行重新发版/重启等,连接才会重新建立。
例如,下图给出了一个简单的微服务划分示意图,在图中,网关与后端服务之间,后端服务与后端服务之间均以 RPC 的形式进行通信。
注:本文介绍的连接管理均是针对于数据中心内部的,也就是内网环境下的服务连接管理,非面向外部的负载均衡,CDN 等连接管理。
1.1-小型数据中心
对于小型数据中心而言,其接收的外部流量并不多,单个微服务部署的数量也并不会多。
此处,我们假设针对与每个 Backend Service,我们都将其部署为 3 个实例(在 k8s 中对应为 3 个 Pod)。例如对于 User Service 和 Account Service 我们各自部署了 3 个实例,User Service 分别与每个 Account Service 都建立了 RPC 连接,那么此处一共建立了 9 个 RPC 连接进行服务的通信。
可以看到,对于小型的数据中心,微服务的数量并不多,即使全部建立连接,对于这些连接的管理并不会成为我们在部署和运维微服务时遇到的难题。
1.2-大型数据中心
对于大型数据中心而言,其需要承载更多的外部流量,在微服务部署时需要考虑更大的容量,在水平方向上,通常表现为部署更多的服务数量。
例如,对于谷歌,微软,阿里巴巴等公司,其核心的服务通常需要承载来自全国/全世界的流量,在微服务的部署上,单个领域服务的数量绝对不单单只有若干台。此处我们假设针对于每隔领域服务,其部署的数量为 100 台。
如果我们再按照 1.1 中所介绍的方式,每一台服务都与下游建立建立,那么在此业务场景中,User service 与 Account Service 建立的连接数目就为 100*100=10000。
1W 条 RPC 连接,而且这还只是 2 个 Service 之间的连接数量,当微服务的数量变多时,这个连接的数量将会呈指数级别递增。
1.3-连接管理所遇到的问题
针对于 1.2 中介绍的场景,全量的微服务连接遇到的问题有很多:
资源消耗:连接管理需要占用系统资源,爆炸式的连接数量会占用非常多的系统资源,包括机器的 CPU/内存,网络的带宽等。
糟糕的网络链路:微服务之间建立连接之后,通常会定时传输探活心跳包(health check),当连接的数量变多时,这些心跳包会消耗过多的网络带宽,从而使得网络通信链路变差。
连接过剩:想一想,100 台 User service 连接 100 台 Account Service,真的每一条连接都有使用到么?当用户流量处于平均水平或者低峰期时,有的连接虽然建立了,但是可能并没有接收到用户流量,从而一直处于"空连"状态。
......
1.4-理想状况
针对于理想的情况,我们肯定是希望每一台机器只与下游的若干台机器建立连接,而不是全量建立连接。在能够承载数据中心外部流量的情况下,最大化服务资源利用率。
当然,连接管理不能是随意的,例如下图所示,所有的 User Service 只与 Account Service 的其中一台/若干台建立连接,那么显然这是糟糕的。
理想情况下,我们希望将所有的连接打散在各个后端机器中,使得每一台后端机器所承载的负载是一致的,并且资源利用率是最大的。
例如,下图还给出了在糟糕的与良好的连接管理下,后端服务所表现的负载情况(此处以 CPU 为指标作为参考。)
2.Subset:子集划分算法
subset 子集划分算法是 Google 提出的,核心的目的就是为了解决大型数据中心内部的服务连接。
子集算法从全集群中选取一批节点(子集),而不是选取全部节点,利用划分子集限制连接池的大小。
2.1-Subset 算法要解决的问题
避免出现"爆炸式的微服务连接数量"场景。
提供良好的连接分配管理方式,保证后端负载均衡。
当服务数量变化(服务宕机/重启/滚东更新/扩容等)时,最小化连接变更。
2.2-Subset 算法实现
2.3-演示案例
假设,我们此处有 12 个 Account Service(backends 数组长度为 12),子集大小为 3(subsetSize 为 3),有 10 个 User Service(每一个 User Service 会连接 3 个 Account Service)。
对于 12 个 Account Service,我们从 0 开始编号,编号为[0,1,2,3....11]。对于 10 个 User Service,我们从 0 开始编号,编号为[0,1,2,3....9]。
首先,Round 总共有 3 轮,分别称为 Round 0,Round 1,Round 2,在每一轮中,Account Service 的序号被随机打散,如下所示:
Round 0:[0,6,3,5,1,7,11,9,2,4,8,10]
Round 1:[8,11,4,0,5,6,10,3,2,7,9,1]
Round 2:[8,3,7,2,1,4,9,10,6,5,0,11]
为什么是 3 轮:对于每一个 User Service,需要连接 3 个 Account Service,所以每一轮只能给 4 个 User Service 分配实例。例如下图所示:
每一轮的 12 个 Account Service 被 subsetsize 切割为 4 份,每一份有 3 个 Account Service。
然后每一个 User Service(Client-X)连接 3 个(subsetSize 决定)Account Service。
那么 10 个 Account Service 总共需要 3 轮才能全部分配到对应的 Account Service。
2.4-Subset 算法为什么是均匀的
通过上图我们可以看到,只要 client 的 id 是连续的,那么他们就会依次被分配到 Round 0,Round 1,Round 2....Round N 中,并且每隔客户端不会出现重叠的情况。
2.5-小插入:subset 不是绝对均衡的
在上图中,可以看到在 Round2 中,最后 6 个 Account Service 是没有被分配连接的。因此也不是决定负载均衡的。简单的说就是有一部分 backend 无法被 client 负载,backend 的负载不能完全均衡。
因此,subset 也不是决定均衡的,需要看你的服务数量与对应的划分子集大小。
3.其他
3.1-client 的上下线
在 2.4 中介绍过,只要 client id 是连续递增的,那么他们会就被依次分配到 Round 中。
client 的下线:client 的下线时,已存在的 client 连接不会变化,下线的 client 对应的 Round 中 backend 节点连接数变少。
client 的上线:只要保证 client id 是连续递增的,那么根据 subset 算法,会循环在 Round 后面进行 backend 分配。
3.2-backend 的上下线
对于 backend,在每一轮 Round 中是被随机分配的,因此 backend 的上下线不会导致连接变动很大,都是打散的。
版权声明: 本文为 InfoQ 作者【董哥的黑板报】的原创文章。
原文链接:【http://xie.infoq.cn/article/2075b4d8473854bd606ab49e0】。文章转载请联系作者。
评论