微服务治理:体系、架构及实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

3.2 服务关系维度

3.2.1 治理目标

服务化的本质就是一个“拆”字,原来的单体应用被拆成了大大小小的应用集群和服务集群,并分散到网络的各个节点,由不同团队负责。这时传统意义上的架构师的职责实际上弱化了,对应用及服务的规划、架构、设计能力更多下沉到一线团队,由开发人员直接承担。但每个团队的整体能力和开发风格各不相同,服务拆分及设计的尺度很难做到完全统一。在这种情况下,对架构完全放任不管会导致随意创建服务、服务滥用及服务能力冗余等一系列问题。

因此,在分布式环境下做服务拆分,更要加强对服务集群整体架构的掌控,防止架构“劣化”。要做到这一点,最重要的就是对线上服务进行有效的梳理。在服务数量比较少的时候,架构师还勉强能将服务之间的调用关系梳理清楚,一旦服务数量膨胀,服务之间的调用将构成一张非常庞大和密集的“网”。这时候,依靠人工来梳理服务调用关系是不现实的,需要借助一些自动化的手段从总体上对服务的整体调用关系进行定期梳理和优化,并达到如下治理目标:

● 避免循环调用;

● 梳理集中调用;

● 避免深度调用;

● 梳理冗余服务;

● 优化资源配置;

● 根据服务的重要性,进行分级运维。

3.2.2 服务基础视图

服务提供者和服务消费者都会在服务注册中心注册,因此在服务注册中心中可以获得完整的服务注册信息及被调用信息,图3.4是其数据结构的典型示例。需要注意的是,图中的consumers节点列表不一定等同于单个微服务,也可能是应用ID,这取决于服务消费者的部署模式,可以采用一个节点(容器或虚拟机)部署一个微服务,也可以将若干个微服务合设部署在同一个节点。如果是后者,服务注册中心中的consumers子节点下挂载的就是这个合设应用的ID了。

图3.4 服务注册中心的数据结构

由于服务注册中心还承担管控指令下发通道的职责,除了服务的注册及调用信息,服务注册中心还包含服务管控配置信息。将这些信息综合起来,可以在服务治理平台中用友好的表单界面展示出来,图3.5是一个微服务在服务注册中心中注册的所有服务信息的分页列表展示视图。

图3.5 微服务列表展示视图

列表展示视图列出了服务最核心的一些指标信息,包括服务ID、接口、负责人、服务提供者及消费者的统计量、服务所属应用等。这个视图就是微服务管理的入口,通过单击每一条服务记录,可以进入单个服务的详细信息展示页,如图3.6所示。

图3.6 以页卡表单的形式展示的服务信息

图中展示了“基础信息”(图3.6-①)和“路由(访问)规则”(图3.6-②)两个页卡的内容。除此之外,还有SLA、提供者、消费者、关联应用等其他页卡信息。另外,还可通过信息聚合的方式,将来自运维流程管理系统的服务运维历史信息以“维护历史”页卡展示,如图3.7所示。

图3.7 服务运维历史信息聚合展示视图

3.2.3 服务调用关系视图

业界普遍采用微服务接口定义来标识微服务,可通过如下3种方式获取微服务之间的调用关系。

● 通过服务注册中心中的服务注册信息获取微服务的调用关系。服务提供者基本都以接口为单位进行注册,服务注册中心也以服务提供者的接口(例如图3.4中的com.company.app1.module.MyServer1)为单元来组织服务的相关信息。一些团队会坚持微服务架构的严格限定,尤其是在采用容器化部署时,一个容器或实例只运行一个微服务,这时候从服务注册中心获取的服务消费者节点信息基本(简单换算后)等同于服务接口。但如果采用合设部署模式,多个微服务被合并到一个应用中部署,服务注册中心是无法获得微服务接口一级的服务消费者信息的,只能获得应用的ID信息。因此,虽然通过服务注册中心获取微服务调用关系是最经济的途径,但获取完整的接口级信息是有前提条件和限制的。

● 通过APM的动态调用链分析获取微服务的调用关系。动态调用链可以精确地抓取服务调用双方的详细接口信息,但微服务需要被显式调用才能被动态调用链捕获,因此需要通过较长时间的线上服务调用收集才能获得全面的调用关系。如果部分异常托底服务或者特定业务的分支服务始终未被触发,动态调用链可能永远不会体现它们的信息。因此动态调用链中存在服务接口信息不全的可能性。

● 参考2.2.1.2节,通过扫描源码仓库的全部工程源码,梳理出方法级的调用矩阵,在此基础上梳理出微服务和微服务接口之间的调用子集,即微服务的静态调用链。这种方式的技术难度高,但获取信息全面、准确。

