专栏首页码农桃花源用 subsetting 限制连接池中的连接数量

用 subsetting 限制连接池中的连接数量

内网使用服务发现后,服务与其它服务的实例之间使用一条 TCP 长连接进行通信。这种情况下常见的做法是按照 registry 下发的 host:port 列表来直接建连。

简单来说就是下图这样:

每一个服务实例都需要和它依赖的服务的每一个实例都把连接给建上。如果各个服务的规模不大,这样没什么问题。

互联网公司的核心服务规模都比较大,几千/万台机器(或几千/万个实例)的单一服务并不少见,这时候 client 要和所有 server 实例建连,会导致 client 端的 conn pool 里有大量连接,当然,server 端自然也少不了,这么多连接可能会产生一些问题:

  • 活跃的连接管理需要使用连接池,依赖 5~6 个大服务就得建出几万条连接来,如果是在 Go 里,那我们就得有一堆 goroutine 了
  • 同理,client 端的连接和 server 端都是对应的,server 端也好不到哪里去
  • 连接保活需要收发应用层心跳以应对网络的异常情况,这也是有成本的,极端情况下可能服务没有请求的前提下,心跳请求就消耗了 40% 的 CPU

如果让我们自己去设计一个新的方案,挑选一些实例来建连的话,还是有点难的,这里面要考虑:

  • 连接分布应该均衡,不能这个实例 10 条,那个 199 条
  • server 和 client 上下线,不能造成大量的连接重建和迁移
  • 连接要够用,不能影响客户端

Google 的 subset 算法

好在 Google 爸爸给我们提供了一个解决方案:subsetting。当然,SRE 书里是先说了随机的 subset 不可以,这里的算法被称为 deterministic subsetting:

func Subset(backends []string, clientID, subsetSize int) []string {

 subsetCount := len(backends) / subsetSize

 // Group clients into rounds; each round uses the same shuffled list:
 round := clientID / subsetCount

 r := rand.New(rand.NewSource(int64(round)))
 r.Shuffle(len(backends), func(i, j int) { backends[i], backends[j] = backends[j], backends[i] })

 // The subset id corresponding to the current client:
 subsetID := clientID % subsetCount

 start := subsetID * subsetSize
 return backends[start : start+subsetSize]
}

抄作业有点丢人,我们还是管这个叫借鉴业界先进经验好了。算法非常的短,不过还是需要解释清楚的。

入参:backends 指的是你的 server 端列表,client_id 我们可以给 client 分配一个 id,subsetSize 其实就是你的 client 端的连接量需求,也就是我一个 client 端对应的一个外部依赖,建立多少条连接合适,那么最终也就会从这个大 backends 列表中挑出 subsetSize 个项来。

为什么是均匀的

首先,shuffle 算法保证在 round 一致的情况下,backend 的排列一定是一致的。

因为每个实例拥有从 0 开始的连续唯一的自增 id,且计算过程能够保证每个 round 内所有实例拿到的服务列表的排列一致,因此在同一个 round 内的 client 会分别 backend 排列的不同部分的切片作为选中的后端服务来建连。

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]

上面是不同 round 的 backends 排列,如果我的 client id 计算出的 round 是 0,且这个 client 需要 4 条连接,则说明每个 round 可以分为 3 组,client id % 3 = 0,那么这个 client 则应该选择 [0, 6, 3, 5] 这 4 条连接。如果 client id % 3 = 2,则应该选择 [2, 4, 8, 10] 这 4 条连接。

Round 0: [0, 6, 3, 5, 1, 7, 11, 9, 2, 4, 8, 10]
            第 0 组      第 1 组      第 2 组

比较好理解,只要我的 client id 能保证连续,那么 client 打到后端的连接则一定是均匀的。

上下线的情况

client 上下线

client 上下线用滚动更新的方式,并不会影响其它 client 的连接分布,所以每个 client 下线时,只是对应的后端少了一些连接,暂时会导致某些 backend 的连接比其它 backend 少 1。

