对微服务架构设计实践中若干问题的探讨
今天讨论下在微服务架构实践中经常遇到的一些问题的思考,其中有些来源于我们自己的微服务改造项目,有些来源于客户现场微服务架构实施项目和售前方案沟通。
本文仅针对关键问题点进行讨论,具体如下:
微服务下数据库划分
微服务开发和技术框架的选择
微服务和 DevOps,容器云集成
微服务网关和注册中心
微服务相关关键技术,如限流熔断,安全,服务链监控等
对于微服务相关的基础知识,大家在网上基本都可以搜索到就不再重复叙述,对于 SOA 和微服务,中台架构等的区别等,也可以参考我前面发布过的文章。
数据库拆分是否和微服务必须 1 对 1
在最早谈微服务架构的时候我们就谈到。要确保每个微服务都独立自治和松耦合,那么微服务从数据库到逻辑层到前台全部都要进行纵向拆分。
即数据库的拆分是整个微服务架构设计中的一个关键内容。
而这里有一个关键思考就是,在微服务实践中你会看到,实际上你上层的微服务组件的拆分相当来说会更加细,一个不算复杂的业务系统拆分到 20 到 30 个微服务组件都是正常情况。
而对于数据库也拆分为 20 个独立的 DataBase 显然不合理。
这个一方面是增加了数据库本身的管理复杂度,同时由于数据库的太细拆分也引入了更多的分布式事务处理问题,跨库数据关联查询不方便等问题。因此在这里,最好的建议是我们引入一个业务域的概念,即:
可以按业务域来进行数据库拆分,每一个业务域相当独立并对应一个独立数据库,但是业务域里面本身可以有多个上层的微服务模块组件。同一个业务域里面的微服务仍然通过注册中心进行访问和调用。
即同一个业务域里面的微服务在逻辑层本身还是解耦的,只能够通过 API 接口访问调用,以方便分布式部署,但是数据库层本身不拆分,共享同一个数据库。
比如在我们的项目里面,我们会将 4A 和流程引擎两个微服务共享一个数据库,将费用报账,差旅报账,借款报账等独立微服务共享一个报账数据库。
在这种方式下虽然没有实现数据库的彻底解耦,但是通过在逻辑层的微服务拆分和解耦,我们可以实现微服务部署包的更加细粒度管理。同时当存在业务逻辑变更的时候,我们也仅仅需要变更相应的微服务模块,做到变更影响最小化目的。
是否使用 SpringCloud 全家桶
可以看到如果采用 SpingCLoud 微服务技术开发框架,那么对应服务注册中心,限流熔断,微服务网关,负载均衡,配置中心,安全,声明式调用所有能力全部提供。
你需要做的就是采用 SpringBoot 框架来开发一个个微服务组件即可。
当然在我们实施的项目里面也存在另外一种方式,即只使用 SpringBoot 框架进行单个微服务组件的开发,其次再去组合和集成当前业界主流的微服务开源技术组件产品。
服务注册中心:
服务配置中心:
限流熔断:
服务链监控:
API 网关:采用 Kong 网关来实现 API 集成和管控治理
当然你也可能完全不采用 SpringBoot,而是走更加高效的支持 RPC 调用的 Dubbo 开源框架进行微服务组件的开发和集成。
如果从微服务技术平台的构建和快速开发上来说,当然是你直接选择 SpingCLoud 整个开源框架和组件来实现最简单,而且基本也完全能够满足需求。对于日常传统企业应用来说,性能完全足够,也不存在说性能无法满足的情况。毕竟不是所有项目都类似互联网存在海量数据访问和大并发下的高性能要求。
如果采用各种开源组件,技术框架自己集成,那么肯定是存在前期的基础技术平台搭建,集成验证等相关的工作量。同时本身也会增加整个基础架构的复杂度。比如你采用了 Nacos 注册中心,对于注册中心你同样需要去进行集群化配置,以满足高可用性的需求。
综合以上描述,简单总结就是:
如果图省事,没有太高性能要求,直接采用 SpingCloud 整体框架
如果性能要求高,自己技术储备足够,可以自己进行开源技术组件集成
那么在我们实际的微服务架构实施项目里面,我们会看到第三个场景。
比如一个集团型企业,本身一个计划管理系统进行前期架构设计后拆分为 10 个微服务模块,需要招标三家软件开发商来定制开发。对开发商要求也是进行微服务架构化开发。
在这个时候我们就发现一个关键问题,各个厂家自己采用微服务架构没有关系,但是从整个大应用角度我们实际上是存在对 10 个微服务模块进行统一的微服务治理和管控需求的。类似 API 网关,类似服务配置中心等。
而这些组件就不太适合再使用 SpingCLoud 里面的技术组件,而是需要从单个微服务架构体系里面提取出来,形成一个共享的服务能力。在这种时候,我们的建议就是尽量去集成和使用第三方的其它开源技术组件来进行管控治理。
比如上面这个例子,三家供应商可以保留最基础的配置。
即某家供应商开发 A、B、C 三个微服务模块的时候,可以启用 Eureka+Feign+Ribbon 来完成自己开发的三个组件的内部集成,API 接口注册和调用。
但是三个供应商之间的模块要协同的时候则统一使用外部搭建的共享技术服务平台。
比如 API 接口注册到统一的 Kong 网关上,Kong 网关由平台集成商管理
比如涉及到三家的一些共性配置由 SpingCloud Config 统一转到 Apollo 配置中心
因此再简单总结下就是,评估是否采用 SpingCLoud 全套方案的时候,还需要评估是否存在跨有明确边界的团队协同的情况。或者是否存在类似集团型企业,多个业务系统微服务大集成的情况。如果存在,那么一些共性技术服务能力就必须抽出独立建设。
开发团队如何拆分的问题?
我们在实施微服务和云原生转型的时候,你看起来好像是 IT 系统分为了多个微服务,但是更加重要的是业务组织和团队本身需要分解为微服务,分解高度独立自治的业务团队。
每个团队都配置独立的前后端开发,需求,测试人员高度自治。
那么在拆分为多为业务团队后,如何保证原来一个大应用和产品的概念一致性或架构完整性。在这里我们提出,对于整体的产品规划和总体架构设计仍然需要集中化统一进行,然后在拆分和分配到各个微服务开发团队。
那么这里的架构设计包括哪些内容呢?具体如下:
各个微服务模块的功能列表清单
各个微服务模块的接口清单
数据库的拆分和数据表的 Owner 归属
以上三点就是最重要的架构设计需要提前进行的点。这个清楚后即可以分配到各个微服务团队,那么微服务团队高度自治和扁平化,各个团队之间之间进行协同和沟通,而不需要再通过架构师来协同增加沟通路径。
即产品规划和架构师很类似微服务架构里面的注册控制中心的职责。这也是我们常说的技术上的微服务拆分,实际上真正先行的业务组织团队的架构调整和职责拆分。
首先不可能你拆分了 20 个微服务,就拆分出 20 个开发团队。这里面仍然有域划分的概念在里面,即对 20 个微服务还要进行归类,以方面拆分。
方式 1:按纵向业务域进行归类,参考我们前面数据库拆分方法
方式 2:按横向分层归类,比如平台层团队,中台层团队,前台和 APP 应用团队
在团队拆分后,我们可以看到每一个开发小组必须配置前端开发,后端开发和测试人员。对于需求可以统一进行配置不拆分到开发组。当然也可以每个开发组配置一个需求细化人员,而仅仅在整个大团队配置产品经理出产品需求。对产品需求的细化还是到开发组内部完成。
为何如此强调开发团队要拆?
简单来说,就是各个开发团队内部的工作应该是对其它开发团队透明不可见的。开发团队之间高度直治,只能够通过粗粒度的接口交付。
如果开发团队本身不拆分,你会看到,一个开发团队管理多个微服务模块的时候,我们在前面制订的各种微服务开发规范,规约等很容易就被破坏,而这些事后审计和修改都会花费大量的时间进行变更和返工。
举个简单的例子,拆分为 2 个 DataBase 库后,同一个开发人员管理的时候,往往就省事的通过两个库间的跨库关联查询来解决问题,而这在微服务开发规约里面是不允许的。
当然从软件企业本身的 IT 治理管控来说,这也是最好的方案,对于一个大项目或大应用系统,并不是每个开发人员都能够看到所有项目模块源代码,其它非自己 Owner 的组件只能够消费和使用接口,其它内容都是不可见。
对于服务注册中心和 API 网关选择
对于服务注册中心和 API 网关,在前面我有专门文章详细分析。
什么时候需要使用 API 网关?
如果一个微服务架构下,虽然不会外部的其它应用进行交互和集成,但是整个应用本身存在 APP 应用端,而 APP 应用端通过前后端分析开发,同时需要通过互联网访问。本身存在需要一个统一访问 API 访问入口,同时也需要考虑和内部微服务模块进一步进行安全隔离。
当我们谈到这里的时候,你会发现我们常说的 API 网关的服务代理或透传能力,实际和我们常说的 Ngnix 反向代理或路由是一个意思。
如果你仅仅是为了统一 API 接口的访问出口,并考虑类似 DMZ 区的安全隔离,那么在你架构前期完全不需要马上实施 API 网关,直接采用 Ngnix 进行服务路由代理即可。因为在这种架构下,API 接口消费端,提供端全部是一个开发团队开发,各种问题分析排查都相当方便,类似 API 接口安全访问等也可以通过 JWT,Auth2.0 等统一实现,而且这个过程也并不复杂。
能力开放或多应用外部集成对 API 管控治理需要
但是当我们面临是和多个外部应用集成,或者说将自己的 API 接口服务能力开放给外部多个合作伙伴使用的时候,这个时候对于 API 接口的管控治理要求自然增加。
即在常规的服务代理路由基础上,需要增加类似负载均衡,安全,日志,限流熔断等各种能力,而且我们不希望这些能力在 API 接口开发的时候考虑,而是希望这些能力是在 API 接入到网关的时候统一灵活配置来实现管控。
那么这个时候使用 API 网关作用就体现出来。
多个开发团队协同,服务治理标准化需要
这个是我理解的需要 API 网关的第二个场景,这个有点类似于传统 IT 架构下对 ESB 服务总线的需求。当存在多个开发团队的时候,我们就需要对各个开发团队注册和接入的 API 接口服务进行统一管理,而这个时候就需要有 API 网关来实现。
即跨开发团队的 API 接口集成交付的统一管控都由 API 网关来复制,包括安全,日志审计,流量控制等,这些内容在多团队协同的时候不可能再依赖单个团队内部的一些技术,开发规范约定,而是需要有一个统一的标准。
同时多个开发团队协同和集成,必须有一个统一的集成方来解决协同中的问题。即使是在 ServiceMesh 服务网格架构下,我们也可以看到有一个控制中心来统一协调。
在使用 API 网关后技术组件的选择上
注意,对于 API 网关本身具备负载均衡,限流熔断,服务代理的能力。
即在注册中心下,Eureka+Feign+Ribbon+Hystrix 全部可以转由 API 网关来完成。但是一个应用的完整微服务架构可能存在一个 API 接口既要满足内部组件的 API 消费调用,又需要将该接口通过 API 网关暴露给外部应用使用。
通过 API 网关对外保留 Http Rest API 接口,传统 API 消费访问而不再是类似 Feign 声明式方式进行类似内部 API 接口方式调用。如下图:
可以看到,微服务 A 既需要满足内部微服务 B 作为消费方,通过服务注册中心进行消费调用,也需要满足外部 APP 通过 API 网关接口进行消费调用。
那么进入到微服务 A 集群的流量实际上是没有一个统一的入口的。
在这种场景下如果企业了 Hystrix 限流熔断,那么也仅仅是对内部的微服务模块组件间的消费调用进行控制。而对于外部 APP 限流,仍然还需要启用网关上的限流熔断功能。
微服务架构和容器云集成的集群和负载均衡
最后谈下微服务架构和 Kubernetes+Docker 容器云集成后的服务发现和负载均衡问题。
前面谈到在采用 Eureka 服务注册中心的时候,对于同一个微服务模块 A,我们可以启动多个微服务实例,不同的端口号。在端口启动后通过 Eureka 来实现服务的自动注册和发现。然后通过 Ribbon 来实现服务访问的负载均衡处理。
也就是说我们添加和部署微服务模块 A 节点是手工完成的。
但是在 DevOps 持续集成下,在实施 Kubernetes+Docker 容器云后,我们可以通过 k8s 来实现微服务节点资源的动态扩展。扩展的 Pod 资源统一由 Kubernetes 来实现集群负载均衡均衡,即对外只需要通过 Node+端口号访问即可。
所以在这个时候实际有两种做法。
做法 1:不再使用 Eureka 服务注册和发现
在这种时候,不再使用 Eureka 服务注册发现,而是通过 Kubernates 动态部署后的 VIP 进行访问,由 Kubernates 来进行后台节点的负载均衡。
这个时候我们只能够按常规调用 Http Rest API 接口的方式进行接口消费和调用,类似原来的 Feign 声明式调用可能不再适合。也就是说在这种场景下你只使用 SpringBoot 开发独立的能够暴露 Http Rest API 接口的微服务。不再使用 SpringCLoud 框架中的 Eureka+Feign+Ribbon。
做法 2:采用 Eureka 来替代 Kubernetes 中的 Service
在这种场景下,即不使用 Kubernetes 本身的集群功能,而是将动态部署出来的微服务模块还是自动化注册到 Eureka 服务注册中心统一管理。也就是还是按传统的 SpringCLoud 框架体系来进行架构搭建。
在这种思路下可以进一步保留 SpingCloud 下的限流,容错,心跳监测等方面的关键能力。
做法 3:进一步的思路还是ServiceMesh
实际上我们看到进一步的思路还是类似 Istio 的完全去中心化微服务治理方案。在这种模式下可以更好的通过 Sidecar 来实现相关服务注册,发现,限流熔断,安全等各种关键服务治理管控能力。
如果微服务模块全部是通过 Kubernetes 部署到 Docker 容器里面,那么我们可以看到完全可以在 k8s 进行镜像制作和容器部署的时候将 SideCar 的内容附加到具体的部署包里面实现集成。
简单来说,就是:
我们在开发微服务模块的时候完全不需要考虑太多的分布式 API 接口集成交互,但是和 Kubernetes 和Service Mesh集成后就具备了分布式接口调用和集成的能力。同时也具备了对 API 接口的安全,日志,限流熔断的管理能力。
因此也常说,Service Mesh 是 Kubernetes 支撑微服务能力拼图的最后一块。
评论