对微服务架构设计实践中若干问题的探讨

点击上方“程序猿技术大咖”,关注加群讨论

今天讨论下在微服务架构实践中经常遇到的一些问题的思考,其中有些来源于我们自己的微服务改造项目,有些来源于客户现场微服务架构实施项目和售前方案沟通。

本文仅针对关键问题点进行讨论,具体如下:

  • 微服务下数据库划分

  • 微服务开发和技术框架的选择

  • 微服务和DevOps,容器云集成

  • 微服务网关和注册中心

  • 微服务相关关键技术,如限流熔断,安全,服务链监控等

对于微服务相关的基础知识,大家在网上基本都可以搜索到就不再重复叙述,对于SOA和微服务,中台架构等的区别等,也可以参考我前面发布过的文章。

数据库拆分是否和微服务必须1对1

在最早谈微服务架构的时候我们就谈到。要确保每个微服务都独立自治和松耦合,那么微服务从数据库到逻辑层到前台全部都要进行纵向拆分。

即数据库的拆分是整个微服务架构设计中的一个关键内容。

而这里有一个关键思考就是,在微服务实践中你会看到,实际上你上层的微服务组件的拆分相当来说会更加细,一个不算复杂的业务系统拆分到20到30个微服务组件都是正常情况。

而对于数据库也拆分为20个独立的DataBase显然不合理。

这个一方面是增加了数据库本身的管理复杂度,同时由于数据库的太细拆分也引入了更多的分布式事务处理问题,跨库数据关联查询不方便等问题。因此在这里,最好的建议是我们引入一个业务域的概念,即:

可以按业务域来进行数据库拆分,每一个业务域相当独立并对应一个独立数据库,但是业务域里面本身可以有多个上层的微服务模块组件。同一个业务域里面的微服务仍然通过注册中心进行访问和调用。

即同一个业务域里面的微服务在逻辑层本身还是解耦的,只能够通过API接口访问调用,以方便分布式部署,但是数据库层本身不拆分,共享同一个数据库。

比如在我们的项目里面,我们会将4A和流程引擎两个微服务共享一个数据库,将费用报账,差旅报账,借款报账等独立微服务共享一个报账数据库。

在这种方式下虽然没有实现数据库的彻底解耦,但是通过在逻辑层的微服务拆分和解耦,我们可以实现微服务部署包的更加细粒度管理。同时当存在业务逻辑变更的时候,我们也仅仅需要变更相应的微服务模块,做到变更影响最小化目的。

是否使用SpringCloud全家桶

可以看到如果采用SpingCLoud微服务技术开发框架,那么对应服务注册中心,限流熔断,微服务网关,负载均衡,配置中心,安全,声明式调用所有能力全部提供。

你需要做的就是采用SpringBoot框架来开发一个个微服务组件即可。

当然在我们实施的项目里面也存在另外一种方式,即只使用SpringBoot框架进行单个微服务组件的开发,其次再去组合和集成当前业界主流的微服务开源技术组件产品。

  • 服务注册中心:

    阿里的Nacos注册中心

  • 服务配置中心:

    携程开源的Apollo配置中心

  • 限流熔断:

    阿里的Sentinel

  • 服务链监控:

    直接对SkyWalking服务链监控进行集成

  • 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支撑微服务能力拼图的最后一块。

喜欢就点个"在看"呗,留言、转发朋友圈