微服务拆分策略

Posted 90后小伙追梦之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微服务拆分策略相关的知识,希望对你有一定的参考价值。

1 微服务迁移准备

1、需对业务充分了解,这是服务拆分,通信设计,资源整合的必要前提。

2、适应微服务架构设计原则:小版本,高速迭代。

3、快速的环境提供能力:依赖于云计算、容器技术,快速交付环境。

4、服务合理拆分:需符合团队结构或能逆向影响,能对组织架构进行微调并划分职责。(康威定律和逆康威定律)

5、基本的监控能力:包括基础的技术监控和业务监控。

6、快速的应用部署能力:需要部署管道提供快速的部署能力。

7、DevOps 自动化运维能力:需要具有良好的持续集成和持续交付能力,还需要对问题、故障的快速响应能力,开发、测试和运维能协同工作。

2 微服务颗粒的拆分策略

前面两篇文章我们学习了What & Why(什么是微服务和为什么需要做微服务架构),这一章我们就来探讨如何做微服务架构的拆分(How)。

微服务拆分没有一个绝对的标准答案,服务拆分的粒度需要根据业务场景来规划,而随着业务的发展,原先的架构方案也需要做调整。

虽然没有固定的套路,但是我们在业务实践过程中总结的一些经验,以做参考。

2.1 基于业务逻辑拆分

基于业务逻辑拆分相对好理解一点,典型的单一职责原则,我们将功能相近的业务整合到一个服务颗粒上。比如一个办公领域系统,考勤、工作流、音视频会议是是三个截然不同的业务领域,这可能就是我们拆分的一个入手点。

2.1.1 领域模型拆分

领域驱动设计DDD(Domain-Driven Design 领域驱动设计)是一个很简单的概念,表示我们对系统的划分是基于领域的,也即是基于业务方向去思考的。

举一个典型的电商业务例子。电商的业务体系庞大,涉及各方面的细节。但是我们大概能够根据业务的职能做一个拆分,比如阿里的电商中台业务,包含 用户账号子系统、商品子系统、订单子系统、客户子系统、物流子系统 等。

因为职能不同,这些领域之间包含清晰的界限,所以我们可以按照这个方向将服务于不同领域(商品域和订单域)的子系统拆成独立的服务颗粒。如下图:

2.1.2 用户群体拆分

根据用户群体做拆分,我们首先要了解自己的系统业务里的用户角色领域是否没有功能耦合,有清晰的领域界限。

比如教育信息化系统,教师的业务场景和学生的业务场景,基本比较独立,而且拆分后流量上有明显的削弱,这时候结合具体的业务分析,看是否有价值。如下图所示:

2.2 基于可扩展拆分

这个需要区分系统中变与不变的部分,不变的部分一般是成熟的、通用的服务功能,变的部分一般是改动比较多、满足业务迭代扩展性需要的功能,我们可以将不变的部分拆分出来,作为共用的服务,将变的部分独立出来满足个性化扩展需要。

同时根据二八原则, 系统中经常变动的部分大约只占 20%,而剩下的 80% 基本不变或极少变化,这样的拆分也解决了发布频率过多而影响成熟服务稳定性的问题。

比如一个电商领域的系统,用户信息、基本商品信息、物流信息 等模块的管理能力和视图界面,一般是比较稳定的;而类似运营活动的功能和页面一般是经常变化的(520、618、双11),会有不同的活动策略和视图界面,需要经常迭代发布。如下图所示

2.3 基于可靠性拆分

2.3.1 核心模块拆分

我们团队在做mysql数据库和Redis集群拆分的时候,总会把一些重要的模块独立放在一个集群上,不与其他模块混用,而这个独立的集群,服务机性能要是最好的。这样做的目的是,当重要度较低的模块发生故障时,不会影响重要度高的模块。

同要的道理,我们会将 账号信息、登录信息、服务中心等重要度最高的要害模块单独拆分在一个服务颗粒上(因为这类模块不可用之后,整个系统基本完全瘫痪),再做成服务集群,来保障它的高可用。 如下图所示:

2.3.2 主次链路拆分

在各个业务系统中,其实都会有主次业务链路。主业务链条,完成了业务系统中最核心的那部分工作。而次链路是保证其他基础功能的稳定运行。

以电商为例子:商品搜索->商品详情页->购物车模块->订单结算->支付业务,就是一条最简单的主链路。主链路是整个系统的核心主战场,最好的资源跟火力都要放在这里,保证不失守。

一个系统一般有多条核心链路和多条次链路,互相支持构成一个完整的系统。而我们将主次链路进行拆分,主要为了以下几个目标。

异常容错

为主链路建立层次化的降级策略(多级降级),以及合理的熔断策略,这部分我们将在Hystrix服务容错降级的文章中详细解释。

计算资源分配

主链路通常来讲都是高频场景,自然需要更多的计算资源,最主要的体现就是集群里分配的虚机数量多。比如电商场景中特惠专场抢购等。

但是无论是虚机的分配,还是kubernetes的动态扩缩容,应该都需要单独优待,如资源分配倾斜,独立治理等。

服务隔离

主链路是高频且核心的主业务模块,把主链路的服务与其他起辅助作用的业务服务隔离开来,避免次链路服务的异常情况影响到主链路服务。

2.4 基于性能需求拆分

根据性能需求来进行拆分。简单来说就是访问量特别大,访问频率特别高的业务,又要保证高效的响应能力,这些业务对性能的要求特别高。比如积分竞拍、低价秒杀、**抢购。

我们要识别出某些超高并发量的业务,尽可能把这部分业务独立拆分出来。这么做的原因非常简单,一个保证满足高性能业务需求,另一个保证业务的独立性,不互相影响。

