六合彩最高极限

B站在微服务治理中的探索与实践

B站在微服务治理中的探索与实践

作者 | 曹国梁

编辑 | 田晓旭

本文整理自曹国梁在趣头条技术沙龙上发表的演讲《B 站在微服务治理中的探索与实践》。

大家都知道微服务有两个痛点,一个是如何拆分微服务,微服务的边界怎么划分制定;二是微服务上了规模之后如何管理,因为只要上了规模,任何小小的问题都可能会被放大,最后导致雪崩效应。

一.微服务化带来的挑战

B站在微服务治理中的探索与实践

上图是我们 B 站全链路追踪的一个截图,这只是其中一个拓扑图的调用链路,就已经非常复杂了。可以想象一下,如果是整个公司所有的调用链路,会有多么复杂。而这就带来了微服务治理的复杂性问题:如何保证注册和发现;如何保证多机房高可用;如何保证低延迟等等。

其次,微服务化以后,服务拆分的比较多,调用链也比较长,调用链很容?#36164;?#21040;一个?#21040;?#28857;的影响,导致用户端出现超时的现象。另外,负载不均衡会导致热点问题,并影响?#35797;?#35843;度;单个节点不可用,如果限流或者熔断手段做的?#32531;?#21487;能有雪崩效应;微服务代理的分布式事务问题和分布式一致性问题,以及编排、日志、链路追踪等问题。

二.Go 语言在 B 站开源服务发现框架 Discovery 的实践历程

B站在微服务治理中的探索与实践

2015 年到 2017 年,B 站的微服务也是基于 Zookeeper,Zookeeper 是一个 CP ?#20302;常?#21487;以保证一致性,在网络分区的情况下保证可用性。但是我们 CP ?#20302;?#26377;一个问题,就是难以支持跨机房。如果机房 1 和机房 2 由于某些不稳定的原因发生网络断开,provider B 去往 ZK Follower 的注册是无法实现的。因为 ZK Follower 所有的请求是强一致,都有同步到 ZK Leader,这时机房 2 就无法注册了,但其实 Consumer B 和 Provider B 之间的网络是正常的。

Zookeeper 有一个性能瓶?#20445;?#22240;为强一致?#20302;?#19968;般都会缓存全量日志,而 ZK Leader 是单节点的,所有的写请求都会到 ZK Leader 上,因此,写是无法水平扩展的。另外,基于 TCP 的健?#23548;?#26597;也不是最优的。

B站在微服务治理中的探索与实践

2018 年,我们开始自研了服务发现框架,目?#26696;?#26694;架已经在 B 站大规模使用了。这是一个 AP 的?#20302;常?span class='wp_keywordlink_affiliate'>Service Provider 注册以后,所有的注册、健?#23548;?#27979;、取消注册都会通过 Discovery Server 异步同步到其它 Discovery Server,?#32531;?#26469;保证最终一致。

Discovery Server 一定要满足网络分区时的自我保护,保证健康的服务节点可用。

客户端与 Discovery Server 是通过 HTTP Long Polling 来连接的。这种方式开发比较简单,且拥有?#35780;?#32467;合的?#20040;Γ?#26082;能及时感知到节点变更,又方便并发编程的维护。

上图中?#36335;?#30340;表格是与开源 Eureka 的对比图,基本上 Eureka 可以做到的,Discovery 也可以做到,Eureka 不能做到的,Discovery 还可以做到。(具体可参考表格)

B站在微服务治理中的探索与实践

接下来介绍一下机房的流量调度。

右下角的运维小人感知到机房 A 有问题,可以?#36335;?#19968;个指令,指令可通过 Discovery 节点在机房 B 扩散,扩散完之后,会在机房 A 随机挑一个节点扩散,最后把调度信息发给 consumer,consumer 自动把大多数流量切换到 B。

B站在微服务治理中的探索与实践

如何保证最终一致?

每一个服务提供者实例都是全球唯一的,可以通过服务 ID+HostName 全球定位到服务实例,所以只要保证每个服务提供者实例达成一致,那么服务发现就大功告成了。服务提供者实例只要维持一个单调递增的 dirtyTime,发给 Discovery 节点之后,Discovery Server 收到注册请求或者其它请求,都会把这些请求广播一遍,在广播的时候就可以检查数据的一致性。

B站在微服务治理中的探索与实践

Discovery 另外一个比?#29616;?#35201;的问题就是容灾。当发生网络分区和网络抖动的时候,因为每一个 Discovery 之间会同步复制心跳信息,所以短时间会丢失大量的心跳。例如,每分?#26377;?#36339;小于阈值,Discovery 就会感知到,这时就不会剔除一些本该剔除的指令。即使没有进入非自我保护模式,Discovery 也会随机逐步剔除,避免一下子剔除导致全部过期。

