一分钟精华速览
本文概述了挚文集团(陌陌和探探母公司)在微服务架构下解决故障定位问题中遇到的痛点、解决方案以及取得的效果。通过构建统一可观测平台,实现了故障快速定位,大幅提升了问题定位的效率。文中还讨论了存储优化、数据采集、链路追踪等相关细节。总体上,可观测平台在挚文集团内部已得到了广泛运用和较好的业务支撑成效。
作者介绍
挚文集团基础平台技术总监——童子龙
TakinTalks 社区特邀讲师。2022 年加入挚文集团,目前负责陌陌和探探的基础平台部门,包含基础架构、中间件、监控、系统平台等团队。曾就职于腾讯云中间件团队任职技术专家,腾讯云微服务 TSF 开源社区 Founder,专注于微服务治理、基础架构、精益管理、云计算及分布式中间件技术等。
温馨提醒:本文约 7500 字,预计花费 13 分钟阅读。
「TakinTalks 稳定性社区」公众号后台回复 “交流” 进入读者交流群;回复“0809”获取课件资料;
背景
挚文集团于 2011 年成立,2014 年 12 月 11 日在美国纳斯达克交易所挂牌上市(NASDAQ: MOMO),拥有陌陌、探探等多款手机应用,以及电影制作发行、节目制作等多元业务,其中两款主要社交软件——陌陌和探探,它们的月活跃用户分别达到了亿级和千万级。此外出海等业务,在东南亚和中东地区也拥有庞大的用户群体。因此,整体公司的业务规模非常庞大,拥有数万级线上实例数和千万级的峰值 QPS,全天调用超过万亿次。
挚文集团是微服务领域的早期探索者之一。自从 2013 年 RPC 理念在国内流行以来,我们就开始采用微服务架构。虽然微服务架构提高了团队的协作效率,但也带来了一些问题,比如服务调用链路复杂、故障定位困难、性能瓶颈定位困难、问题定位依赖专家经验等。
面对不断增长的业务规模和复杂度,我们需要从零开始探索和实践,找到一种高效且低门槛的方式来解决这些问题。
一、故障定位遇到了哪些挑战?
在早期,我们没有系统地实践可观测性工程,通常是像消防员一样,哪里起火了就去灭火,然后再找一些专用工具来解决问题。这导致我们使用的工具非常多,而且彼此之间是独立的。例如,当服务应用出现故障时,我们需要查看基础监控、业务打点、错误日志等;而当底层系统出现问题,如 CPU、内存堆栈和线程问题时,我们又需要使用其他工具来查看。这样的情况导致整体体验非常差,处理一个问题可能需要花费 3 分钟去寻找合适的排查工具,再花费 10 分钟查看数据,而且这些数据无法进行联动分析,我们还需要手动编写文档来整合信息,才能深入分析问题的根源。
这种情况带来了两个问题。首先,使用这些工具的门槛非常高,新人很难掌握。其次,效率非常低下,故障排查依赖经验,需要高级专家的介入。我们尝试过专家会诊制度,但这并不是一个最理想的解决方案,作为技术管理者,我们希望任何人都能参与故障排查,而不是依赖于少数资深人员的经验。假如专家刚好不在呢?线上故障排查是非常严肃的事情,不能靠个人主义和运气,更需要一套健全可靠的工具和机制。
经过不断探索和实践,目前挚文集团绝大部分故障定位都能在 3 分钟内完成,即使是新人无需培训也能完成相关工作。接下来,我将重点分享相关平台工程的建设方法和技术要点。
二、如何构建一站式观测平台解决?有哪些技术要点?
要解决问题定位的最后一公里,我们的实施方向是什么?要达成哪些目标?要怎么着手做?带着这些问题我们进行了大量的调研工作,包括研究行业内成熟的商业产品和技术、以及访谈行业专家,了解他们的观点和实践方法,然后以此为基础,我们设定了一个目标——建立一个统一的可观测平台,数据协同实现简单高效的可观测性能力。
2.1 可观测平台工程目标
我们需要从两个方面来设定目标:效率和门槛。
首先,我们的目标是让任何人,不论是新手还是较低级别的开发人员,都能够快速准确地找到问题所在,而不需要依赖专家经验。为此,我们计划建立一个企业级的平台工程,数据采集、分析工具化、产品化,来降低使用门槛。
其次,我们追求更加全面的可观测性,覆盖整个软件生命周期。我们需要建立完善的基础设施,从开发环境到测试阶段,再到生产环境,实时监测和分析系统的运行状态。通过及时发现并解决问题,来确保系统的稳定性和可靠性。
2.2 分布式 Trace 追踪
我们需要先明确分布式 Trace 追踪的目的是解决哪些问题。它可以帮助我们了解服务之间的调用行为,快速定位异常问题,进行精确的链路调用性能分析,包括方法和整个应用内的性能追踪和监控。接下来,我们需要思考如何实施这个追踪系统。
(分布式 Trace 整体架构)
首先我们希望基于开源组件的基础上自建生态,为什么坚持基于开源标准自建呢?因为开源有人才规模效应,从管理角度,不希望基础平台被自研技术栈绑架。另外,我们希望实现业务接入的无侵入性,减少接入成本,并支持动态下发采集策略,使用户接入层更稳定灵活,不会给业务带来任何 Debuff。
分布式 Trace 追踪系统整体架构分为三层:接入层、处理层和平台层。
接入层:嵌入应用进程,按照 Opentracing 协议收集 Trace 信息,通过插件化的传输协议发送给 Server 端,接入层的采集数据可以传输给任何支持 Opentracing 协议的 Server 端处理。
处理层:接收接入层采集的 Trace 数据信息,然后对采集的数据进行分析和聚合,生成性能指标、统计数据和可视化报告,将解析后的数据持久化到 ES 集群。
平台层:负责 Trace 链路实时检索,指标统计可视化报告,Trace 配置管理下发。
动态下发策略解决了什么问题?在 Trace 采集过程中,我们采用了多种灵活策略。例如,我们对于一些有价值的用户和服务,提升采样概率;我们可以根据 CPU 负载进行熔断,并为不同的应用设置不同的阈值,以适应不同的生产情况,保障线上业务的稳定性。
具体的架构细节将在后续进行详细讲解。总体而言,接入层负责采集数据,然后通过 Kafka 传输到接收端进行聚合分析。平台层将下发配置,并提供 Trace 实时检索和离线统计分析的能力。
然而,整个实施过程并非一蹴而就。正如前面提到的,我们是从零开始探索这个过程,因此我们面临了许多问题和挑战,有些情况和挑战甚至导致我们的方案无法顺利落地。我们需要不断改进技术方案,以应对这些挑战。
挑战一:链路采集端维护成本高
我们面临的第一个问题是,维护链路采集端的成本非常高。早期我们采用了 SDK 的方式,由于涉及多种语言和框架,如 PHP、Java 等。在实际落地过程中,我们发现这种方式的推广成本非常高。首先,涉及到的团队太多,导致沟通成本很高。其次,推广和升级的时间非常长,因为需要得到业务方的配合。整个版本的碎片化问题也会变得非常严重,因为有些业务不迭代或者系统本身由于各种原因无法进行升级。
技术要点 1:下沉 Agent 模式采集链路
在进行了一些行业调研后,我们开始调整方案——主流的 Java 语言朝着 Agent 方向改进,其他语言也尽可能地能力下沉,采用 Instrumentation 或者 Ebpf 抓取 Syscall 机制。
Java Agent 模式采集是基于 JVM 原生机制。具体来说,我们使用了 JVM 提供的一种叫做 JVMTI 的接口工具。它的工作原理如下图所示。
通过动态注入字节码方式,我们可以基于 Opentracing 协议,组装服务之间的调用关系,获取方法调用时间、自动捕获异常以及异常日志等。这种方式有两个优点。首先,它采用了动态注入的方式,不需要业务方去修改代码。这样就解决了之前所提到使用门槛和沟通成本高的问题。相比之前的 SDK 等方式,推广成本大大降低了。其次,由于微服务组件的生态规模越来越大,我们可以很低成本适配各种基础设施并进行字节码注入。
技术要点 2:基于开源扩展的 Agent 插件
前面提到,我们是基于开源组件进行扩展,并对内部的中间件插件和 RPC 插件进行定制。我们希望在开源范式下开展这些工作,并且希望为社区做出贡献,将遇到的共性问题分享给开源社区一起解决。通过这种协作方式,进一步降低整体的维护成本,即使人员流动,新的同事和社区成员也能够轻松接手和维护这套系统。
在这个过程中,有一个细节需要注意。由于一些插件可能存在不同的版本,比如 1.0 和 2.0 版本的使用方法不兼容,有些代码的出参和入参也不一样,可能会导致不兼容或逻辑发生较大变化,因此我们还需要在一个 Agent 中解决插件版本的兼容问题,开源原生的 witnessClass 匹配的机制能比较巧妙的解决这个问题。
此外,我们还遇到了许多跨线程相关的问题。为了应对其中潜在的风险和矛盾,我们对整个跨线程池调用进行了大量的梳理和治理工作。
技术要点 3:构建工具为生产推广提速
为了更好的解决推广和维护成本的问题,仅仅能力下沉仍然不够,Agent 作为 Premain 探针,需要用户在启动时添加参数,实例需用重启。为了在保障推广接入过程稳定不出故障的同时,让生产推广提速,我们构建了两个重要工具。
CI/CD 平台对于不同服务大规模单实例批量灰度工具,能够实现在同一时间大批量的服务进行单实例的扩容灰度接入,对于灰度实例自动挂载探针包,自动添加启动参数。
对于生产的大规模变更进行自动异常检测,对于线上大规模的灰度发布变更,无需 SRE 人工值守,对黄金指标的波动和异常能自动检测告警,SRE 能快速针对有异常服务迅速回滚,减少因变更引起的故障范围。
当然整个工具链构建过程比较复杂,甚至可以另起一篇,这里暂时不详细展开了。
挑战二:Trace 完整性问题
Trace 的完整性问题是整个行业都面临的一个共性痛点。在采样方面,目前有两个主要方向,一个是尾部采样,另一个是后置采样。
由于陌陌和探探的调用量每天都达到万亿级别,数据量非常庞大。如果要持续地进行全量采集,存储和查询的成本将难以承受,查询性能也会受到很大影响。另一方面,如果要实现全量采集,采集端的 CPU 消耗也会非常高,因为每个 Span 都需要进行采集、传输和分析。
虽然开源社区已经提供了一些采样率限制策略,但这些策略并不能解决上述两个问题。如果链路缺失,我们排查问题的效率就无法提高,用户体验也会大打折扣。因此,我们的目标是在不影响用户排查体验的情况下,尽可能降低成本。
在明确了这个目标后,我们开始着手解决这个问题。
技术要点 1:成本 &价值导向的采样策略
我们首先根据成本和价值的考量来确定采样策略。我们明确了用户关注的链路重要等级,例如出现错误、高耗时的情况以及确保整个链路拓扑图的完整性等等。在这个基础上,我们可以进一步选择适合的采样策略。
技术要点 2:自定义的 Agent 采样策略
在我们讨论自定义采样策略之前,先来谈谈行业通用的尾部采样。尾部采样是指在整个链路的 Trace 完整采集完成后,对链路进行分析,筛选出有价值的链路,例如出现错误或者耗时较长的链路,这些链路会被完整的传输到服务器端进行进一步的分析,保留有价值的链路,而正常的链路则会被概率性丢弃。尾部采样保留了整个链路的完整性,但对采集端的性能有较大影响,网络传输开销也比较大。然而,对于一些性能、资源消耗敏感的业务来说,不能接受 Agent 对 CPU、内存等业务资源过度占用。为了解决这个问题,我们提出了后置采样策略。
后置采样的思路仍然是如何保留用户最关注的链路,在不削减用户体验的同时,最大限度地减少成本和损耗。在采样前置阶段进行 CPU 阈值分析和熔断保护。我们会根据应用的 CPU 使用情况判断是否进行采样,避免对业务造成额外负担。例如,如果业务的 CPU 处于较高水平,我们就不会进行采样,以避免进一步消耗其资源。
每个探针都可以针对性地设置熔断阈值,以保护系统。如果没有熔断,我们可以进行百分比的概率采样。需要注意的是,有些服务方法的 QPS 很低,可能一小时只有一两条记录,但这并不意味着这条链路不重要。因此,针对这些低频方法,我们也需要设计一些策略来保留,以确保它们在一定的时间窗口内被采样到,不受概率影响。
而在请求后置阶段,错误和高耗时的链路是必采的。如何判定高耗时,我们会根据每个探针的预设值进行设置,以便将其记录下来并在 Tracing 链路中查看。
综合起来,我们的采样策略是基于概率采样、低频方法兜底和后置采样的组合。这样可以最大程度地减少对业务资源的占用,同时保留最有价值的链路。我们的目标是在最小化浪费的同时,将 Trace 采样完整性、高价值做到极致。
(常见采样方式对比)
全量采样是将所有链路都进行采集、传输和存储,但这种方式的存储成本非常高,可能需要上千亿台存储节点,成本非常昂贵。
频次限制是社区比较通用的采集方式,但它的一个很大缺点是整个拓扑图不完整,链路的缺失严重,这会严重影响业务排查问题的效率。例如,当业务方收到一些告警并希望通过我们的平台进行排查时,却发现无法看到他们的请求,这会给业务带来很大的困扰。
尾部采样个人认为唯一的缺点是对采集端的性能影响较大,网络传输成本高。但对业务来说,它的体验应该是最佳的,因为它保证了链路的完整性,无论是异常链路还是正常链路,都能完整地展示出来。
后置采样虽然不能像尾部采样一样保证链路完整性,但我们的目标是在不影响业务排障体验的同时,最大程度地保证链路的完整性,来节省采集端损耗、传输成本、存储成本。最后的成果整体也非常可观,评估下来采集成本可以降低 90.9%,存储成本可以降低 99.9%。
挑战三:Trace 存储成本
存储是整个成本中最大的一部分,无论是专门从事这一领域的 ToB 服务公司,还是企业技术团队本身,在存储方面的成本都在不断寻求进步。挚文集团也不例外,希望尽可能降低存储方面的成本。
技术要点 1:Trace 存储数据分析
为了实现这个目标,我们在存储 Trace 数据时进行了大量的数据分析,而不是盲目采用某些方案。
在进行有效性分析时,我们发现服务端在进行聚合分析时会产生许多无用的 Metrics 数据,这些数据量相当大,基本达到十亿级别。对于这些无用的数据,我们会进行优化关闭分析和存储。
同时,我们还会进行类型分析和数据冷热分析。对于经常使用和查询的数据,我们会将其存储在性能较高的存储介质中。而对于很少使用和查询的数据,我们会将其存储在成本较低、查询效率相对较低的存储介质中。这样既不牺牲用户体验,又能够极致地压缩存储成本。
技术要点 2:Trace 存储冷热分离
在存储方面,我们还进行了冷热数据的分离。对于三天内的链路数据,即时效性要求非常高的数据,我们将其存储在高速的 SSD 存储介质中,并通过 ILM 策略自动将旧的数据同步到成本较低的 HDD 中。这些冷数据的查询可能会稍慢一些,但大多数情况下并不需要查询三天前或一周前的数据,因为这些问题通常已经及时解决和分析了。通过这种数据分析和存储方式的优化,我们成功降低了存储成本约 99%,链路调用详情的 P99 响应时间基本上能够保持在 100 毫秒左右的水平。
存储是一个重要的研究领域,大家都在努力寻找低成本、高效和高性能的存储解决方案,来取代以前的老存储方案(例如 ES)。然而,自研存储对人才密度有较高的要求。尽管挚文集团的业务规模比较庞大,但我们没有足够的人力来支持自研存储的工作。因此,我们更倾向于使用基于开源方案来降低成本。我相信这条路对大多数公司来说可能更适合。
2.3 Metrics 指标监控
在 Metrics 监控方面,我们采用了分布式实时计算架构,并将其分为数据上报、数据计算和数据存储几个模块。通过 Agent 和 SDK 上报的方式,将数据发送到数据中心,然后通过流式计算进行分钟级和秒级的计算,及时发送监控告警通知。
在存储方面,我们将报警、采集策略存储在 Mongo 中,而监控时序数据则存储在 Clickhouse 中。这样的架构具有许多优点。首先,能够支持大规模指标的的数据监控——我们仅 Metrics 指标的类型就有上万种,并且我们的数据量非常庞大。其次,大量数据能够进行秒级、分钟级的实时计算,并且集群具备高可用、高扩展性。支持的报警策略也十分灵活。这样的架构能够满足我们对数据实时计算和及时告警推送的需求,而不依赖对海量数据的实时查询,确保用户能够获取准确的监控数据,并及时发出告警信息。
技术要点 1:为什么不选 Prometheus
大规模、高可用、高扩展,以上提到的架构优势是我们通过对比行业中一些常见架构得出的结论,比如 Prometheus 和 Grafana。下面我将分享一下为什么我们没有选择 Prometheus。
首先,Prometheus 采集端的维护成本高。Prometheus 采用拉取式架构,需要维护大量的目标端点,并且需要更新这些目标端点的配置信息,不管是手动配置还是使用服务发现,都增加了维护的复杂性和成本。
其次,Prometheus 有自己的数据存储引擎,然而在处理数据节点故障方面,Prometheus 的表现不佳。一旦节点发生故障,数据可能会丢失,这是我们无法接受的,当然 Prometheus 也能使用第三方存储引擎,比如 VictoriaMetrics,但部署比较复杂,我们在落地过程遇到问题也不少,基础设施应追求稳定,简单高效。
第三,Prometheus 的存储引擎相对于其他方案消耗更多的存储资源,监控的时序数据特征比较明显,比如有一定的周期性和重复性等。根据数据特征我们能通过合适的压缩算法,如 Differential Compression、LZ4 等,实现比较高的数据压缩比。
第四,Prometheus 的报警策略支持相对不够灵活,其次告警依赖 PromQL 对海量数据的实时查询,对数据库造成的压力非常大。报警策略的分析推送量非常大,然而在 Prometheus 架构中,每个告警都需要通过 PromQL 对数据进行查询,这给存储带来了巨大的压力,PromQL 对复杂策略查询(如组合策略、波动策略)支持难度也比较大。
最后,Prometheus 对于一些分布式方案的支持效果也不够理想,高可用集群方案维护复杂,HA 模式不能保证 Server 之间的数据一致性问题和数据丢失问题,横向水平扩展需要借助联邦集群模式。然而现实场景中也没有十全十美的方案,我们需要根据自己的数据特征、策略特征,选取适合的技术方案,因地制宜,丰俭由人。
技术要点 2:数据上报
在数据上报方面,黄色部分表示需要业务方手动埋点的内容,例如会员增长趋势和业务运营数据。而绿色部分则可以通过框架自动采集、自动上报和聚合分析。
我们进行的分析包括应用基础监控、基础组件监控以及主机监控等等,例如网络、磁盘和 CPU 等基础监控数据。总体而言,我们的采集维度比较全面,覆盖范围广泛。能用工具解决的上工具,能自动化的不手动,我们宁愿基础平台多走 99 步,也要让业务用户少走 1 步。
技术要点 3:告警 &智能分析
技术要点 4:TSDB 存储引擎
在存储引擎方面,我们也是不断的演进,不懈的追求成本和效率,从之前的 OpenTSDB,到现在的 Clickhouse 引擎。
在存储方面,如之前所提到的,整个行业都在关注提高查询性能和降低存储成本。当然,Clickhouse 可能并不是最佳选择,未来可能会出现其他新的存储介质和架构。不管是软件还是硬件,都会朝着更好的方向发展,因此我们需要不断优化,并呼吁大家关注这个方向。
至于 Clickhouse 的优势,在我们替换了之前的 TSDB 存储引擎后,查询性能得到了显著提升。以前的 P99 平均查询时间约为 900 毫秒,经常出现 10s 以上的超时,现在已经降低到 100 毫秒。同时,存储成本也降低了约 4 倍。升级后,我们还能支持更长时间范围的查询(最长一年的单次查询),提供更好的用户体验和成本效益。
2.4 Profile 应用性能持续分析
Trace 更多的是解决的是服务之间的调度问题,但是对于服务内部的复杂问题,比如持续的 CPU 占用、不合理的内存申请等,这些工具很难深入应用内部进行定位。因此,我们需要一些轻量级的工具来解决这些问题,并将相关工具进行产品化和平台化,让用户能够在平台上进行这一系列复杂操作,并获得完整的数据。
产品化的目标是确保用户不会错过任何事故现场。当应用出现问题时,我们希望能够直观地查看应用进程内部的堆栈数据和信息。我们不希望用户错过现场,同时也不希望采集到一些低价值的数据。因此,我们制定了一些策略,如在异常发生时触发告警或定时采集数据,以获取一些有价值的时序数据。
时序数据有什么作用呢?在行业中,有一个差分图的概念。我们需要了解一个方法在过去和现在的性能是否发生了变化,CPU 时间片的占用情况是否发生了变化,以及一些趋势如何。这些信息能够帮助我们找到问题所在,例如某个方法的同比环比增高,就需要关注是否存在泄漏等更深层次的原因。这样的分析能够提醒我们并解决潜在的风险,避免故障的发生。
2.5 基于数据协同的故障自动化检测分析
当可观测数据达到一定规模和覆盖度之后,我们开始思考能不能将所有的数据进行协同分析,做到线上异常智能检测,并进一步对问题根因进行智能分析。追踪 AMP 系统、Logs、Metrics 数据中的异常值,结合黄金指标的异常波动,基于以上海量的监控数据获取更多的错误上下文,帮助用户更快定位问题,以及快速发现线上潜在问题和风险,我们目前能做到线上的大规模变更的智能检测异常,并给出明确的错误原因。
我们的愿景是能做到无人值守、故障根因自动分析,然而路漫漫其修远兮,我们离这个目标还有距离,国内外商业化产品能做到的也寥寥无几,落地效果也是千人千面,我们也在不断的尝试和演进,结合自己的数据特征,真正做到解决故障根因定位的最后一公里。
三、落地成果和业务价值
3.1 故障定位速度提升 85%,90%问题定位率
通过构建一个一站式的可观测平台,我们以数据和流程的方式提炼出了最佳的故障定位路径,不再依赖经验。问题定位的时间从之前的 20 分钟缩短到了大约 3 分钟。通过这种平台闭环的最佳实践,我们已经实现了 90%左右的问题在平台上得到定位。我们希望能够通过其他途径和补充手段将剩余的 10%也能找回来。
3.2 降低使用门槛,只需 2 步即可操作
为了降低使用门槛,我们采用了行业标准的解决方案,注重产品的易用性,降低用户理解成本,并与现有基础设施充分契合。这意味着使用我们的平台无需繁琐的培训和讲解,新人入职后也能够快速上手。
3.3 降低业务接入成本,提升接入效率超过 60%
为了推广平台,我们非常关注业务团队的需求,业务团队最关注的无外乎三点,1.产品的价值;2.稳定性;3.接入成本。通过工具化的批量接入和监控能力下沉,自动感知生产的变更异常,我们目前可以实现对业务的无感知接入,业务无需投入任何人力成本,以前需要 4 个季度才能完成的任务,现在可能只需要 1.5 个季度就能完成,大大降低了组件推广成本。
3.4 业务性能损耗较低,大部分在 5%以内
目前在稳定性方面,我们能够保证约 80%的服务的 CPU 占用率在 5%左右,整体内存增长也较少,整个推广过程 0 故障,0 线上应急处理。
通过对各个业务线的满意度调研、客观数据和主观数据的验证,可观测平台的价值和稳定性得到了业务方的广泛认可。
四、总结展望
稳定性是我们的基本盘,而我们的团队目标是支持公司业务在 5 分钟内发现问题、10 分钟内做出响应、15 分钟内完成恢复。虽然这个目标看似直观,从整个故障的生命周期来看,需要大量的工程平台来支撑,涉及不同企业环境,不同工种,覆盖软件的全生命周期,研运一体,数字化协同。
Jeff Bezos 曾说过,空有美好的愿景没有用,你需要良好的机制来实现它,从管理的角度来看,流程不能完全依赖人,必须通过一套有效的机制和工具来帮助团队做好兜底工作,机制永远比人可靠。作为基础平台这样的职能部门,我们也需要有全局的视角,从问题出发,从业务出发,持续审视团队的价值和目标,并制定明确的规划和路径来实现这些目标。(全文完)
!!重要通知!!
TakinTalks 社区第二本印刷物《SRE 可靠性体系建设实战笔记》已正式启动,现公开征集共创作者,一起布道 SRE!
招募对象:运维负责人、SRE 负责人、架构师等稳定性相关职位,团队负责人/总监级以上
第一期参考:10万字干货:《数字业务连续性提升最佳实践》免费领取|TakinTalks社区
若您有意成为本书作者之一,请您与我们联系,获取本书目录。
添加助理小姐姐,凭截图免费领取以上所有资料
并免费加入「TakinTalks 读者交流群」
声明:本文由公众号「TakinTalks 稳定性社区」联合社区专家共同原创撰写,如需转载,请后台回复“转载”获得授权。
本文由博客一文多发平台 OpenWrite 发布!