类似积分竞拍、超低价秒杀、**抢购,对瞬间峰值和计算性能要求是非常高的。这部分的业务如果跟其他通用业务放在一块,一个是可能互相影响,比如某个链路阻塞,会导致雪崩沿调用链向上传递。

另外一个是如果多个业务耦合在一块,发布频率变高、服务扩缩容变难、维护复杂度变高。

3 总结拆分原则

  • 先少后多(微服务数量)、先粗后细(粒度)
  • 基于业务逻辑进行拆分(用户群体、业务领域等模型)
  • 基于可靠性(核心模块独立化、主次链路隔离)
  • 基于性能拆分(独立拆分高性能场景)
  • 接口需保证幂等
  • 接口数据定义严禁内嵌,透传
  • 规范化工程结构,符合微服务风格
  • 不止对计算服务记性拆分,服务层 -> 缓存层 -> 数据层 的逐步拆解,才能发挥最大功效。

Brand

1 传统单体系统介绍


在很多项目的业务初期阶段,高速迭代上线是首要考虑的事情,对后期的容量预估、可扩展性和系统健壮性、高可用一般没有那么重视。但随着业务的发展,用户量、请求量的暴增,

发现原来的单体系统已经远远不满足需求了,特别是随着互联网整体的高速发展,对系统的要求越来越高。

但是物理服务器的CPU、内存、存储器、连接数等资源有限,单体系统能够承受的的QPS也是有限的,某个时段大量连接同时执行操作,会导致web服务和数据库服务在处理上遇到性能瓶颈。

为了解决这个问题,伟大的前辈们发扬了分而治之的思想,对大数据库、大表进行分割,可以参考我的《分库分表》,以便实施更好的控制和管理。

同时创建多个服务实例,使用多台服务机进行CPU、内存、存储的分摊,提供更好的性能。

1.1 单体系统的问题

1、复杂性高:由于是一个单体的系统,所以整个系统的模块是耦合在一起的,模块的边界比较模糊、依赖关系错综复杂。功能的调整,容易带来不可知的影响和潜在的bug风险。

2、服务性能问题:单体系统遇到性能瓶颈问题,只能横向扩展,增加服务实例,进行负载均衡分担压力。无法纵向扩展,做模块拆分。

3、扩缩容能力受限:单体应用只能作为一个整体进行扩展,影响范围大,无法根据业务模块的需要进行单个模块的伸缩。

4、无法做故障隔离:当所有的业务功能模块都聚集在一个程序集当中,如果其中的某一个小的功能模块出现问题(如某个请求堵塞),那么都有可能会造成整个系统的崩溃。

5、发布的影响范围较大:每次发布都是整个系统进行发布,发布会导致整个系统的重启,对于大型的综合系统挑战比较大,如果将各个模块拆分,哪个部分做了修改,只发布哪个部分所在的模块即可。

1.2 单体系统的优点

1、系统的简易性:系统语言风格、业务结构,接口格式均具有一致性,服务都是耦合在一起的,不存在各个业务通信问题。

2、易于测试:单体应用一旦部署,所有的服务或特性就都可以使用了,简化了测试过程,无需额外测试服务间的依赖,测试均可在部署完成后开始。

3、易于部署与升级:相对于微服务架构中的每个服务独立部署,单体系统只需将单个目录下的服务程序统一部署和升级。

4、较低的维护成本:只需维护单个系统即可。运维主要包括配置、部署、监控与告警和日志收集四大方面。相对于单体系统,微服务架构中的每个服务都需要独立地配置、部署、监控和日志收集,成本呈指数级增长。

1.3 单体服务到微服务的发展过程

EUREKA的注册中心逐渐被ZooKeeper和Nacos等替代了。

2 关于微服务

微服务是一种架构模式,是面向服务的体系结构(SOA)软件架构模式的一种演变,

它提倡将单一应用程序划分成一组松散耦合的细粒度小型服务,辅助轻量级的协议,互相协调、互相配合,为用户提供最终价值。

所以,微服务(或微服务架构)是一种云原生架构方法,其中单个应用程序由许多松散耦合且可独立部署的较小组件或服务组成。这些服务通常包含如下特点:

2.1 单一职责

微服务架构中的每个节点高度服务化,都是具有业务逻辑的,符合高内聚、低耦合原则以及单一职责原则的单元,包括数据库和数据模型;

不同的服务通过“管道”的方式灵活组合,从而构建出庞大的系统。

2.2 轻量级通信

通过REST API模式或者RPC框架,实现服务间互相协作的轻量级通信机制。

2.3 独立性

在微服务架构中,每个服务都是独立的业务单元,与其他服务高度解耦,只需要改变当前服务本身,就可以完成独立的开发、测试、部署、运维。

2.4 进程隔离

在微服务架构中,应用程序由多个服务组成,每个服务都是高度自治的独立业务实体,可以运行在独立的进程中,不同的服务能非常容易地部署到不同的主机上,实现高度自治和高度隔离。

进程的隔离,还能保证服务达到动态扩缩容的能力,业务高峰期自动增加服务资源以提升并发能力,业务低谷期则可自动释放服务资源以节省开销。

2.5 混合技术栈和混合部署方式

团队可以为不同的服务组件使用不同的技术栈和不同的部署方式(公有云、私有云、混合云)。

2.6 简化治理

组件可以彼此独立地进行扩缩容和治理,从而减少了因必须缩放整个应用程序而产生的浪费和成本,因为单个功能可能面临过多的负载。

2.7 安全可靠,可维护。

从架构上对运维提供友好的支撑,在安全、可维护的基础上规范化发布流程,支持数据存储容灾、业务模块隔离、访问权限控制、编码安全检测等。

