万字干货:从消息流平台 Serverless 之路,看 Serverless 标准演进
摘要:如今,Serverless 化已经成为消息流平台发展的新趋势,而如何更好地基于 Serverless 化的消息流平台进行应用设计和开发,则成为了一个值得思考的问题。
本文分享自华为云社区《9000字干货:从消息流平台Serverless之路,看Serverless标准演进》,作者:华为云 PaaS 服务小智。
这是一个最美好的时代。
随着以数字化升级为代表的第四次工业革命浪潮的席卷,企业正在不断地深化运用这一技术,构建一个又一个全连接,全感知,全场景,全智能的数字世界,进而优化再造物理世界的业务,对传统业务模式,经营模式,管理模式以及商业模式进行创新和重塑,带来新一轮可观的业务增长。
而这背后,云服务已经成为企业数字化升级的必然选择。
Gartner 预测,2025 年将有近三分之二的应用软件支出转向云技术,到 2026 年全球云计算市场将突破万亿美元。
去年 10 月,随着马斯克旗下的 X 工程技术团队的重磅发帖,下云,瞬间成为了一个热议话题。抛开背后的政治、商业合作、经济下行等因素不谈,从工程技术以及资产运营角度来看,早些年,一部分借助低固定成本的公有云率先成长起来的企业,出于进一步安全可靠、低边际成本、灵活定制等新一轮发展需求,开始将自己的业务有选择的下云。当然,下云依然有不小代价,还要考虑自建 IDC 机房的运营成本(不仅仅是建设成本)。
事实上,现代云平台和自建 IDC,对于资源的利用率都没有达到尽善尽美的程度,资源浪费的情况始终存在,这主要受限于传统的软件技术架构,并没有很好地适应云化场景。云厂商也在不断积极探索与创新,通过技术变革不断优化结构,提供更灵活的付费模式。
另一方面,一些云厂商通过 FinOps 服务(一种通过工程、财务、产品等方面的跨职能团队的协作,实现更快的产品交付、获得更多的财务控制和可预测性的实践活动)帮助企业合理使用云。由此可见,从“上好云”到“用好云”,是所有企业新一轮数字化飞跃的关键机遇所在。
为了帮助企业更有效地用好云,云厂商们不约而同地转向 Serverless 这一新的架构设计理念。不但能够充分利用资源规模化效应,提供更好的弹性,也能让企业享受到真正的按需使用,按用付费。
Serverless 模型使开发者无需关注具体的部署资源,通过事件驱动的方式使用云资源,运行业务。
Serverless 化正在朝着云计算通用标准范式演进。Gartner 则认为这是“十大未来将影响基础设施和运维的技术趋势之一”。云平台所面对的用户群体非常庞大,结合 Serverless 化架构,能够将资源利用、成本优化提升到新的高度。云厂商深厚的工程技术团队和运维经验,也能更好地支撑企业不断的技术演进升级。
作为 Serverless 架构通用事件驱动底座的消息流平台,在这一时期迎来了新的爆发增长。在诸如金融、游戏、电商、交通、教育等关键行业,消息流引擎扮演了削峰填谷、依赖解耦、实时通信与计算等重要作用。作为在线业务核心链路的基础组件,其往往同时具有消息(数据)和流的属性。
在消息场景,如订单业务等,对可靠性、一致性等有较高的要求;而在流的场景,如风控、实时交通等,又对延时、吞吐有更高的要求。传统的消息流平台,往往基于单体架构或传统分布式架构设计,难以充分发挥云上巨大的资源优势,在扩缩容速度、成本等方面,都面临着巨大的挑战。
在深度用云的云原生时代,Serverless 架构模式能够更大限度地发挥云平台的优势。目前,不少厂家也在不断进行 Serverless 化的尝试。但由于对部署架构、存储方式依然有着强烈包袱,存在着不必要的资源开销,并没有一种非常完善、成熟的解决方案。华为云消息流在不断的架构演进中,也在尝试解决这些问题,实现一种适用于消息流的 Serverless 化技术方案。
01 华为云消息流发展历程
华为云消息流平台在为客户持续提供价值、使能业务的过程中,不断思考、探索如何更好地发挥资源价值,提供更灵活、更低成本、更高可靠的消息流能力。我们在技术发展过程中,也经历过几次大的架构演进:
最初的架构是一个共享集群的模式,所有业务都跑在一个公共的集群上,不同的业务按不同 Topic 进行隔离。
这种模式的优点是构建复杂度较低,资源提前部署,使用者不感知底层节点,只关注业务 Topic,起步成本较低。
然而,随着用户和业务的快速增长,云上集群的压力也越来越大,尤其在一些大流量、大数据量的场景下,这种共享集群的模式开始显得力不从心。同时,由于资源共享带来的爆炸半径大、资源抢占、扰邻的问题,也使得其在面对多用户、高负载场景时,存在一定的局限性。
因此,为了更好地适应云上大业务量的场景,我们演进出了独立集群的架构,即每个租户的集群都是独占的,使用独立的计算和存储资源部署,网络上也是独立的 VPC。可靠性上,支持计算节点在 AZ 级和物理机级的反亲和部署,保证单物理机故障、单 AZ 故障时,集群依然可用,并且单集群故障或异常,不会影响其他集群,也不会有集群间的资源争抢和扰邻问题。
在这种架构模式下,用户需要提前评估业务量,创建特定规模的集群。然而,业务总是在变化的,每个业务在不同时间阶段也可能有不同的性能容量诉求,创建新的集群往往意味着业务的拆分,需要进行业务改造,在生产环境中往往是成本很高,或者是难以接受的。
因此,也对单集群的扩展能力提出了要求,这种扩展主要体现在存储、计算和带宽上。
消息流平台本质上也是一种存储服务平台,要满足不断变化的存储诉求,就需要支持存储空间的动态扩展。
基于 LVM 技术,在需要扩容存储空间时,创建指定大小的云盘,挂载到对应的计算节点,并加入对应的逻辑卷组,扩充到逻辑卷中,达到存储空间的动态扩容,上层业务逻辑不用感知底层的多块云盘。对于计算和带宽资源,我们则是通过扩大节点规格和增加节点数来达到扩容的目的。
这种单租架构也是业界比较常采用的一种方式,然而这种架构模式没有实现完全的 Serverless 化,也存在着一些不足,主要体现在存储、计算和带宽资源的弹性和成本上。
目前主流实现下,在集群扩容时,新节点由于没有分片的分布,无法承接流量,均衡负载,需要进行分片的迁移,而迁移会因为数据复制而带来迁移时间长、占用资源的问题,虽然可以通过限制迁移的带宽和设定在低峰时间进行迁移,一定程度上减小迁移带来的业务冲击,但对于高负载下紧急扩容场景,迁移时间长和业务冲击的问题依然难以避免。对于存储资源,业务需要按最大存储空间诉求来预留存储容量,导致存储空间的利用率不高,会有一定的存储资源浪费。
面对这些问题,我们在不断思考、探索的过程中,也逐渐演进出了新的数据流处理平台架构。
02 新一代消息架构
当前主流架构下,由于计算和数据绑定,计算层扩容时,压力无法分担到新的节点,需要将数据分片迁移到新节点。
而分片迁移的过程需要同步历史数据,该过程会导致额外的带宽占用,导致原有节点负载加重,影响业务。
需要迁移数据是因为计算和数据是绑定的,每个节点只能访问本节点的数据。那么,我们如果可以将数据访问解绑,就可以不迁移数据,消除数据迁移带来的诸多问题。
计算和存储分离,能够使得业务扩展更灵活,计算的扩展不需要迁移数据,但伴随而来,是更复杂的部署架构和基础设施资源,对于云上多租、大业务量等场景,更适合存算分离模式,而在小型化部署等轻量化场景,更适合存算一体的模式。
因此,一个更普适的架构,应该是存储计算可分可合,逻辑上独立,物理部署上同时兼容两种模式。我们基于这种思想,也进行了一系列的实践。将存储与上层队列模型进行了解耦,彻底消除了存储介质、存储位置及存储方式与队列模型的耦合关系,通过面向应用的逻辑数据映射访问存储。
逻辑存储架构
消息流数据往往是一种过程性数据,数据追加写入,读取也主要集中在刚写入的数据。基于这种特点,我们首先实现了一种逻辑存储方式,在迁移分片时,只做逻辑上的迁移,物理数据在不同节点存储,通过元数据实现逻辑存储和物理存储的映射。
前面提到,消息是一种流式的数据,通常情况下,生产的消息都会被及时消费,也就是只需要访问本地的数据,并不会产生时延的增加,并且随着新数据的不断产生和历史数据的清理,分片的数据分布会逐渐趋近于目标节点,达到就近访问的效果。
通过这种逻辑存储方式,避免了在分区迁移时进行数据同步带来的资源开销,从实测的结果看,能做到秒级的迁移,并且不会增加原有节点的负载,让扩缩容和迁移过程更平顺。
这种方式一定程度上解决了分区迁移过程中的数据同步问题,但在物理部署上依然是存储计算绑定的,对于存储成本高、利用率低等问题,依然没有得到很好的解决。
在业界,我们看到一些消息流服务也在做相关的尝试,但是对部署架构、存储方式有着强烈耦合,带来了较大的运维成本。
我们看到,造成这些问题的关键在于存储介质、部署和计算依然存在一定的耦合,因此需要做更彻底的分离。
分级存储架构
我们知道,在存储体系中,存储介质、访问时延、成本是互相平衡的,如下图所示,越接近 CPU 的存储介质,时延越低,相应的,容量也越小,单位容量的成本也越高。
因此,不同类型的存储介质适合存储不同类型的数据,访问频繁、时延低的数据适合放在高性能、低容量的存储,访问不频繁、数据量大、需要长时间存储的数据,适合放在低性能、大容量的存储中。
在存储领域,业界已有对冷热数据分离的相关实践。对冷热数据的划分,主要根据其访问的频率,高频访问的为热数据,低频的为冷数据,其次是根据最近访问原则,长久未访问的数据也会被判定为冷数据。
AWS 根据数据访问的频率,将频繁访问的在线类数据划分为热数据,非频繁访问的离线类数据划分为冷数据,在 Redshift 架构中,则是将冷热数据进行了分离存储,本地缓存的热数据使用 SSD 存储,而冷数据则通过 S3 进行存储,以提高存储效率,优化成本。
我们看消息流数据的特点:生产是追加式的,消费场景绝大多数都是及时消费,在金融、消息回溯等场景,可能需要访问历史的数据,这部分历史数据的特点是数据量大,但访问相对不频繁。因此,从读写和存储的生命周期,将数据分为热、温、冷三种:
•热数据:实时读写的缓存数据,这部分数据既是最近访问的,又是近期会频繁访问的,这种访问趋势在流场景是可以预测的,这类数据通常数据量不会很大,但对访问时延、吞吐要求较高;
•温数据:在消息积压场景,数据会在写入后一段时间才内读取,访问频率和实时性相对热数据更低,但其访问依然是可预见的,且对访问性能也有较高的要求,适合高性能、小容量的存储;
•冷数据:已经消费过的历史数据,只在消息回溯等场景再次访问,访问频率低,适合存储在大容量、低成本的对象存储中。
我们根据消息流数据的分布特点,采用分级存储的架构,将冷热数据分别存储在大容量的远端存储和低时延的本地存储,达到存算分离、成本优化的效果。
华为云消息流平台在分级存储架构上的实践,采用块存储作为本地存储,对象存储作为远端存储。对象存储的特点是存储容量大,相比本地块存储具有成本上的优势,并且支持按需使用。
在 Serverless 化大背景下,对象存储正在逐渐成为一种标准存储解决方案。而在读写性能上,块存储具有更好的时延表现。
因此,我们使用了本地块存储和远端对象存储相结合的方式,本地块存储保证了数据实时写入的低时延,对象存储则保存不常访问但数据量庞大的冷数据,达到性能和成本的平衡。
同时,大多业务场景中,流量都是波动的,本地块存储可以作为远端存储的缓冲,起到为磁盘 IO“削峰填谷”的效果,消除对象存储瞬时性能的不足带来的影响。
IO 读写路径
根据前面对消息流数据冷热特征的分析,我们在消息生产时,先写入 pagecache,再同步或异步地刷到本地数据段,异步地将数据段上传到对象存储中,并更新元数据。
对于冷数据往对象存储中卸载的过程,我们的设计是,从时间和空间两个维度出发,存储时间达到阈值,或超出存储容量阈值的数据,这部分数据已经不是访问的热点了,对访问的时延要求也没有那么高,可以由远端存储来承载,会被异步卸载到对象存储中。
消费消息时,根据需要拉取的数据位点,判断目标数据是否在本地存储,如果本地存储命中,则直接从本地存储获取数据,这种情况通常发生在及时消费的场景,往往在 pagecache 中就能命中,也就不需要产生真正的磁盘 IO。当目标数据不在本地时,会尝试从远端存储进行拉取。
一种直观的想法是从远端把数据文件下载到本地磁盘,再从本地磁盘进行读取。
然而这种方式下,会因为 IO 串扰而导致严重的性能问题。我们知道,流式存储在实时消费场景吞吐很高,其中一个重要原因,就是消费的数据,在 pagecache 中能命中,也就是我们所说的热数据,直接从 pagecache 获取数据,无需缓慢的 IO 操作,如果出现大量的冷数据访问,数据已经从 pagecache 中被逐出,需要从磁盘中读取,就会导致磁盘 IO,时延上升,并且 pagecache 中的热数据被冷数据逐出,导致及时消费的流量也需要从磁盘中读取,pagecache“失效”,也就是缓存污染问题。
如果我们把数据文件从远端下载到本地存储,再从本地存储进行读取,那么缓存污染问题依然存在,更加拖慢了访问的延迟,同时,因为多了从远端下载数据到本地的过程,增加了 IO 带宽的开销,每份数据要进行两次磁盘 IO,导致额外的资源开销和时延开销,也增加了本地块存储的空间使用,在发生大量随机读时,会导致大量下载数据文件,使原本轻量的本地块存储变得不堪重负。
前面提到,消息流数据是流式的、过程性的数据,在访问冷数据时也具有这种特点,一段数据通常不会在较长一段时间内被反复访问,也就是通常不会有相对固定的热点数据,或者说热点数据是在持续变化的。
基于这种特点,我们实现了基于内存的远端数据访问机制,通过应用层内存池技术,对内存空间进行管理,将缓存的数据分为多个 slice,和真实的数据段进行映射,数据访问不需要额外的磁盘 IO,带宽占用更小、时延更低。而且实现了冷热数据的 IO 隔离,解决了 pagecache 污染的问题,吞吐能力提升 20%以上。同时能够避免因大量随机读而导致大量下载数据文件,优化本地块存储空间,降低存储成本。
缓存预读优化
由于消息的访问通常是顺序的,因此对于即将访问的数据位置,通常是可预测的,可以通过提前预读下一块数据,提升数据从远端加载的整体性能,减小读取卡顿。
在缓存管理模块中,存在一个预加载水位,当某个 slice 的访问位点达到预加载水位时,缓存管理模块开始加载下一个或多个 slice,该过程是异步执行的,并不会阻塞当前的读取操作。这样,当访问到下一个 slice 时,数据已经提前完成了加载,避免了因从远端加载而产生的卡顿。
存储 Serverless 化
目前业界主流的消息流平台,用户需要提前购买指定大小的磁盘容量,并持续关注磁盘容量使用率的监控,在容量不足时提前进行容量扩容,否则可能因为磁盘写满而影响业务。
在实际的生产环境中,业务量并不是一直稳定不变的,很多业务都存在业务高峰和低谷,如双 11 的流量突增等,相应的,对磁盘容量的需求也可能有较大的波动,在这种模式下,业务通常需要按业务量峰值来评估磁盘使用量,并预留资源,这就直接导致了在非业务高峰时期的资源的浪费。
据估算,目前业界消息流平台的存储使用率平均在 20%-30%左右。
在分级存储架构下,由于大量的数据都被卸载到了远端对象存储,对于业务层而言,是一个统一的存储池,而且支持存储空间的按需使用和超长时间存储,无需预留存储空间,无需进行磁盘的扩容和缩容,消除存储空间使用率不高带来的成本浪费,提升资源利用率,结合线上统计数据,可以提升 3-5 倍的存储使用率。
同时,华为云对象存储服务通过存储介质的慢盘/坏道检测、AZ 内设备和数据冗余、AZ 之间数据容灾、跨区域复制等技术方案,提供针对介质、服务器、机柜、数据中心和区域的多级可靠性保障。其数据持久性高达 99.9999999999%(12 个 9),可用性高达 99.995%,远高于传统架构。
基于这种数据的高可靠和高可用,业务层在卸载数据时,只需保留主副本,减少了多副本对存储空间的额外开销。以三副本为例,在相同业务量下,可以减少约 2/3 的存储空间,结合存储按需使用带来的存储使用率提升,综合存储效率提升 10-15 倍。
弹性扩缩容
在这种分级存储架构下,由于 Broker 本地只有有限的一小部分温数据,使得 Broker 变得更“轻量”,在进行集群扩缩容时,只需迁移 Broker 本地的一小部分的数据,大大降低了迁移过程中数据同步带来的负载影响,并且大幅缩短迁移时间。
这里我们会发现,在迁移过程中,同步的这部分数据,最终也会被卸载到远端存储,那么如果我们直接卸载到远端,就可以省去这部分的数据同步,通过远端存储来达到数据的“同步”。
这里我们用到了前面提到的逻辑存储的思想,将分级存储和逻辑存储结合,在分区迁移时,新的 Broker 只同步增量的数据,当需要发生主备切换时,原主会进行数据文件的切割,并将分段的数据文件卸载到远端存储,当卸载完成后,进行主备切换,切换过程在秒级完成,无需同步历史数据,10GB 数据的节点,可在 1min 内完成卸载和主备切换。在高负载场景下也能实现高效、平滑的扩容。
故障转移
对于故障的场景,基于引擎自身的主备能力,能做到秒级的感知、切换,配合客户端的重试机制,能做到业务不中断的故障自愈。对于部分节点故障导致的负载加重问题,由于计算节点没有历史数据,可以通过快速扩容节点和分区迁移,实现负载的均衡。
售卖模式
传统的消息流平台售卖方式,是基于实例集群的规格,用户需要知道每种规格对应的性能,需要评估节点规格、节点数量等,而用户关注更多的是能力,业务需要多少吞吐、多少 TPS。我们在新的架构中,提供按性能指标售卖的模式,如 TPS 等,让用户根据性能指标选择所需的规格,回归业务本身,而无需关注底层资源,这也是 Serverless 化理念所倡导的。
计费主要分为计算部分、存储部分和增值服务部分。计算部分的计费模式为基础带宽/TPS + 弹性流量。当流量/TPS 超出基础规格范围时,我们将允许一定比例的超限使用,即弹性流量,超出部分独立计费,避免流量突增场景对业务的影响,用户也无需为临时性的流量突增而扩大集群规格,减少资源的浪费。
通过这种模式,帮助用户更合理地使用服务资源,优化成本;存储部分支持按需使用,即根据实际使用的存储空间进行计费,不再需要进行存储的扩缩容;增值服务部分主要包含基本的消息收发之外的一些增量能力,如可观测日志功能、超出规格范围的 topic 数、数据同步等。
智能可观测
在这种分层存储的架构下,我们需要考虑每一层的稳定性,并提供相应的可观测和恢复能力。例如,我们会实时监控对象存储层的访问状态,当对象存储层出现访问异常时,能够快速感知,并自动调整块存储资源,避免 IO 受阻。
另外,传统的运维方式对运维人员的经验有较强的依赖,当线上业务运行产生异常时,往往需要运维人员后台排查,效率较低。目前,业界已有一些自动诊断的实践,这些诊断主要是对一些观测结果和异常项的汇总,如 CPU 使用率过高等,对于一些相对复杂的异常场景,如频繁 rebalance 导致消费延迟等,无法直接得出产生异常的原因和指导性的建议。
针对这些问题,我们基于长期以来积累的运维经验和可观测手段,实现了一键智能诊断的能力,在基础观测指标的基础上,做更深入的自动化业务分析,给用户呈现导致异常的原因和优化建议。
03 云上 Serverless 消息流实践标准探索
如今,Serverless 化已经成为消息流平台发展的新趋势,而如何更好地基于 Serverless 化的消息流平台进行应用设计和开发,则成为了一个值得思考的问题。
在云平台具备高可靠、高可用等能力基础之上,本文结合华为云消息流平台在 Serverless 化演进中的实践,总结提炼出了 Serverless 消息流的三大目标和五大特征,并和信通院共同努力推动标准落地,帮助使用者更好地理解 Serverless 化消息流平台架构原则并能更高效地进行应用开发,发挥 Serverless 化带来的巨大优势,如下表所示。
目标一:业务聚焦(Business Focus)
Serverless 化的消息流平台,应为使用者屏蔽底层基础设施,让使用者聚焦于业务本身,具备以下特征:
基础设施无感知(Infra-less):使用者在使用其提供的服务时,无需直接感知集群底层的资源类型,也无需感知集群的构成,如集群节点数等,使用者只需关注其本身的业务诉求,如所需使用的带宽、对时延的要求等,大大降低了其使用的复杂度。同时,在运维过程中,使用者也无需感知底层资源的负载,如集群节点的 CPU、内存使用率等,更聚焦于业务本身。
目标二:成本优化(Cost Optimization)
Serverless 化的消息流平台,会通过更具弹性的架构和技术手段,在提供同等服务能力的情况下,提升资源利用率,优化成本,具备以下特征:
按需使用(On-Demand):通常,业务负载会随着时间而波动,有的业务会有明显的波峰和波谷,如果按峰值负载预留集群规格,则会导致资源的浪费。
在 Serverless 化模式下,消息流平台支持按使用者实际使用的资源服务进行收费,主要包括计算服务费用、存储服务费用和附加能力费用:计算服务通过业务吞吐衡量,体现在流量和 TPS;存储服务通过存储容量衡量,体现在统计时间点的实际存储容量;附加能力费用包括一些高级特性,如消息轨迹、智能分析等。在负载低峰时,相应的费用也会降低,帮助使用者优化成本。华为云消息流平台在 Serverless 模式设计中,也依据使用者对资源的实际使用来计费。
自动弹性伸缩(Auto Scaling):随着业务负载的波动,底层资源也需要相应地进行扩缩容,以最合适的资源规模满足业务负载诉求。Serverless 化的消息流平台,在面对负载变化时,能够根据负载情况,自动进行弹性伸缩,及时调整底层资源,并进行业务流量的负载均衡,整个过程对使用者无感。
目标三:运维简化(Maintenance Simplification)
Serverless 化的消息流平台在为使用者提供消息流能力的同时,也将大大降低其运维复杂性,让运维变得更简单、快捷,具备以下特征:
可观测(Observability):Serverless 化的消息流平台为使用者屏蔽了底层资源,不再提供直接的资源指标,如 CPU、内存等,而是为使用者提供更为丰富的业务级可观测能力,如细粒度监控、消息轨迹、关键事件通知等,帮助使用者快速、实时地掌握业务的负载情况,发现业务性能瓶颈和潜在风险,及时调整业务。
智能诊断(Intelligent Diagnosis):在业务遇到异常时,能够结合各类观测指标,自动对异常现象进行分析诊断,给出可能的原因和修复的建议,降低使用者运维的复杂度和运维投入,并提升异常发现和恢复的速度,同时,能够对当前的业务使用情况进行分析,并给出优化建议。
04 未来展望
软硬结合垂直优化
华为云自主创新共享存储池,通过软硬件结合优化,使用智能硬件设备和优化存储算法,提升存储利用率,通过优化数据存取方式,提升数据读写的性能,并保障故障时数据持久度不降级。
华为云消息流平台将基于共享存储池,构建高性能、高可靠的统一存储层,应用层无需感知多级存储概念,专注于业务逻辑,减少数据在多级存储之间转移带来的带宽开销,进一步优化成本。
同时,结合华为云底层硬件加速能力,将部分计算逻辑卸载到硬件中执行,降低 CPU/IO 开销,避免因大量 CPU 计算导致资源争抢、线程阻塞等问题,有效降低时延,实现十倍性能提升。
存储计算轻量化
将状态从计算层完全抽离,消除副本概念,通过统一的接入层,在所有计算节点上负载均衡,扩缩容无需迁移分片。同时基于华为云 Serverless 底座,将计算节点轻量化,实现计算节点秒级扩缩。同时,支持存储计算“可分可合”的灵活架构,支持小型化场景部署。
智能化
在业务波动等场景,根据业务实时负载,自动感知,自动弹性扩缩,做到计算能力的按需使用。借助快速发展的 AI 能力,自动分析历史业务变化趋势,自动调整资源分配,做到精细化的资源控制。
在保障业务稳定的同时,也能进一步优化成本。同时,通过 AI 自动分析集群异常和不恰当的使用方式,实现自动运维、无人运维,能够“自动驾驶”的消息流平台。
版权声明: 本文为 InfoQ 作者【华为云开发者联盟】的原创文章。
原文链接:【http://xie.infoq.cn/article/347c64fbae397b3ffe4a1e398】。文章转载请联系作者。
评论