当只有部分 Discovery 节点不可用?#20445;?#22240;为每一个节点都是有数据的,所以此时只要选择连接其他正常的 Discovery 节点获取数据就可以了,并?#20063;?#21487;用的节点重启之后,会自动拉取正常的节点,保持最新的同步。

如果全部的节点都不可用?#20445;?#23458;户端 SDK 会缓存数据,并拒绝任何实例数过低的异常变更?#25169;停?#22312;宕机期间,服务提供者会一直向 Discovery 节点发送心跳请求,直到 Disocvery 节点重启恢复正常之后会返回 404,此时服务提供者通过调用 Register 接口重新注册。

B站在微服务治理中的探索与实践

Discovery 框架客户端基本是零配置的,客户端 SDK 通过请求 SLB 拿到所有的 Discovery 服务端节点,并随机挑选一个节点作为拉取数据的节点。其次,我们在代码中做了动态注册,也就说每个 client.Dial 都会生成一个 connection,每个 connection 都会消费一个服务,每个服务都?#26434;?#19968;个全?#27835;?#19968;的 appID,代码中通过写死 appID 来获取节点信息并连接。这种 appID 的方式能够做到动态订阅、动态销毁,实现零配置。

B站在微服务治理中的探索与实践

零配置的一个特点是在客户端 SDK 中的都是动态生成的,即所有的订阅、拉取?#23478;?#22312;客户端中动态生态。这?#20445;?#25105;们就需要创建一个全?#27835;?#19968;的 Builder。Builder Interface 实现了两个方法,一个是 Build,另一个是 Scheme。Build 方法会接受?#38382;?/a>——appID,?#32531;?#36820;回 Resolver,Resolver 会调用 watch。当有全局事件变更?#20445;?#37117;会?#25169;?#32473; Builder,Resolver 从 MailBox 中获取?#36739;?#20851;信息,通过 fetch 实现动态通知和实时?#25169;汀?/p>

这些都得益于我们的 Golang CSP 并发模型,Discovery 基本都是通过这种方式通信,并用这个方法解决并发编程的问题。和大家分享一下 Discovery 中的 Go 语言最佳实践。

B站在微服务治理中的探索与实践

首先是 errgroup 的使用,当我们启动了多个 groupteam,其中某个 groupteam 失败了,那就认为这次并发请求失败了。但是使用 errgroup 之后,当某个 groupteam 失败了之后,return error 后会生成一个新的 context,这样就可以通过散播 error 的方式来避免?#35797;?#28010;?#36873;?/p>

B站在微服务治理中的探索与实践

其次是分布式客户端出错重试时尽量使用 BackoffRetry。假设此时有 100 个客户端,当搜索端炸了或者 CPU 满了,如果客户端同时一起重试会?#20204;?#20917;变得很糟,大家都会竞争,排队会越来越?#29616;亍?#32780;使用 BackoffRetry,相当于加了一些随机量,出错之后随机 Sleep,并?#20197;?#21152;一个避退的规则,例如这次是 1 毫秒,下次是 2 毫秒。这样,可以尽可能的保证重试的成功率。

三、RPC 负载均衡算法的演进之路

服务发现是个 AP ?#20302;常?#21487;能会出现延迟的情况,你拉取到的节点可能是一个错误节点,所以我们需要负载均衡来快速剔除它。另外,当出现某个节点 CPU 比较高或者网络抖动的情况,也是需要用到负载均衡。

B站在微服务治理中的探索与实践

这是我们负载均衡算法的 1.0 版本,比较常见的 Weighted Round Robin。从上图中可以看到,NodeA 权重:NodeB 权重:NodeC 权重 =3:2:1,也就是说 NodeA 会被调用 3 次,NodeB 会被调用 2 次,NodeC 会被调用 1 次,通过这种方式来做到负载的散布。但是这个版本也存在一些问题,一是无法快速摘除有问题的节点,二是无法均衡后端负载,三是无法降低总体延迟。

B站在微服务治理中的探索与实践

针对以上问题,我们进行了改进——动态感知的 WRR 算法,利用每次 RPC 请求返回的 Response 夹带 CPU 使用率,尽可能感知到服务负载,并且每隔一?#38382;?#38388;整体调整一次节点的权重分数。

B站在微服务治理中的探索与实践

但是这个版本也存在一个问题。有一天,我们发现服务一直在报警,日志一直在报 504 错误(即超时重试),但是在监控时并没有发?#27835;?#39064;,CPU 使用率基本都是 90% 左右。在 CPU 没有满的情况下,理论上来讲只可能出现一两个超?#20445;?#19981;可能出现大量的超?#20445;?#26368;后通过查看 WRR 日志,发现其实是信息滞后和分布式带来的羊群效应。