3 微服务演进史

我们前面已经了解了微服务的概念,通过百度指数可以看出,从2012年之后,微服务的发展有显著的发展趋势。

目前业内的微服务相关开发平台和框架还是比较多的,比如较早的Spring Cloud(使用Eureke做服务注册与发现,Ribbon做服务间负载均衡,Hystrix做服务容错保护),

阿里的Dubbo,微软的.Net体系微服务框架 Service Fabric,再到后来进阶的服务网格(Service Mesh,如 Istio、Linkerd)。

那从12年开始到现在,微服务到底发展到哪个阶段了,在各个阶段的进阶过程中,又有哪些的变化。所以我们需要了解微服务技术的历史发展脉络。

下面的内容参考了 Phil Calçado的文章《Pattern: Service Mesh》,从开发者的视角,详细分析了从微服务到Service Mesh技术的演进过程,这边做了进一步的整理和总结。

3.1 第一阶:简单服务通信模块

这是最初的模样,开发人员最开始的时候想象的两个服务间简单的通信模式,抽象表示如下,两个服务之间直接进行通信:

3.2 第二阶:原始通信时代

上面的方式非常简单,但实际情况远比想象的复杂很多,通信需要底层字节码传输和电子信号的物理层来完成,在TCP协议出现之前,

服务需要自己处理网络通信所面临的丢包、错误、乱序、重试等一系列流控问题,因此服务实现中,除了业务逻辑外,还包含对网络传输问题的处理逻辑。

3.3 第三阶:TCP时代

TCP协议的出现,避免了每个服务自己实现一套相似的网络传输处理逻辑,解决网络传输中通用的流量控制问题。

这时候我们把处理网络传输的能力下沉,从服务的实现中抽离出来,成为操作系统网络层的一部分。

3.4 第四阶:第一代微服务(Spring Cloud/RPC)

TCP出现之后,服务间的网络通信已经不是一个难题了,所以 GFS/BigTable/MapReduce 为代表的分布式系统得到了蓬勃的发展。

这时,分布式系统特有的通信语义又出现了,如服务注册与发现、负载均衡、熔断降级策略、认证和授权、端到端trace、日志与监控等,因此根据业务需求,完成一些通信语义的实现。

3.5 第五阶:第二代微服务

为了避免每个服务都需要自己实现一套分布式系统通信的语义功能,随着技术的发展,一些面向微服务架构的通用开发框架出现了,如Twitter的Finagle、Facebook的Proxygen以及Spring Cloud等,

这些框架实现了分布式系统通信需要的各种通用语义功能:如负载均衡和服务发现等,因此一定程度上屏蔽了这些通信细节,使得开发人员使用较少的框架代码就能开发出健壮的分布式系统。

3.6 第六阶:第一代Service Mesh

上面的第二代微服务框架目前看着挺完美了,但整套微服务框架其实是很复杂的,比如Spring Cloud,聚合了很多组件。所以在实践过程中,会发现有如下诸多问题:

  • 侵入性强。想要集成SDK的能力,除了需要添加相关依赖,业务层中入侵的代码、注解、配置,与治理层界限不清晰。
  • 升级成本高。每次升级都需要业务应用修改SDK版本,重新进行功能回归测试,并对每一台服务进行部署上线,与快速迭代开发相悖。
  • 版本碎片化严重。由于升级成本高,而中间件版本更新快,导致线上不同服务引用的SDK版本不统一、能力参差不齐,造成很难统一治理。
  • 中间件演变困难。由于版本碎片化严重,导致中间件向前演进的过程中就需要在代码中兼容各种各样的老版本逻辑,带着"枷锁”前行,无法实现快速迭代。
  • 内容多、门槛高。依赖组件多,学习成本高,即使通用分布式系统屏蔽了很多的实现细节,我们引入微服务框架并熟练使用也是要花费巨大的精力的。
  • 治理功能不全。不同于RPC框架,SpringCloud作为治理全家桶的典型,也不是万能的,诸如协议转换支持、多重授权机制、动态请求路由、故障注入、灰度发布等高级功能并没有覆盖到。
  • 无法实现真正意义上的语言无关性。提供的框架一般只支持一种或几种语言,要将框架不支持的语言研发的服务也纳入微服务架构中,是比较有难度的。

所以,第一代微服务架构 Service Mesh就产生了,它作为一个基础设施层,能够与业务解耦,主要解决复杂网络拓扑下微服务与微服务之间的通信,其实现形态一般为轻量级网络代理,并与应用以边车代理(SideCar)模式部署,同时对业务应用透明。

SideCar将分布式服务的通信抽象为单独一层,需要和服务部署在一起,接管服务的流量,通过代理之间的通信间接完成服务之间的通信请求。

所以在这一层中它能够实现负载均衡、服务发现、认证授权、监控追踪、流量控制等分布式系统所需要的功能。

如果我们从一个全局视角来看,绿色的为应用服务,蓝色的为SideCar,就会得到如下部署图:

如果我们省略去服务,只看Service Mesh的代理边车的网格应该是这样的:

流量经过的时候,会先被代理边车所劫持,然后再进入服务,所以它就是一个由若干服务代理所组成的错综复杂的网格。

3.7 第七阶:第二代Service Mesh

第一代Service Mesh由一系列独立运行的单机代理服务构成,为了提供统一的上层运维入口,演化出了集中式的控制面板,我们称之为控制面(control plane)。

控制面和所有的数据面(data plane,即代理边车)进行交互,比如策略下发、数据采集等。这就是以Istio为代表的第二代Service Mesh。