上线 client 从尾部开始,client id 依然是递增的,按照该算法,这些 client 会继续排在其它 client 后面,一个 round 一个 round 地将连接分布在后端服务上,也必然是均匀的。

server 上下线

与 client 上下线类似,server 的滚动升级和上下线也是不会有大影响的,因为每个 server 会随机地分布在不同 client 的子集中,不会因为该 server 上下线,导致计算结果有大变化。

这个算法的问题

这个算法看上去比较完美,但是问题在于它需要一些前提。

每个服务都能被分配从 0 到 N 的连续唯一 id,这一点在没有外部依赖的情况下比较难做到。绑定了外部基础设施的方案又可能比较难推广。比如 k8s 的 statefulset,也没办法强制所有服务都使用?

服务下线时,并不一定能保证下线的服务的 client id 是连续的,这样就总是可以构造出一些极端情况,在拿到一些 client 之后,让某台 backend 的连接数变为 0。

现在大规模的服务节点很多,有些批量发布一次性发布几百个节点,Google 的这个算法说一般 100 条连接(We typically use a subset size of 20 to 100 backend tasks)就够了?如果正好批量发布的后端都被同一个 client 选中了,那这个 client 就废掉了。

client 服务是需要知道 backends 的 id 的,否则当 backend 发生下线时,会导致 client 端的连接重新排布。

因此想要应用该算法,我们还是要进行一些特殊情况的考量和处理。

参考资料:

[1] https://sre.google/sre-book/load-balancing-datacenter/

本文分享自微信公众号 - 码农桃花源(CoderPark),作者:曹春晖

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-11-29

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 千难万险 —— goroutine 从生到死(六)

    上一讲说到调度器将 main goroutine 推上舞台,为它铺好了道路,开始执行 runtime.main 函数。这一讲,我们探索 main gorouti...

    梦醒人间
  • 【原创】“三次握手,四次挥手”你真的懂吗?

    记得刚毕业找工作面试的时候,经常会被问到:你知道“3次握手,4次挥手”吗?这时候我会“胸有成竹”地“背诵”前期准备好的“答案”,第一次怎么怎么,第二次……答完就...

    梦醒人间
  • 新官上任 —— Go sheduler 开始调度循环(五)

    上一讲新创建了一个 goroutine,设置好了 sched 成员的 sp 和 pc 字段,并且将其添加到了 p0 的本地可运行队列,坐等调度器的调度。

    梦醒人间
  • 使用 Python 操作 HDFS

    一份执着✘
  • 粘包问题的解决,上传与下载,多用户聊天

    2.并可以返回终端的执行结果 subprocess.Popen(1,2,3,4) 1:cmd命令 2:shell = True 3:返回正确结果参数 ...

    GH
  • 深度学习入门篇--手把手教你用 TensorFlow 训练模型

    最近笔者终于跑通 TensorFlow Object Detection API的ssd_mobilenet_v1 模型,这里记录下如何完整跑通数据准备到模型使...

    付越
  • 【nowcoder-2017校招真题】保留最大的数

    给定一个十进制的正整数number,选择从里面去掉一部分数字,希望保留下来的数字组成的正整数最大。

    饶文津
  • 使用Bluemix,NoSQL DB和Watson创建云应用程序

    大家好,自从我上次写文章已经很久了。事实上,这几年,我总是忙于工作。我现在是IBM的Bluemix平台的云架构师。我在Tomcat服务器上用Web应用程序编写了...

    老人雨何
  • 一个ReadinessGates Controller Demo

    kubernetes从1.11版本开始引入了Pod Ready++特性对Readiness探测机制进行扩展,在1.14版本时达到GA稳定版本,称其为Pod Re...

    有点技术
  • linux的ssh服务升级后无法启动修复

    *** Missing privilege separation directory: /var/run/sshd***

    小沨

扫码关注云+社区

领取腾讯云代金券