从图上可以看到当土拨鼠收到了金矿信息,它们就会蜂拥而至,跑在前面的可以抢到了金矿,但是跑在后面的可能抢不到,因为信息肯定是延迟的。另外,这些土拨鼠都是一个个独立的个体,它不是市场经济,市场经济即使信息有延迟,但是也可以通过规划、调度来分配?#35797;础?/p>

导致出现上文诡异情况的原因,就是负载均衡 2.0 版本会自动刷新权重值,但是在刷新时无法做到完全的实?#20445;?#20877;快也不可能超过一个 RTT,都会存在一些信息延迟差。当后台?#35797;?#27604;较稀缺?#20445;?#36935;到网络抖动?#20445;?#23601;可能会把该节点炸掉,但是在监控上面是感觉不到的,因为 CPU 已经被平均掉了。

B站在微服务治理中的探索与实践

发现这个问题之后,我们就引入了负载均衡 3.0。

  1. 尽可能获得最新的信息: 使用带时间衰减的 Exponentially Weighted Moving Average(带系数的滑动平均值)实时更新延迟、成功?#23454;?#20449;息。

  2. 引入 best of two random choices 算法,加入一些随机性。上图中,横轴是信息延迟的时间,纵轴是平均请求响应时间。当横坐标接近 0 ?#20445;琤est 算法和负载均衡 2.0 差不多,但是当横坐标接近 40、50 ?#20445;?#36825;个差距就很明显了。

  3. 引入 infliht 作为参考,平衡?#21040;?#28857;流量,inflight 越高被调度到的机会越少。

B站在微服务治理中的探索与实践

?#25169;?#26435;重分数,每次请求来时我们都会更新延迟,并且把之前获得的时间延迟进行权重的衰减,新获得的时间提高权重,这样就实现了滚动更新。

B站在微服务治理中的探索与实践

上图就是 best of two 算法,每次从所有节点中随机 rand 一个节点 A 和 B,之后再经过了比较分数的算法,代码中的权重值指的是 Discovery 中设置的权重值。

B站在微服务治理中的探索与实践

如何测试 RPC 负载均衡?这个测试比?#29616;?#35201;,上线的时候稍不注意就可能导致雪崩,所以需要谨慎一些,除了基本的单元测试外,测试代码还会模拟多客户端、多服务端场景,并随机加入网络抖动、长尾请求、服务器负载突变、请求失败等等真实场景中可能出现的情况,并在最后打印出结果来判断新的功能是否有效果。

另外,我们也会在线上的 Debug 日志中加一些?#27835;觶?#20363;如当前的分数成功?#23454;?#31561;。

B站在微服务治理中的探索与实践

上图是这是我们上线以后 CPU 收敛的效果。

四、限流 & 熔断

微服务中的负载均衡解决的是技术?#21040;?#28857;的问题,而限流和熔?#29616;?#35201;是防止?#20302;?#36807;载,防止?#20302;?#38634;崩。

B站在微服务治理中的探索与实践

这是 B 站一开始的熔断算法,是参考 Hystrix 熔断算法,当请求失败比率达到一定阈值之后,熔断器开启,并休眠一?#38382;?#38388;,这段休眠期过后,熔断器将处于半开状态,在此状态下将试探性的放过一部分流量,如果这部分流量调用成功后,再次将熔断器闭合,否则熔断器继续保持开启并进入下一?#20013;?#30496;周期。

但这个熔断算法有一个问题,过于一刀切,会把所有的?#20302;?#19968;下子全部关掉,本来当时?#20302;?#36824;可以通过 30% 或 20% 的流量,但是现在所有流量都不能通过。在半开状态下,试探性放入的流量必须全部成功,但是此时?#20302;?#24050;经过载了,想要成功很难。因为这些问题,后来我们采用了 Google SRE 弹性熔断算法,弹性熔断是根据成功?#24335;?#34892;调整的,当成功?#35797;?#39640;的时候,被熔断的概率就越小,反之亦然。同?#20445;问?#26159;可以自定义的,通过调整?#38382;?#21487;以使得熔断算法更加激进或者更加温和。

B站在微服务治理中的探索与实践

单机令牌桶限流是我们一开始就在使用的限流算法,就是到了现在,还有 50% 的服务是在使用这个算法。令牌桶一开始会装一些 token,每隔?#35813;?#20196;牌桶中会收到新的 token,?#23849;?#25130;器从令牌桶中拿 token 的时候,如果可以拿到就接着放行,如果拿不到就丢弃掉。

这个算法的问题是只针对局部服务端的限流,无法?#29942;?#20840;局?#35797;矗?#32780;且令牌桶的容量以及放 token 的速率无法很好的评估,因为?#20302;?#36127;载一直在变化,如果?#20302;?#22240;为某些原因进行了缩容和扩容,还需要人为手动去修?#27169;?#36816;维成本比较大。另外,令牌桶是没有优先级的,所以无法让重要的请求先通过。

B站在微服务治理中的探索与实践