只包含控制面和数据面的 Service Mesh 服务网格全局结构图 如下:

从上面的结构图可以看出,Service Mesh 的基础设施层主要分为两部分:控制平面与数据平面。当前流行的开源服务网格 Istio 和 Linkerd 都是这种构造。

控制平面的特点:

  • 不直接解析数据包。
  • 与控制平面中的代理通信,下发策略和配置。
  • 负责网络行为的可视化。
  • 通常提供 API 或者命令行工具可用于配置版本化管理,便于持续集成和部署。

数据平面的特点:

  • 通常是按照无状态目标设计的,但实际上为了提高流量转发性能,需要缓存一些数据,因此无状态也是有争议的。
  • 直接处理入站和出站数据包,转发、路由、健康检查、负载均衡、认证、鉴权、产生监控数据等。
  • 对应用来说透明,即可以做到无感知部署。

到这一步我们大概了解了微服务架构的演进过程,也初步了解Service Mesh技术比较于传统的微服务架构有哪些优势。

1 微服务优势与挑战


1.1 微服务的优势

1.1.1 单一职责

微服务架构中的每个节点高度服务化,都是具有业务逻辑的,符合高内聚、低耦合原则以及单一职责原则的单元,包括数据库和数据模型;

不同的服务通过“管道”的方式灵活组合,从而构建出庞大的系统。

1.1.2 轻量级通信

通过REST API模式或者RPC框架,事件流和消息代理的组合相互通信,实现服务间互相协作的轻量级通信机制。

1.1.3 独立性

在微服务架构中,每个服务都是独立的业务单元,与其他服务高度解耦,只需要改变当前服务本身,就可以完成独立的开发、测试、部署、运维。

1.1.4 进程隔离

在微服务架构中,应用程序由多个服务组成,每个服务都是高度自治的独立业务实体,可以运行在独立的进程中,不同的服务能非常容易地部署到不同的主机上,实现高度自治和高度隔离。

进程的隔离,还能保证服务达到动态扩缩容的能力,业务高峰期自动增加服务资源以提升并发能力,业务低谷期则可自动释放服务资源以节省开销。

1.1.5 混合技术栈和混合部署方式

团队可以为不同的服务组件使用不同的技术栈和不同的部署方式(公有云、私有云、混合云)。

1.1.6 简化治理

组件可以彼此独立地进行缩放,从而减少了因必须缩放整个应用程序而产生的浪费和成本,独立的发布、服务治理。

1.1.7 安全可靠,可维护。

从架构上对运维提供友好的支撑,在安全、可维护的基础上规范化发布流程,支持数据存储容灾、业务模块隔离、访问权限控制、编码安全检测等。

1.2 面临的挑战

1.2.1 分布式固有复杂性

微服务架构是基于分布式的系统,而构建分布式系统必然会带来额外的开销。

  • 性能: 分布式系统是跨进程、跨网络的调用,受网络延迟和带宽的影响。
  • 可靠性: 由于高度依赖于网络状况,任何一次的远程调用都有可能失败,随着服务的增多还会出现更多的潜在故障点。因此,如何提高系统的可靠性、降低因网络引起的故障率,是系统构建的一大挑战。
  • 分布式通信: 分布式通信大大增加了功能实现的复杂度,并且伴随着定位难、调试难等问题。
  • 数据一致性: 需要保证分布式系统的数据强一致性,即在 C(一致性)A(可用性)P(分区容错性) 三者之间做出权衡。这块可以参考我的这篇《分布式事务》。

1.2.2 服务的依赖管理和测试

在单体应用中,通常使用集成测试来验证依赖是否正常。而在微服务架构中,服务数量众多,每个服务都是独立的业务单元,服务主要通过接口进行交互,如何保证它的正常,是测试面临的主要挑战。

所以单元测试和单个服务链路的可用性非常重要。

1.2.3 有效的配置版本管理

在单体系统中,配置可以写在yaml文件,分布式系统中需要统一进行配置管理,同一个服务在不同的场景下对配置的值要求还可能不一样,所以需要引入配置的版本管理、环境管理。

1.2.4 自动化的部署流程

在微服务架构中,每个服务都独立部署,交付周期短且频率高,人工部署已经无法适应业务的快速变化。有效地构建自动化部署体系,配合服务网格、容器技术,是微服务面临的另一个挑战。

1.2.5 对于DevOps更高的要求

在微服务架构的实施过程中,开发人员和运维人员的角色发生了变化,开发者也将承担起整个服务的生命周期的责任,包括部署、链路追踪、监控;因此,按需调整组织架构、构建全功能的团队,也是一个不小的挑战。

1.2.6 运维成本

运维主要包括配置、部署、监控与告警和日志收集四大方面。微服务架构中,每个服务都需要独立地配置、部署、监控和收集日志,成本呈指数级增长。

服务化粒度越细,运维成本越高。

怎样去解决这些问题,是微服务架构必须面临的挑战。

2 微服务全景架构


3 微服务核心组件


微服务架构核心组件包括:

组件名

服务注册与发现

API 网关服务

分布式配置中心

服务通信

服务治理

服务监控

分布式服务追踪

3.1 服务注册与发现

3.1.1 原理图

服务注册与发现三要素:

  • Provider:服务的提供方
  • Consumer:调用远程服务的服务消费方
  • Registry:服务注册和发现的注册中心

3.1.2 注册中心的原理、流程

1、 Provider(服务提供者)绑定指定端口并启动服务

2、提供者连接注册中心,并发本机 IP、端口、应用信息和服务信息发送至注册中心存储

3、Consumer(消费者),连接注册中心 ,并发送应用信息、所求服务信息至注册中心

