gitbook/系统性能调优必知必会/docs/259594.md
2022-09-03 22:05:03 +08:00

93 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 26 | 应用层多播:如何快速地分发内容?
你好,我是陶辉。
[\[第7讲\]](https://time.geekbang.org/column/article/235302) 我们曾介绍了网络层的IP协议是如何支持多播的这节课我们再来从应用层看看如何实现多播功能。
当你的分布式集群只有十多个节点时每次发布版本时尽可以从发布服务器将新版本的安装包通过ftp、scp、wget等工具分发到各个节点中。可是一旦集群规模达到成千上万个节点时再这么做就会带来很大的问题文件分发的时长高达几个小时甚至会打挂文件源终止分发流程。在微服务环境中这点尤为明显 毕竟每个Docker镜象的体积动辄就在数百兆字节以上。
虽然网络层的IP协议允许通过路由器、交换机实现高效的多播但IP层很难实现文件的可靠传输而且跨越多个局域网时路由器等网络设备对IP多播的支持也不好。此时通过应用层的接力传播就能通过多播思想大幅提升系统的传输效率解决上述问题。
除了分发文件场景外应用层多播协议也常用于完全去中心化的分布式系统特别是在管理成千上万个节点的状态时非常有用。比如Gossip就是这样一个多播协议[\[第22讲\]](https://time.geekbang.org/column/article/254600) 介绍过的Cassandra数据库使用它来同步节点间的状态比特币通过它传输账本数据Redis集群也使用它同步Redis进程间的状态。
那么这节课我们就重点介绍应用层中的多播协议并以阿里的蜻蜓、Cassandra中的Gossip协议为例看看它们的工作原理。
## 认识应用层的多播协议
之所以需要应用层的多播协议是因为网络层的IP多播功能参见[\[第7讲\]](https://time.geekbang.org/column/article/235302) 的IP广播与组播有以下4个方面的问题
* 从功能上看IP多播缺失了质量控制、可靠性传输等特性无法满足绝大部分场景中的要求
* 从管理上看,监控跨网络的多播报文并不容易,多播地址的管理也很复杂,而且多播很容易造成网络洪峰及安全问题;
* 从市场上看,单播报文在终端上的计费很成熟,相反,运营商对多播报文的计费要困难许多,相对缺乏推进动力;
* 从产业协作上看IP多播必须由各设备厂商的路由器、交换机配合由于网络层是由内核实现的所以还要同步升级操作系统。
这些因素都使得网络层的多播功能难以推广因此各类基于IP单播功能的应用层多播协议应运而生。可能有些同学对于单播、多播这些网络知识还不熟悉下图我将传统单播、网络层多播、应用层多播放在一起对比着看你可以很容易看到它们的区别。
这里仍然以版本发布场景为例主机1是发布节点它需要将文件分发到其余3台主机上其中传统单播协议的玩法是在主机2、3、4上分别向主机1发起下载文件命令因此主机1上有3条TCP下载链路图中我用3种不同的颜色表示。很明显此时主机1的下行流量一定会成为瓶颈而且随着集群规模的扩大网络链路中的路由器也会很快达到流量瓶颈。
![](https://static001.geekbang.org/resource/image/34/7a/3457fac0f6811d21357188c48fe5867a.png)
再来看效率最高的网络层多播主机1可以仅通过1次报文传输将文件分发到所有主机上。在整个流程中仅由路由器将网络报文扩散到相邻的主机、路由器上没有任何多余的动作。如同上述所说网络层多播的使用有很多困难这里列出它是为了方便与应用层多播作对比。
在上图的应用层多播中文件传输分为两个阶段。首先主机2、3直接从主机1中下载文件参见绿色与红色线。其次当主机3完成下载后主机4再从主机3上下载文件参见蓝色线。可见相对于IP多播**应用层多播有以下3个缺点**
首先网络效率下降了不少这由2个原因所致
* 数据在网络中有冗余比如主机1将数据重复发送了2次参见红色、绿色线主机3既接收了一次数据也发送了一次数据
* 虽然图中主机4从同一局域网中的主机3下载数据但在复杂的互联网环境中应用层很难掌握完整的路由器组网信息主机4一旦跨网络从主机2上下载数据这就增加了路由器A及网络链路的负载效率进一步下降。
其次由于传输路径变长了文件传输的总完成时间也变长了。比如主机4的总传输路径是主机1 -> 路由器A -> 路由器B -> 主机3 -> 路由器B -> 主机4既多出了主机3、路由器B这两个传输环节而且进入主机3后协议栈的处理深度也增加了。
最后单次传输路径中引入了功能复杂的主机相比仅由网络设备参与的IP多播可靠性、稳定性也降低了。
说完缺点,我们再来看应用层多播的优点。
* 首先它回避了IP多播的问题无须改变现有组网环境也不需要管理组播IP地址立刻就可以应用在当下的生产环境中
* 其次,在数以万计的大规模集群下,单一发布源很容易被流量打爆,进而导致分发流程停止,应用层多播可以避免这一问题;
* 再次,通过应用层节点的接力分发,整个传输带宽被大幅度提高了,分发速度有了数量级上的飞跃;
* 最后如果分发集群跨越不同传输成本的网络比如多个区域IDC构成的集群在应用层也很容易控制分发策略进而减少高成本网络的数据传输量提升经济性。
所以,综合来说,**集群规模越大,应用层多播的优势也越大。**实际上十多年前我们在使用BT、迅雷下载时就已经接触到应用层多播协议了接下来我们结合2个服务器端的案例看看多播协议的实现与应用。
## 应用层多播协议是如何工作的?
其实,应用层多播主要是指一种[P2PPeer to Peer](https://zh.wikipedia.org/wiki/%E5%B0%8D%E7%AD%89%E7%B6%B2%E8%B7%AF)网络传输思想任何基于IP单播的通讯协议都可以拿来使用。比如针对于在分布式集群中分发软件安装包的场景完全可以使用HTTP协议实现应用层的分发阿里巴巴开源的[Dragonfly蜻蜓](https://github.com/DarLiner/Dragonfly)就是这么做的。
蜻蜓拥有1个中心化的集群服务节点SuperNode其中既可以直接保存着源文件也可以通过HTTP缓存加速第三方文件源比如Docker仓库。集群中的每个节点都要启动dfget进程替代了传统的wget它就像我们平时使用的迅雷在下载文件的同时也会将自己下载完成的文件传输给其他节点。其中通过HTTP的Range文件分段下载规范dfget就可以实现断点续传、多线程下载等功能如下图所示
[![](https://static001.geekbang.org/resource/image/2f/00/2fbba7f194bd3bcabded582467056700.png "图片及下图来源https://github.com/DarLiner/Dragonfly/blob/master/docs/zh/architecture.md")](https://github.com/DarLiner/Dragonfly/blob/master/docs/zh/architecture.md)
各个dfget程序间通过SuperNode协调传输关系这样绝大部分dfget程序并不需要从SuperNode节点下载文件。比如上图中的节点C通过HTTP Range协议从节点A中下载了文件的第1块同时并发地从节点B中下载了文件的第2块最后再把这些Block块拼接为完整的文件。
这里你可能会想这不就是一个P2P下载工具么是的但你站在集群运维的角度这就是基于应用层多播协议的文件分发工具。当每个节点部署dfget服务后新版本安装包发布时就可以由SuperNode节点推送经由各个dfget进程以多播的形式分发下去此时性能会获得大幅度的提升。
![](https://static001.geekbang.org/resource/image/36/82/36de22b49038a6db2b8bc7ce953e5c82.png)
上图是传统的wget单播与蜻蜓多播分发文件的性能对比图。我们可以看到传统方式下**分发客户端越多Y轴总分发时长X轴就越大特别是1200个以上的并发节点下载文件时会直接将文件源打爆。**而采用应用层多播方式后,下载时长要低得多,而且伴随着节点数的增加,下载时长也不会增长。
蜻蜓虽然传输效率很高但SuperNode却是保存着全局信息的中心节点一旦宕机就会造成系统不可用。我们再来看去中心化的[Gossip流言协议](https://en.wikipedia.org/wiki/Gossip_protocol)是如何实现应用层多播的。
Gossip协议也叫epidemic传染病协议工作原理如下图所示。在这个分布式网络中并没有任何中心化节点但只要第1个种子节点被感染为红色后每个节点只需要感染其相邻的、有限的几个节点最终就能快速感染网络中的所有节点即仅保证最终一致性
![](https://static001.geekbang.org/resource/image/fa/b5/fa710dc6de9aa9238fee647ffdb69eb5.gif)
当然所谓的“感染”就是数据的传输这一算法由1987年发布的《[Epidemic algorithms for replicated database maintenance](http://bitsavers.trailing-edge.com/pdf/xerox/parc/techReports/CSL-89-1_Epidemic_Algorithms_for_Replicated_Database_Maintenance.pdf)》论文提出同时证明了算法的收敛概率。Cassandra数据库、Fabric区块链、Consul系统等许多去中心化的分布式系统都使用Gossip协议管理集群中的节点状态。以Cassandra为例每秒钟每个节点都会随机选择1到3个相邻节点通过默认的7000端口传输包含节点状态的心跳信息这样集群就可以快速发现宕机或者新增的节点。
[![](https://static001.geekbang.org/resource/image/79/fe/7918df00c24e6e78122d5a70bd6bd2fe.png "图片来源https://www.linkedin.com/pulse/gossip-protocol-inside-apache-cassandra-soham-saha")](https://www.linkedin.com/pulse/gossip-protocol-inside-apache-cassandra-soham-saha)
## 小结
这一讲我们介绍了应用层的多播协议。
网络层的IP多播功能有限对网络环境也有过多的要求所以很难通过多播协议提升传输效率。基于IP单播协议如TCP或者UDP在应用代码层面实现分布式节点间的接力转发就可以实现应用层的多播功能。
在分布式集群的文件分发场景中,阿里开源的[Dragonfly蜻蜓](https://github.com/DarLiner/Dragonfly)可以将发布节点上的源文件通过HTTP协议推送到集群中的每个节点上其中每个节点在应用层都参与了多播流量分发的实现。当节点数到达千、万级时蜻蜓仍然能保持较低的分发时延避免发布节点被下行流量打爆。
在完全去中心化的分布式集群中每个节点都没有准确的全局信息此时可以使用Gossip流言协议通过仅向有限的相邻节点发送消息完成整个集群的数据同步实现最终一致性。因此Gossip协议常用于大规模分布集群中的节点状态同步。
## 思考题
最后留给你一道讨论题。在5G完成设备层的组网后类似华为NewIP这样的基础协议层也在做相应的重构其中[Multicast VPN协议](https://support.huawei.com/enterprise/en/doc/EDOC1000173015/e0de8568/overview-of-rosen-mvpn)就将现有IPv4无法在公网中推广的多播功能在VPN逻辑链路层实现了。你对未来多播协议的发展又是如何看的欢迎你在留言区与大家一起探讨。
感谢阅读,如果你觉得这节课让你了解到应用层的多播协议,而通过它可以大幅度提升分布式集群的网络传输效率的话,也欢迎你把今天的内容分享给你的朋友。