3.2.3.1 单个服务调用关系视图

通过以上任何一种方式获得全面的微服务调用信息后,以单个服务为维度,用表单形式进行关系展示,可以获得如图3.8所示的单个服务调用(图3.8-①)及被调用(图3.8-②)视图。

图3.8 单个服务调用关系视图

3.2.3.2 整体服务调用拓扑视图

单个服务调用关系视图可以让我们了解服务的周边信息。更多的时候,我们需要掌握整个微服务集群的全局调用关系。这时,通过聚合所有服务的调用关系可获取服务集群整体的调用拓扑关系视图,如图3.9所示。

图3.9中的左图是线上微服务集群中服务的调用关系总图,一些拥有大量线上业务的企业,微服务数量巨大,它们之间的调用关系就构成了一个密集立方球体。目前大部分公司都通过动态调用链汇总来勾画这个图,此外还可以基于静态调用链(调用矩阵)来获得。由于静态调用链描述的是代码层面的所有调用关系,比起依赖实际触发的动态调用链获取的调用关系更全面,这个图只是静态调用矩阵的一个子集。有了微服务的整体调用关系,就可以对微服务的调用质量进行深入的分析了。

图3.9 整体服务调用拓扑关系视图

3.2.3.3 闭环检测

虽然微服务集群的服务数量众多,但服务是根据业务分层的(参考图1.15),因此理想的服务调用拓扑关系应该也是分层的,并且层层推进最终形成一个有向无环图(DAG)。如果出现图3.9中G服务调用B服务的情况,说明调用关系中存在反向依赖,需要进行优化。微服务数量少时,可以基于人眼来识别这些闭环,但现实中微服务的数量往往很多,需要借助自动化的手段。这实际上是一个有向图的闭环路径查找问题,可以使用图论中的深度优先搜索算法(DFS)或者广度优先搜索算法(BFS)来实现。在本书的GitHub站点中,有一个DFS算法的演示案例,读者可以自行前往下载体验。

3.2.3.4 最长调用深度检测

我们知道对跨网络的调用访问,整体稳定性是各网络节点稳定性的乘积,因此涉及的网络节点越多,稳定性越差。通过对整个调用网络进行遍历计算,找出所有调用深度最深的调用链,如图3.9上标注出来的调用链路A→B→C→E→K。找到这些最长调用链后,按调用深度进行TopN排序,重点分析排在头部的调用链的必要性及合理性,看是否能对调用深度进行缩减和优化。最长调用路径的查找同样可以采用深度优先搜索算法(DFS)、广度优先搜索算法(BFS)或者单源/全源最短路径算法来实现。

3.2.3.5 集中调用检测核心服务检测

集中调用检测主要评估服务被其他服务调用的情况,主要指标是服务的扇入(Fan-In)数,即调用该服务的上级服务的数量。

从普遍意义来说,如果微服务A被10个其他微服务调用,微服务B只被1个其他微服务调用,那么微服务A的重要性大概率要高于微服务B。例如图3.9中的微服务F(用户中心-账户查询),一共被3个其他微服务调用(依赖),那么就需要给这个微服务定义更高的运维等级,提供更好的资源配置和更高的监控告警防控级别。

另一方面,被集中调用的服务也有可能是由于设计不合理导致的多种功能杂糅在一起,这就需要研发人员及架构师定期梳理并分析评估服务的调用合理性。对不合理的微服务进行拆分,拆成粒度更细的多个微服务。这也是服务治理的日常工作范畴。

通过有向图的PageRank算法可以获得图上每个服务的进入线的数量排行,排行越前面的就是被依赖度越高的微服务。当然,单凭进入线的数量决定微服务的依赖度不够严谨。在实际应用中,可结合服务调用量及服务标签(在设计阶段对服务重要等级的人为定义,存在主观性)这两个指标共同评估服务的重要性等级。在3.7.1节中将对服务重要性的判定做更深入的探讨。

3.2.3.6 清除冗余服务(或版本)

随着架构的变迁,服务之间的调用关系也在不断变化,有些微服务不再存在调用关系,在微服务整体调用关系图上,这些微服务不再有与其他服务的连线,例如图3.9中的L服务。这些再不会去调用别的服务,别的服务也不会来调用它。通过定期关系扫描可找出这类“孤点”,对它进行下线处理,以释放资源。

调用关系网络本质上属于图计算的范畴,以上各种针对调用关系的分析还有一种更便捷的做法,采用诸如NEO4J这类图数据库来存储微服务及调用关系,并基于图数据库的计算引擎做各类关系分析。图数据库的图计算引擎包含丰富的算法工具集合,上述的DFS、BFS及PageRank算法都作为基本算法被其集成,可以提供一站式的分析体验。