4、注册中心根据消费者所求服务信息匹配对应的提供者列表发送至Consumer 应用缓存。

5、Consumer 在发起远程调用时基于缓存的消费者列表择其一发起调用。

6、Provider 状态变更会实时通知注册中心、在由注册中心实时推送至Consumer设计的原因:

Consumer 与 Provider 解偶,双方都可以横向增减节点数。注册中心对本身可做对等集群,可动态增减节点,并且任意一台宕掉后,将自动切换到另一台

7、去中心化,双方不直接依赖注册中心,即使注册中心全部宕机短时间内也不会影响服务的调用(Consumer应用缓存中保留提供者 Provider 列表)

8、服务提供者无状态,任意一台宕掉后,不影响使用

注册中心包含如下功能:注册中心、服务注册和反注册、心跳监测与汇报、服务订阅、服务变更查询、集群部署、服务健康状态检测、服务状态变更通知 等

我们有很多种注册中心的技术,Zookeeper、Etcd、Consul、Eureka 4种比较常用,如下

CAP模型CPCPCPAP

数据一致性算法ZABRaftRaft❌

多数据中心❌❌✅❌

多语言支持客户端Http/gRPCHttp/DNSHttp

WatchTCPLong PollingLong PollingLong Polling

KV存储✅✅✅❌

服务健康检查心跳心跳服务状态,
内存,硬盘等自定义

自身监控❌metricsmetricsmetrics

SpringCloud 支持✅✅✅✅

自身开发语言JavaGoGoJava

ZookeeperEtcdConsulEureka

分布式系统中CAP模型3者不可兼得。由于网络的原因,分布式系统中P是必备的,意味着只能选择 AP 或者 CP。CP 代表数据一致性是第一位的,AP 代表可用性是第一位的。

Zookeeper、Etcd、Consul 是 CP 型注册中心,牺牲可用性来保证数据强一致性

Eureka 是 AP 型注册中心,牺牲一致性来保证可用性

3.2 API 网关服务

上面是Api网关服务的基本架构:用户的请求经过统一的Api网关来访问微服务里具体的服务颗粒,并且可能产生串联的链路服务调用。

有很多耳熟能详的API网关技术,比如 Zuul、Kong、Tyk等,提供了服务路由在内的很多通用功能,后面会有专门的章节来说这个。

Tyk:Tyk是一个开放源码的API网关,它是快速、可扩展和现代的。Tyk提供了一个API管理平台,其中包括API网关、API分析、开发人员门户和API管理面板。Try 是一个基于Go实现的网关服务。

Kong:Kong是一个可扩展的开放源码API Layer(也称为API网关或API中间件)。Kong 在任何RESTful API的前面运行,通过插件扩展,它提供了超越核心平台的额外功能和服务。

Netflix zuul:Zuul是一种提供动态路由、监视、弹性、安全性等功能的边缘服务。Zuul是Netflix出品的一个基于JVM路由和服务端的负载均衡器。

除了路由之外,Api网关服务还包含:认证和授权,重试、熔断、降级,负载均衡,日志、监控、链路追踪,灰度发布,ABTesting 等功能。

3.3 配置中心

上面这个是携程的开源配置中心Apollo系统的架构设计,我们从下往上进行分析:

1、Config Service提供配置的读取、推送等功能,服务对象是Apollo客户端

2、Admin Service提供配置的修改、发布等功能,服务对象是Apollo Portal(管理界面)

3、Config Service和Admin Service都是多实例、无状态部署,所以需要将自己注册到Eureka中并保持心跳,支持注册、更新、删除能力

4、在Eureka之上我们架了一层Meta Server用于封装Eureka的服务发现接口

5、Client通过域名访问Meta Server获取Config Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Client侧会做load balance、错误重试

6、Portal通过域名访问Meta Server获取Admin Service服务列表(IP+Port),而后直接通过IP+Port访问服务,同时在Portal侧会做load balance、错误重试

7、为了简化部署,我们实际上会把Config Service、Eureka和Meta Server三个逻辑角色部署在同一个JVM进程中

上面的架构体现了如下特点:

•高可用:配置服务为多实例部署,访问层保证 load balance、错误重试
•弱依赖:使用了Eureka来做配置中心的服务注册,如果出现问题或者网络出现问题的时候,服务应该可以依赖于它本身所缓存的配置来提供正常的服务

3.4 服务通信

分布式系统一般是由多个微服务颗粒组成的,微服务与微服务之前存在互相调用,甚至多个链路访问的情况。所以他们之间是需要通信的,通信方式继承于SOA,包含同步与异步两种模式。

3.4.1 同步访问方式

1、RPC 访问模式

Remote Procedure Call Protocol,远程过程调用协议,一般使用在分布式业务或者微服务架构风格中。像调用本地函数一样,去调用一个远端服务。

本质上是请求链的底层,维护同一个端口,进行socket通信。常见的RPC技术包含 gRPC、Dubbo、Thrift 等。

2、REST 访问模式

这个应该大家最常用,可以通过一套统一风格的接口模式,为Web,iosandroid等提供接口服务。

3.4.2 异步访问方式

消息中间件:RabbitMQ、Kafka、RocketMQ之类,对于实时性要求不那么严格的服务请求和计算。

3.5 服务治理

常见的服务治理手段有如下几种:

3.5.1 节点管理

服务调用失败时可能是服务提供者自身出现,也可能是网络发生故障,我们一般有两种处理手段。

1. 注册中心主动摘除机制
这种机制要求服务提供者定时向注册中心汇报心跳,如果超时,就认为服务提供者出现问题,并将节点从服务列表中摘除。

2. 服务消费者摘除机制
当服务提供者网络出现异常,服务消费者调用就会失败,如果持续错误就可以将它从服务提供者节点列表中移除。

