内网使用服务发现后,服务与其它服务的实例之间使用一条 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 / subsetCountr := 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 % subsetCountstart := subsetID * subsetSizereturn 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/

用 subsetting 限制连接池中的连接数量相关推荐

  1. 聊聊 SQLAlchemy 连接池中的连接失效问题

    最近项目中事情比较多,也遇到了一些问题,其中有一个是关于连接池的,比较有意思,这里分享下. 一天早上,进入业务系统,点击了一个功能按钮,页面上突然弹出个 MySQL gone away 的错误,我擦, ...

  2. Redis进阶-JedisCluster初始化 自动管理连接池中的连接 _ 源码分析

    文章目录 Pre Code 初始化 槽计算 无需手工调用close方法 Pre Redis进阶-Redis集群原理剖析及gossip协议初探 集群原理部分 简单的提了下Jest是如何实现Redis C ...

  3. 但是尚未从池中获取连接_SQLServer超时时间已到,但是尚未从池中获取连接

    小编最近开发了一个项目,数据库是SQLServer2008R2,在WinForm程序通过API接口短时间大批量上传数据时,出现了错误"超时时间已到,但是尚未从池中获取连接",数据是 ...

  4. [bug]超时时间已到。超时时间已到,但是尚未从池中获取连接。出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小。...

    引言 自己弄了一个小项目--日程管理系统,在初始化日期时,查询了数据库,每个日期就会查询一次数据库,就导致了这个问题. 问题 出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小. Desc ...

  5. 浅谈各种连接池中连接数量的设置

    连接池中连接数量的配置 我们日常开发中经常会用到各种连接池,比如httpclient和jediscluster以及druid等数据库连接池,当使用这些连接池的时候我们总是很疑惑到底要怎么配置连接池中连 ...

  6. 【连接池】Tomcat 连接池中 maxActive,maxWait,maxAge,testOnBorrow,testWhileIdle等选项的作用

    前言 连接池本质作用是为客户端提供连接复用,提升连接效率,降低系统开销.Tomcat的连接池提供了maxActive,maxWait,maxIdle,minIdle,initialSize等参数,配置 ...

  7. 连接池中的最大连接数和最小连接数

    maximum-connection-count 最大连接数, minimun-connection-count 最小连接数 proxool 是一个连接池,池中放了多个连接对象,这两个值设置连接对象数 ...

  8. 连接池中 maxActive,maxWait,maxAge,maxIdel参数

    前言 连接池本质作用是为客户端提供连接复用,提升连接效率,降低系统开销.Tomcat的连接池提供了maxActive,maxWait,maxIdle,minIdle,initialSize等参数,配置 ...

  9. 但是尚未从池中获取连接_解决报错“超时时间已到。超时时间已到,但是尚未从池中获取连接”的方案...

    超时时间已到.超时时间已到,但是尚未从池中获取连接.出现这种情况可能是因为所有池连接均在使用,并且达到了最大池大小. DataReader是独占连接的,就是说你的程序可能设计上有问题.比如说最大连接设 ...

最新文章

  1. 浏览器常见bug及解决办法
  2. 带权并查集--hdu3047 ZJnu stadium
  3. 前端lvs访问多台nginx代理服务时出现404错误的处理
  4. 【CF1189D】Add on a Tree【结论】【构造】
  5. 2017级软件2班安卓应用开发课程主页
  6. 学习ASP.NET之前,先了解它
  7. Maven无法下载远程依赖-强制下载也不行
  8. 并发 不同的隔离等级存在的问题
  9. hdu 1427 24点暴力dfs
  10. Excel表格批量生成Word文档
  11. 一键生成自签名证书,为内网IP配置HTTPS访问来使用navigator.getUserMedia录音
  12. 非线性曲线拟合和多项式曲线拟合
  13. 【Unity3D开发小游戏】《超级马里奥》游戏教程
  14. FMI飞马网 | AI人工智能:54份行业重磅报告汇总(附下载)
  15. 对于圆桌理论和经典概率判断算法的分析(转)
  16. 红蓝军模拟对抗三维电子沙盘开发教程第十课 wpf建立3D GIS数字地球
  17. 计算机设计大赛作品抄袭会怎么样,如何界定借鉴和抄袭?看这7位设计大咖怎么说!...
  18. 嵌入式开发板如何自动登陆校园网实现上网
  19. Ubuntu下,用键盘定义鼠标按键
  20. ipa在线安装搭建_iOS12.4.1 越狱无法安装?教你百分百安装

热门文章

  1. rlm sql mysql.so_UBUUTU7.10上安装配置freeradius+mysql+rp-pppoe手记
  2. Java开源诊断工具 Arthas 发布v3.1.0
  3. 导入要素集到SDE数据库的方法以及使用GP工具的许可问题(转载)
  4. SQL Server 2012笔记分享-6:理解内存管理
  5. 最近任务-2012.05.14
  6. [转贴]制作windows 2003自动安装盘-集成补丁/Raid及硬件驱动
  7. 牛客 - Dance with a stick(大风车模型)
  8. 中石油训练赛 - Gone Fishing(固定大小的圆可以覆盖最多的点)
  9. ZOJ - 2676 Network Wars(01分数规划+最小割)
  10. POJ - 3261 Milk Patterns(二分+后缀数组)