【中】微服务治理
提示
服务治理本质是解决"服务多了之后怎么管"的问题,按实际落地分成四层来看:拆 → 联 → 稳 → 观。
一、拆:服务的拆分
服务大致分成三种类型:基础服务、业务服务、聚合服务,对它们的理解如下。
不依赖业务服务的业务逻辑。允许它调用其他基础服务或读取基础数据,但不应依赖业务服务的业务规则
- 网关服务
- 基础配置服务(提供基础的字典等查询)
- 鉴权服务(提供登录、鉴权)
- 消息服务(message)
- 通用组件服务(比如生成pdf、生成短链)
有自己单独的业务表,系统运行的时候产生自己的业务数据,提供业务数据查询,也会查询其他服务
- 用户服务
- 会员服务
- 套餐服务
- 收营台服务
- 业务订单服务
聚合服务分两种
BFF 层((Backend for Frontend,面向前端的后端),读多写少、面向查询的数据组装):客户端聚合、xxx端聚合 → 职责是接口适配、数据裁剪、多服务数据组装
- 客户端聚合服务
- 管理后台聚合服务 (最常见的BFF 层)
流程编排层(有跨域写入操作的流程串联):就诊旅程、报告中心 → 职责是跨域业务流程的串联
- 聚合订单服务(这个和上面的订单还不一样,上面的是一个订单,所有数据扭转都在这个服务。这个是有很多类型的订单区别很大,每个订单都有各自的业务逻辑,想要在一起按照时间展示,因为分页的原因也会创建一个表来聚合这些数据,但本质上它还是一个聚合层不产生数据只是聚合各种数据。但和BFF不一样,BFF可能提供很多业务聚合接口,而聚合订单只提供订单的接口它有自己的表)
服务间解耦问题
- 分清上下游服务,比如订单可以调 用户、会员,但是反过来不行。
- 实在有耦合问题,通过聚合服务或MQ解决。
BFF 理解
BFF(Backend for Frontend,面向前端的后端):业界的通用模式,由 Sam Newman 于 2015 年提出。核心思路是不同的前端端(App、小程序、H5、管理后台)各自对应一个专属的后端服务,只负责给这个端做数据组装、接口裁剪、协议适配,不承载业务逻辑。
BFF 拆分的核心原因不是因为接口复杂,而是因为不同端对同一业务的数据需求不同。App 首页需要 5 个服务的汇总数据,管理后台可能只需要其中 2 个服务的详细字段。如果不拆 BFF,要么所有端共享一个服务、接口越来越臃肿,要么每个业务服务为不同端维护多套接口。
扩展:服务拆分的依据 DDD(领域驱动设计)
服务拆分不是按表拆、按模块拆,而是按业务域拆。DDD 提供了一套方法论来指导这件事。
核心思想:先搞清楚业务是什么,再决定代码怎么写。传统开发是"需求 → 建表 → 写接口",数据库驱动一切;DDD 是"需求 → 理解业务域 → 划分边界 → 设计模型 → 最后才落库",业务领域知识才是核心,技术只是手段。
关键概念:
- 限界上下文(Bounded Context):一个业务域就是一个独立的上下文,有自己的模型和语言。比如"套餐"在会员域关注的是权益和有效期,在预约域关注的是能不能用这个套餐挂号。拆成独立服务后各自维护自己的模型,互不污染。
- 通用域(Generic Subdomain):不属于核心业务,但所有域都需要的能力,比如鉴权、消息、配置。抽取成基础服务供上层统一调用。
- 上下文映射(Context Map):不同限界上下文之间怎么通信。对应到我们的架构就是聚合服务——把多个域的数据组装成前端需要的视图,或者在跨域流程中做编排。
- 领域事件(Domain Event):一个域里发生了某件事,通知其他域。比如预约域发出"预约成功事件",会员域监听后扣次数,消息域监听后发通知。这就是用 MQ 解耦的理论基础。
- 聚合根(Aggregate Root):一个限界上下文内部的一致性边界。外部只能通过聚合根操作内部数据,不能绕过它直接改。比如预约域里聚合根是"预约单",下面有预约明细、就诊人信息,外部修改必须通过预约单来操作。
二、联:拆分后怎么关联起来提供数据
服务拆分后会带来下面的问题,解决了之后数据自然就连接上了。
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 服务注册与发现 | 服务实例动态变化,调用方需要实时感知上下游的存活状态和地址。 | Nacos |
| 服务间远程调用 | 原来的本地方法调用变成网络调用,引入超时、重试、序列化等网络层面的问题。 | Dubbo |
| 负载均衡 | 同一服务多实例部署时,需要决定请求打到哪一台,并保证各实例负载均衡。 | Dubbo |
| 分布式事务 | 原来一个本地事务能保证的一致性,现在跨了多个服务,需要保证数据一致性。 | 1. MQ的重试 2. Dubbo 重试 3. 异常通知人工接入处理 |
| 配置管理 | 几十个服务各自有配置,需要统一管理、动态更新,并且不重启生效。 | Nacos |
| 分布式任务 | 多个pod,但定时任务只能运行一次 | Xxl-Job |
注:每种问题的解决方案和中间件都有很多,这里只列我常用的
三、稳:系统的可用性保障
如果某个服务挂了、或者流量突然飙升,怎么保证整个系统不崩?怎么防止故障向上游蔓延导致雪崩。
| 防护手段 | 说明 | 对应工具 |
|---|---|---|
| 限流 | 不该进来的流量挡在外面 | Sentinel |
| 熔断 | 下游挂了别拖着上游一起死 | Sentinel |
| 降级 | 不可用的时候给个兜底方案 | Sentinel |
| 超时控制 | 别无限等待 | Dubbo(RPC 超时)/ Feign、RestTemplate(HTTP 超时) |
分层防护的整体思路:这三者的关系不是只做一个就够的,而是多层叠加,形成纵深防御:
- 外层网关限流:挡住超出系统容量的流量
- 中间层熔断:某个服务出问题及时隔离
- 内层降级:即使熔断了也有兜底方案
注:实际上目前几乎没做任何防御~
四、观:监控分析、告警
系统跑起来了,怎么知道它健不健康、出了问题能不能快速定位。
| 问题 | 说明 | 方案 |
|---|---|---|
| API 网关 | 怎么知道请求是否进来了?怎么知道什么时候的请求量比较大?怎么知道某次请求的耗时? | API 网关 (nginx、ingress、kong) |
| 日志统一采集观测 | 服务报错了,怎么查看日志? | 服务中的日志统一采集观测:SLS |
| 全链路跟踪 | 微服务调用,一次请求横跨多个服务,怎么把相关日志关联起来?出现慢接口,到底是链路中哪个环节出了问题? | skywalking、腾讯云/阿里云日志追踪 |
| 流量治理 | 服务上线发布时怎么做灰度引流,怎么让一部分流量打新版本验证 | |
| 异常监控 | 1、中间件监控怎么办(cpu、mq 堆积、内存、数据库大数据量查询) 2、系统出现了某个异常,虽然打印了error但是无法立即感知 | 1、配置服务器异常指标策略 2、配置日志关键字告警策略 3、通知钉钉、飞书等群告警 |
系统上线了,告警也配置了,还可以怎么去优化系统呢? 出现告警说明问题以及产生了,再去处理是问题后制,比如一个慢接口,虽然它慢,但平时也没什么问题,因为访问的人少,如果流量一下来了,告警来了也不太好及时处理。
- 对于系统整体的健康,可以网关出发,每周预留一点时间,去健康网关层面是否有慢接口
- 对于新上线的服务,在线上的一周或几周,可以通过云监控去看这个单独服务的运行情况