3.5.2 负载均衡

服务消费者在从服务列表中选取可用节点时,如果能让性能较好的服务机多承担一些流量的话,就能充分利用机器的性能。这就需要对负载均衡算法做一些调整。

常用的负载均衡算法主要包括以下几种:

1. Radom 随机算法
从可用的服务节点中随机选取一个节点。一般情况下,随机算法是均匀的,也就是说后端服务节点无论配置好坏,最终得到的调用量都差不多。

2. Round Robin 轮询算法(加权重)
就是按照固定的权重,对可用服务节点进行轮询。如果所有服务节点的权重都是相同的,则每个节点的调用量也是差不多的。但可以给性能较好的节点的权重调大些,充分发挥其性能优势,提高整体调用的平均性能。

3. Least Conn 最少活跃调用算法
这种算法是在服务消费者这一端的内存里动态维护着同每一个服务节点之间的连接数,选择连接数最小的节点发起调用,也就是选择了调用量最小的服务节点,性能理论上也是最优的。

4. 一致性 Hash 算法
指相同参数的请求总是发到同一服务节点。当某一个服务节点出现故障时,原本发往该节点的请求,基于虚拟节点机制,平摊到其他节点上,不会引起剧烈变动。

3.5.3 服务路由

所谓的路由规则,就是通过一定的规则如条件表达式或者正则表达式来限定服务节点的选择范围。

制定路由规则主要有两个原因。

1. 业务存在灰度发布、多版本ABTesting的需求

功能逐步开放发布或者灰度测试的场景。

2. 多机房就近访问的需求

一般可以通过 IP 段规则来控制访问,在选择服务节点时,优先选择同一 IP 段的节点。这个也是算力靠近的优先原则。

3.5.4 服务容错

在分布式系统中,分区容错性是很重要的一个话题,要知道,服务间的调用调用并不总是成功,服务提供者程序bug、异常退出 或者 消费者与提供者之间的网络故障。而服务调用失败之后,我们需要一些方法来保证调用的正常。

常用的方式有以下几种:

FailOver 失败自动切换。就是服务消费者发现调用失败或者超时后,自动从可用的服务节点列表中选择下一个节点重新发起调用,也可以设置重试的次数。

FailBack 失败通知。就是服务消费者调用失败或者超时后,不再重试,而是根据失败的详细信息,来决定后续的执行策略。

FailCache 失败缓存。就是服务消费者调用失败或者超时后,不立即发起重试,而是隔一段时间后再次尝试发起调用。

FailFast 快速失败。就是服务消费者调用一次失败后,不再重试。

服务治理的手段是从不同角度来确保服务调用的成功率。节点管理是从服务节点健康状态角度来考虑,负载均衡和服务路由是从服务节点访问优先级角度来考虑,而服务容错是从调用的健康状态角度来考虑。

3.6 服务监控

常见的开发监控报警技术有 ELK、InfluxData的TICK、Promethues 等。

在分布式系统中,微服务一般都具有复杂的链路调用,对于链路之间的状态、服务可用性、调用情况的监控,是需要一套完整的服务监控系统去保障的。

如我们上面的那个图所示, 服务系统主要由哪几部分构成:

1、数据采集部分,包含性能指标信息、日志信息(一般是服务埋点日志或者sidecar的inbound、outbound信息)、端到端的Trace信息。

2、采集上来的监控数据通过传输系统,或者使用消息中间件来异步传输,或者调用服务端接口推送监控数据。并把这些数据持久化到我们的数据服务层中。

3、制定一套规则,对于采集到的数据进行清理、计算、分级等,处理好的数据,通过提前设置好的报警策略,来判断它是否触发了这些报警。

4、梳理完的数据可以进行查询展示(有一个日志查询界面)、分级报警、分析趋势报表推送等。

3.7 服务追踪

服务追踪的原理主要包括下面两个关键点。

1、为了实现请求跟踪,当请求发送到分布式系统的入口端点时,只需要服务跟踪框架为该请求创建一个唯一的跟踪标识,同时在分布式系统内部流转的时候,框架始终保持传递该唯一标识,直到返回给请求方为止,这个唯一标识就是前文中提到的 Trace ID。

通过 Trace ID 的记录,我们就能将所有请求过程的日志关联起来。

2、为了统计各处理单元的时间延迟,当请求到达各个服务组件时,或是处理逻辑到达某个状态时,也通过一个唯一标识来标记它的开始、具体过程以及结束,该标识就是前文中提到的 Span ID。对于每个 Span 来说,它必须有开始和结束两个节点,

通过记录开始 Span 和结束 Span 的时间戳,就能统计出该 Span 的时间延迟,除了时间戳记录之外,它还可以包含一些其他元数据,比如事件名称、请求信息等。

上图显示了Trace ID 和 Spand ID 在链路中的传输过程,它把服务调用的一个时序结构给展现出来了。

常见的服务链路追踪的技术有Zipkin、Pinpoint、SkyWalking 等。后面讲到Service Mesh的时候会详细说下Zipkin的x-b3 header头传递,以及流量染色的使用,非常给力。

由于篇幅有限。我就不全部展示出来了 ,小伙伴们有兴趣想了解内容和更多相关学习资料的请点赞收藏+评论转发+关注我,后面会有很多干货。我有一些面试题、架构、设计类资料可以说是程序员面试必备!所有资料都整理到网盘了,需要的话欢迎下载!私信我回复【999】即可免费获取

微服务的拆分策略

文章目录

什么是架构的风格?