这是我们基于 BBR 算法开发的一个自适应限流,BBR 算法就是一个 TCP 的?#31561;?#25511;制,与微服务中的限流也有一定的相似之处。自适应限流,基于 CPU/IOPS 作为启发值,通过 BBR 算法来决定?#20302;?#30340;最大?#24615;?#37327;,适应零配置限流算法:cpu > 800 AND InFlight > (maxPass x minRtt x windows / 1000) 。

为什么要用 CPU/IOPS 作为启发?#30340;兀?#22240;为自适应限流与 TCP ?#31561;?#25511;制还存在不同之处,TCP 中客户端可以控制发送率,从而探测到 maxPass,但是 RPC 线上无法控制流量的速率,所以必须以 CPU 作为标准,当 CPU 快满载的时候再开启,这时我们认为之前探测到的 maxPass 已经接近了?#20302;?#30340;瓶?#20445;?#20056;以 minRtt 就可以得到 InFlight。

B站在微服务治理中的探索与实践

除了自适应限流,我们还做了 Codel 队列,传统的队列都是先进先出,但是我们发现微服务可能不太适合这种做法,这是因为微服务会有超?#20445;?#32943;定不可能无限期的等下去,可能你的 SLP 已经设置了 800 毫秒的超?#20445;?#22914;果这时放行的是一个老的请求,?#20204;?#27714;的成功率就会变低,因为它可能已经排队了好长时间。

所以这时我们需要一个基于处理时间丢弃的队列,当?#20302;?#22788;于高负载的时候,实行后进先出的策略,也就是说要主动丢弃排队久的请求,并让新的请求直接通过,利用这个队列来弥补之前算法中的缓冲问题,吸收突增的流量。

B站在微服务治理中的探索与实践

这是自适应无限流的效果,蓝色是请求进来的 QPS 量,绿色是真正通过的 QPS 量,从图中可以看到,当 CPU 达到百分百?#20445;?#35831;求通过已经雪崩了。

B站在微服务治理中的探索与实践

这是自适应有限流的效果,可以看到即使?#26029;?#19968;直在增,但绿线通过的量也没有受?#25509;?#21709;,还是保持着一个比较平稳的通过率,可能因为拒绝请求的成本导致绿线稍微有些偏低,但整体影响不大。

五、 回顾与展望

回顾一下前?#27169;珿o 语言天然支持并发编程,CSP 模型满足大部分的并发场景,Discovery 就是大量应用了这种思想?#36824;?#24443;组件化思想,Go 的接口设计刚好够用;Go 语?的程序开发需要在代码可读性与性能之间做好平衡取舍,应?程序并发模型要在控制之内。?#26434;?#26410;来的规划,我们主要有 5 个小方向:

  • Discovery 多机房自动化流量调度(全局视角)

  • Discovery 实现 Merkle Tree 结构 & 支持 Gossip 协议

  • RPC 负载均衡冷启动预热

  • 具有全局视角的分布式限流方案

  • RPC 请求优先?#25238;?#21015;

作者介绍

曹国梁 ,bilibili 主站技术中心高级研发工程师

字节跳动私有?#25945;?TCE 托管头条、抖音、字节国际化业务等内部上万个在线微服务,机器负载愈高,运维?#35759;?#36234;大。为提升?#35797;?#21033;用率,通过动态超售,实现了业务实例的高密度部署,并通过优化 Kubernetes ?#35797;?#27169;型,有效保证了延时敏感服务的 QoS。具体在容器化场景下的一些运维痛点解决方法,可到 7 月 12 日深圳 ArchSummit 架构师峰会上一探究竟。扫码或点击阅读原文看详情。

全价期,报名联系票务?#19968;?17326843116

B站在微服务治理中的探索与实践

原文 

http://mp.weixin.qq.com/s?__biz=MzIzNjUxMzk2NQ==&mid=2247491435&idx=1&sn=537d9119157308ac3fc53b3b273f121f

本站部?#27835;恼?#28304;于互联网,本着传播知识、有益学习和研究的目的进行的转载,为网友免费提供。如有著作权人或出版方提出异议,本站将立即删除。如果您对?#24700;?#36716;载有任?#25105;晌是?#21578;之我们,以便我们及时纠正。

PS?#21644;?#33616;一个微信公众号: askHarries 或者qq?#28023;?74807195,里面会分享一些资深架构师录制的视频录像:有Spring,MyBatis,Netty源码?#27835;觶?#39640;并发、高性能、分布式、微服务架构的原理,JVM性能优化这些成为架构师必备的知识体系。还能领取免费的学习?#35797;矗?#30446;前受益良多

转载请注明原文出处:Harries Blog? » B站在微服务治理中的探索与实践

赞 (0)
分享到:更多 ()

评论 0

  • 邮箱 (必填)
  • 网址
六合彩最高极限