微服务架构设计
1. 微服务中的“微”是什么
Jeff Bezos双披萨原则:建议保持小规模的团队
对于微服务的划分,什么粒度才合适呢?并没有太标准的答案,这个还是要根据具体的用户来确定,如果用户少,单体应用可能是比较不错的选择,微服务的架构在这个时候返回会增加成本带来很多其他的问题
2. 微服务的第一印象
微服务的第一个印象:拆(拆什么,怎么拆)
单一职责:识别出核心的主链路,对主链路进行拆分即可
研发团队的赋能:做好微服务的前提
可独立部署:是微服务系统应用部署架构的前提条件
3. 为什么要将应用微服务化
3.1. 数据访问的杂乱问题
同一个war包内,在数据访问上没有划分任何的领域,对于User、Product、Order表,不同的Service都可以对其进行直接的访问,这种做法有以下几个明显问题
-
数据模型变更带来问题
对于传统应用结构,只要数据库结构变更都会带来业务系统的变动,可用通过微服务架构将各个服务做好隔离,将Data Module相应的影响降到最低,划分好领域模型后,上下游服务只要对接微服务接口就可以,领域模型驱动不依赖于底层数据结构的变更的
-
底层组件变更
比如原来使用的是oracle,现在要改成mysql,底层数据驱动的组件肯定要变,如果我们使用领域模型,微服务架构,只需要更改底层的驱动模块即可,对于业务的影响将非常小
3.2. 代码复用带来的维护成本
整个应用的代码参杂在一块,领域模型非常不清晰,互相调用,互相依赖
- 总觉得别人的代码不好,自己rewrite一个,就导致了大量的过期代码,其他人在调用的使用如果不知道最新的方式和实现很容易导致出现问题
- 总是认为小的改动不会有什么影响,接到一个需求发现改动之前的一个方法就ok了,就直接修改了,很容易影响其他业务
3.3. 快速上线抢占时间窗口
面对传统的单war包的应用,产品的发布节奏肯定是跟上不的,尤其对于“糙快急”的开发状态,在这种节奏下传统的项目模型和架构肯定不适用了
快速迭代
传统的项目每次上线有可能需要经历一个较长的回归测试过程。对于微服务架构这类问题将会得到极大的解决,每个微服务模块的职责边界非常清晰,规模可控便于快速变更和测试,完全可以让团队自己制定发布窗口,即便是上线前的回归测试也只局限在当前服务模块即可。
异常回滚
回滚对于传统的单war包分布式系统来说都是噩梦,无论是版本发布的窗口还是回滚操作上都要进行一系列的处理,在微服务的架构中,回滚只局限在某个微服务范围内,只要把有问题的应用回滚掉即可,不会影响上下游的其他应用
4. 微服务的拆分原则
微服务的拆分没有一个绝对正确的方案,服务的拆分粒度完全要根据企业的场景来规划,而随着业务的发展,原先的架构都要做出相应的调整。根据系统当前业务的结构来进行拆分和重构,一般来讲有以下几种方式
4.1. 压力模型拆分
压力模型简单来说其实就是用户访问量,我们要识别出某些高并发量的业务,尽可能的把这些业务独立拆分出来,这么做的原因很简单,如果不拆分,高压力的业务会导致整个服务都挂掉,肯定不希望某个业务压力影响到其他的用户业务场景
举两个例子:
- 秒杀:一个典型的低频突发流量的场景
- 商品的详情页:高频大流量的场景
在做具体规划的时候,尽量把压力模型拆解为三个维度
- 1)高频高并发场景:比如商品详情页
- 2)低频突发流量场景:比如上面提到的秒杀,虽然不是高频场景(偶尔发生),产生的突发流量会非常高,还有一种管理端的情况,比如平台类的系统,如果一次要将商品或货物上传到平台的所有店铺,就会有一个后端的突发流量发起
- 3)低频流量场景:这一类多为后台运营团队的服务接口,比如商品图文编辑,添加新的商品的优惠计算规则,上架新的商品。发生的频率低,而且不会造成很高的并发量
通常我们建议将高频高并发的场景隔离出来,单独作为一个微服务模块,最好在服务器的物流设备上做隔离,典型的就是电商的商品详情页,对于低频突发流量的场景,如果条件允许页可以剥离出来组成单独的模块,如果必须和其他业务包在一个微服务下,一定要做好流控措施(最典型的就是削峰和限流方法),对于低频突发流量的异常情况还要做好补偿,对于低频流量的场景,根据业务模型进行切分即可。
4.2. 业务模型拆分
业务模型拆分的维度有很多,我们在实际项目中应该综合各个不同的维度做考量
4.2.1. 主链路拆分
在电商领域“主链路”是一个很直接的业务链条,就是指业务从商品访问到下单支付成功的过程,这个就是系统模型中的主链路,可以简单的形成一个链条:商品搜索(列表页导航)->商品详情页->购物车模块->订单结算->支付业务,这就是一条最简单的主链路。
比如隐藏的核心主链路,下单前的营销优惠结算,也会影响到订单的生成,再比如用户地址的填写模块,如果填写或匹配错误也会导致物流配送失败,会导致退单的情况
这里建议将核心主链路进行拆分的时候,有以下这个点需要思考
- 1)异常容错:为主链路建立层次化的降级策略(多级降级),以及合理的熔断策略,这都非常重要,后面会通过Hystrix的学习带给大家
- 2)资源调配:对与主链路通常来讲都是高频场景,自然需要更多的计算资源,最主要的体现就是集群里的主链路机器数量会比较多。
- 3)服务隔离:主链路是应对流量的,一定要把辅助业务和主链路的业务分开,避免边缘服务的异常导致主链路受到影响
4.2.2. 领域模型拆分
领域驱动设计DDD(Domain-Driven Design 领域驱动设计)不是一个新的概念。
其实领域模型是一个很简单的概念,将系统的各个公共服务抽取出来放到一个地方进行统一管理,管理的这个内容就可以形成业务/技术中台,将这个平台输出给公司的各个业务系统进行使用,各个业务系统也需要根据业务的不同模型或范围进行拆分,按照业务域的不同进行划分。
从上面的说明不难看出,其实领域模型就是一套各司其职的服务集合,这里涉及到领域的拆分和合并。
4.2.3. 用户群体拆分
根据用户群体拆分,我们首先要了解自己的系统业务里有哪些用户,比如电商领域,我们有2C的小卖家,也有2B的大客户,还有后端的服务人员,对于不同的用户群体来说,即便是相同的业务领域,也应该有独有的业务场景
根据不同的业务的访问范围形成不同的业务域,根据这些业务域来做拆分
4.2.4. 前后台业务分离
对于前端用户访问的系统和页面,对于后端用户进行操作的管理页面都需要进行业务和数据的隔离,互相不应该影响到对方,如果将前端用户访问的数据库和后端管理访问的数据库放在同一个上应用,这里就需要将两个应用的数据库进行分离并通过数据通道进行数据的交互
5. 如何权衡微服务的利弊
任何一项新的技术或设计思想都是有利弊的,一定不会是十全十美的
微服务架构设计的优点
- 单一职责,独立部署,快速响应变更
- 边界清晰,不过度受制于技术栈,可以独立拓展
- 更加精细粒度的业务控制,对某个具体的业务进行熔断降级,实现局部限流
- 不依赖于数据模型,而是面向业务和领域模型的,数据模型只会存放在当前的微服务领域中并不会共享给其他服务(传统应用是直接访问数据库的),对外只提供业务接口调用即可,更加有利于抽象和屏蔽底层差异。可以通过微服务架构将各个公共领域的业务模型抽象成一系列的中台服务,对其他模块业务服务提供支持,便于新的项目快速落地。
微服务架构设计的缺点
- 部署结构复杂,模块众多,每一个微服务模块的结构都有可能不一样,而之前的集群架构都是镜像复制方式,部署非常简单
- 依赖微服务的组件,依赖平台支撑,对于研发人员的技术要求比较高
- 分布式问题,事务一致性问题
- 由于架构设计人员对于业务的理解程度不同,对系统的拆分水平不一致导致拆方法粒度过粗或过细都降级了微服务架构的优越性
通过对于微服务的优缺点理解可以得出:
业务规模,也就是成本和收益的博弈过程决定了我们是否做微服务改造
业务架构和领域模型的理解决定了我们怎么做微服务,如何拆分和拆分粒度的大小
6. 微服务架构所面临的技术问题
6.1. 服务治理和负载均衡
在整个服务集群中,哪些服务是可以提供对外支持的,哪些已经无法使用了,这些都需要第一时间获得,还需要对服务进行扩容,缩容,以及微服务架构中每个服务的节点状态变化,这都是需要进行管理和协作的,这个就叫做服务治理
与服务搭档的还有负载均衡,才能对服务集群的机器进行访问上的管理
Eureka:服务治理(Alibaba Nacos)
Ribbon:负载均衡
Feign:服务远程调用,就像一个本地接口调用
6.2. 服务容错
比如一些服务在运行的过程中直接就挂掉了,其他调用方刚好调用到这个服务,这个时候就会直接得到错误的Response,如果这种情况出现在分布式集群中,很容易导致服务的雪崩,这种情况在微服务中可以通过熔断降级的方式来解决
Hystrix:熔断降级(Alibaba Sentinel)
6.3. 配置管理
对于单机配置,更改比较方便,但是在集群服务中,配置修改就会比较麻烦,修改的及时性和修改后立即生效都是一个需要解决的问题,对于这种集群化的服务配置一定是中心化管理,在微服务中可以通过服务配置管理来解决这个问题
Config:分布式配置中,可以管理应用中的配置项并可以在运行时刷新(Nacos)
Bus:消息总线,通常是和Config搭配使用,用来批量推送配置项的更新
6.4. 服务网关
对于微服务后端众多的服务的访问,需要有一个统一的管理入口来进行服务调用的录用,这就是服务网关
Gateway:服务网关(Zuul)
6.5. 调用链路追踪
如果出现服务之间调用的追踪错误,如何跟踪呢?
Sleuth:调用链追踪,串联调用上线游服务
6.6. 消息驱动
我们的微服务之间的调用是通过消息队列来进行服务的结耦的,但是消息队列的底层需要依赖和支持消息队列本身的驱动,如果我们更换消息队列,所有的服务调用端都需要更改,这就对我们系统带来不小的改动,可以通过微服务的消息驱动来解决
Stream:消息驱动,屏蔽消息中间件的调用差异
6.7. 限流
限流在微服务里依然需要进行考虑,可以通过微服务提供的相关技术实现限流
Gateway(redis)实现限流
Sentinel:实现限流