架构风格根据结构组织模式定义了一系列系统,更具体地说,架构风格确定可以在该风格的实例中使用的组件和连接器的词汇表,以及关于如何组合它们的一组约束。特定的架构风格提供了有限的元素(组件)和关系(连接器),可以从中定义应用程序架构的视图,应用程序通常使用多种架构风格的组合。例如,单体架构是如何将实现视图构造为单个(可执行与可部署)组件的架构样式。微服务架构将应用程序构造为一组松散耦合的服务。

分层式架构风格

架构的典型例子是分层架构,分层架构将软件元素按“层”的方式组织,每层都有明确定义的职责,分层架构还限制了层之间的依赖关系,每一层只能依赖于紧邻其下方的层(如果严格分层)或其下面的任何层。

流行的三层架构是应用于逻辑视图的分层架构。它将应用程序的类组织到以下层中∶

  • 表现层:包含实现用户界面或外部API的代码。
  • 业务逻辑层:包含业务逻辑。
  • 数据持久化层:实现与数据库交互的逻辑。

分层架构是架构风格的一个很好的例子,但它确实有一些明显的弊端∶

  • 单个表现层:它无法展现应用程序可能不仅仅由单个系统调用的事实。
  • 单一数据持久化层:它无法展现应用程序可能与多个数据库进行交互的事实。
  • 将业务逻辑层定义为依赖于数据持久化层:理论上,这样的依赖性会妨碍你在没有数据库的情况下测试业务逻辑。

六边形架构风格

六边形架构是分层架构风格的替代品。如图下图所示,六边形架构风格选择以业务逻辑为中心的方式组织逻辑视图。应用程序具有一个或多个入站适配器,而不是表示层,它通过调用业务逻辑来处理来自外部的请求。同样,应用程序具有一个或多个出站适配器,而不是数据持久化层,这些出站适配器由业务逻辑调用并调用外部应用程序。此架构的一个关键特性和优点是业务逻辑不依赖于适配器。相反,各种适配器都依赖业务逻辑。

  • 业务逻辑具有一个或多个端口(port),端口定义了一组操作,关于业务逻辑如何与外部交互。有两种端口:入站和出站端口。入站端口是业务逻辑公开的 API,它使外部应用程序可以调用它。入站端口的一个实例是服务接口,它定义服务的公共方法。出站端口是业务逻辑调用外部系统的方式。出站端口的一个实例是存储库接口,它定义数据访问操作的集合。
  • 业务逻辑的周围是适配器。与端口一样,有入站和出站两种类型的适配器。 入站适配器通过调用入站端口来处理来自外部世界的请求。入站适配器的一个实例是 Spring MVC Controller,它实现一组 REST 接口(endpoint)或一组 Web 页面。另一个实例是订阅消息的消息代理客户端。多个入站适配器可以调用相同的入站端口。出站适配器实现出站端口。并通过调用外部应用程序或服务处理来自业务逻辑的请求。出站适配器的一个实例是实现访问数据库的操作的数据访问对象(DAO)类。另一个实例是调用远程服务的代理类。出站适配器也可以发布事件。
  • 六边形架构风格的一个重要好处是它将业务逻辑与适配器中包含的表示层和数据访问层的逻辑分离开来。业务逻辑不依赖于表示层逻辑或数据访问层逻辑。
  • 由于这种分离,单独测试业务逻辑要容易得多。另一个好处是它更准确地反映了现代应用程序的架构。可以通过多个适配器调用业务逻辑,每个活配器实现特定的 API 或用户界面。业务逻辑还可以调用多个适配器,每个适配器调用不同的外部系统。六边形架构是描述微服务架构中每个服务的架构的好方法。

分层架构和六边形架构都是架构风格的实例。每个都定义了架构的构建块(元素),并对它们之间的关系施加了约束。六边形架构和分层架构(三层架构)构成了软件的逻辑视图。现在让我们将微服务架构定义为构成软件的实现视图的架构风格。

微服务架构是一种架构风格

单体架构和微服务架构都是架构风格。

  • 单体架构是一种架构风格,它的实现视图是单个组件∶单个可执行文件或 WAR 文件。这个定义并没有说明其他的视图。例如,单体应用程序也可以具有六边形架构风格的逻辑视图。
  • 微服务架构也是一种架构风格。它的实现视图由多个组件构成∶一组可执行文件或 WAR 文件。它的组件是服务,连接器是使这些服务能够协作的通信协议。每个服务都有自己的逻辑视图架构,通常也是六边形架构。如下图,此架构中的服务对应于业务功能,例如订单管理和餐馆管理等。
  • 微服务架构强加的一个关键约束是服务松耦合。因此,服务之间的协作方式存在一定限制。

为应用程序定义微服务架构

根据业务能力进行服务拆分

创建微服务架构的策略之一就是采用业务能力进行服务拆分。业务能力是一个来自于业务架构建模的术语。业务能力是指一些能够为公司(或组织)产生价值的商业活动。特定业务的业务能力取决于这个业务的类型。例如,在线商店的业务能力包括:订单管理、库存管理和发货等等。

根据子域进行服务拆分

领域驱动设计(Domain Driven Design,DDD)是构建复杂软件的方法论,这些软件通常都以面向对象和领域模型为核心。领域模型以解决具体问题的方式包含了一个领域内的知识。它定义了当前领域相关团队的词汇表,DDD 也称之为通用语言。领域模型会被紧密地映射到应用的设计和实现环节。在微服务架构的设计层面,DDD 有两个特别重要的概念,子域和限界上下文。

  • 领域驱动为每一个子域定义单独的领域模型。子域是领域的一部分,领域是 DDD 中用来描述应用程序问题域的一个术语。识别子域的方式跟识别业务能力一样:分析业务并识别业务的不同专业领域,分析产出的子域定义结果也会跟业务能力非常接近。例如外卖软件的子域包括:订单获取、订单管理、餐馆管理、送餐和会计等。
  • DDD 把领域模型的边界称为限界上下文(bounded context)。限界上下文包括了实现这个模型的代码集合。当使用微服务架构时,每一个限界上下文对应一个或者一组服务。换一种说法,我们可以通过 DDD 的方式定义子域,并把子域对应为每一个服务,这样就完成了微服务架构的设计工作。

拆分的指导原则

单一职责原则(SRP),软件架构和设计的主要目标之一是确定每个软件元素的职责。

单一职责原则如下:改变一个类应该只有一个理由。

  • 类所承载的每一个职责都是对它进行修改的潜在原因。如果一个类承载了多个职责,并且互相之间的修改是独立的,那么这个类就会变得非常不稳定。遵照 SRP 原则,你所定义的每一个类都应该只有一个职责,因此也就只有一个理由对它进行修改。
  • 我们在设计微服务架构时应该遵 SRP 原则,设计小的、内聚的、仅仅含有单一职责的服务。这会缩小服务的大小并提升它的稳定性。

另外一个有用的原则是闭包原则(CPP):在包中包含的所有类应该是对同类的变化的一个集合,也就是说,如果对包做出修改,需要调整的类应该都在这个包之内。

  • 这就意味着,如果由于某些原因,两个类的修改必须耦合先后发生,那么就应该把它们放在同一个包内。这样做的目标是当业务规则发生变化时,开发者只需要对一个交付包做出修改,而不是大规模地修改(和重新编译)整个应用。采用闭包原则,极大地改善了应用程序的可维护性。
  • 在微服务架构下采用 CCP 原则能够把根据同样原因进行变化的服务放在一个组件内。这样做可以控制服务的数量,当需求发生变化时,变更和部署也更加容易。理想情况下,一个变更只会影响一个团队和一个服务。CCP 是解决分布式单体这种可怕的反模式的法宝。

拆分单体应用为服务的难点

从表面上看,通过定义与业务能力或子域相对应的服务来创建微服务架构的策略看起来很简单。但是,你可能会遇到几个障碍∶

  • 网络延迟。
  • 同步进程间通信导致可用性降低。
  • 在服务之间维持数据一致性。
  • 获取一致的数据视图。
  • 上帝类阻碍了拆分。

让我们来看看每个问题,先从网络延迟开始。

网络延迟

网络延迟是分布式系统中一直存在的问题。你可能会发现,对服务的特定分解会导致两个服务之间的大量往返调用。有时,你可以通过实施批处理 API 在一次往返中获取多个对象,从而将延迟减少到可接受的数量。但在其他情况下,解决方案是把多个相关的服务组合在一起,用编程语言的函数调用替换昂贵的进程间通信。

同步进程间通信导致可用性降低

另一个需要考虑的问题是如何处理进程间通信而不降低系统的可用性。例如,实现 createorder() 操作最常见的方式是让 Order Service 使用 REST 同步调用其他服务。这样做的弊端是 REST 这样的协议会降低 Order Service 的可用性。如果任何一个被调用的服务处在不可用的状态,那么订单就无法创建了。有时候这可能是一个不得已的折中,但是在学习异步消息之后,你就会发现其实有更好的办法来消除这类同步调用产生的紧耦合并提升可用性。

在服务之间维持数据一致性

  • 另一个挑战是如何在某些系统操作需要更新多个服务中的数据时,仍|日维护服务之间的数据一致性。例如,当餐馆接受订单时,必须在Kitchen Service 和 Delivery Service 中同时进行更新。Kitchen Service会更改 Ticket 的状态。Delivery Service 安排订单的交付。这些更新都必须以原子化的方式完成。
  • 传统的解决方案是使用基于两阶段提交(two phase commit)的分布式事务管理机制。但对于现今的应用程序而言,这不是一个好的选择,你必须使用一种非常不同的方法来处理事务管理,这就是 Saga。Saga 是一系列使用消息协作的本地事务。Saga 比传统的 ACID 事务更复杂,但它们在许多情况下都能工作得很好。Saga 的一个限制是它们最终是一致的。如果你需要以原子方式更新某些数据,那么它必须位于单个服务中,这可能是分解的障碍。

获取一致的数据视图

分解的另一个障碍是无法跨多个数据库获得真正一致的数据视图。在单体应用程序中,ACID 事务的属性保证查询将返回数据库的一致视图。相反,在微服务架构中,即使每个服务的数据库是一致的,你也无法获得全局一致的数据视图。如果你需要一些数据的一致视图,那么它必须驻留在单个服务中,这也是服务分解所面临的问题。

上帝类阳阻碍了拆分

分解的另一个障碍是存在所谓的上帝类。上帝类是在整个应用程序中使用的全局类。上帝类通常为应用程序的许多不同方面实现业务逻辑。它有大量字段映射到具有许多列的数据库表。大多数应用程序至少有一个这样的上帝类,每个类代表一个对领域至关重要的概念:银行账户、电子商务订单、保险政策等等。因为上帝类将应用程序的许多不同方面的状态和行为捆绑在一起,所以将使用它的任何业务逻辑拆分为服务往往都是一个不可逾越的障碍。


《微服务架构设计模式》第二章 微服务的拆分策略

以上是关于微服务拆分策略的主要内容,如果未能解决你的问题,请参考以下文章

java微服务分布式调用链APM监控

微服务的拆分策略

微服务的拆分策略

微服务的拆分策略

不要在微服务架构中使用单一数据库

学习笔记:《微服务架构设计模式》之服务的拆分策略