SaaS软件架构设计系列 | 5-3 基于微服务的SaaS系统架构概览

1,342 阅读12分钟

基于微服务的SaaS系统架构概览

从单体架构重构到微服务,是一个漫长的过程,单体和微服务架构会共存较长时间,基于前面的分析,我们一起看一下过度阶段的系统架构。

逻辑架构

image.png

  • 接入层:统一网关对客户端提供服务,具体的请求可以路由到微服务或单体。开放平台提供第三方接入,逻辑上和客户端的网关分开,实现上可以共用,也可以使用单独的网关。

  • 微服务:包含业务服务、公共业务服务以及各个平台的服务。和在单体中未完成迁移的业务通过接口交互。

  • 大单体:原有单体业务功能,通过集成平台的SDK来使用第三方能力,将对个开放接口注册到开放平台进行开放。

  • 基础设施:

    • 数据库:单体按原有的设计,产品+租户进行数据库隔离;微服务为服务+租户进行数据库隔离。因此在应用绞杀者模式时,原则上微服务和单体不能对同一数据对象进行修改,这也是单次迁移到微服务颗粒度的一个参考维度。对于跨数据库的数据统计,可以通过大数据平台来完成。

      在未完成微服务迁移时,同一领域的数据可能需要在功能列表中展示跨数据库的数据,这类功能尽量不要依赖大数据的能力,因为大数据平台运行的时效性和可用性很难保障。对于简单的查询和关联,可以通过接口获取相关数据进行展示。例如会员相关的领域已迁移到微服务,但会员的优惠信息管理还在大单体,些时需要展示会员的优惠信息时,可以通过接口从单体获取;相反查询拥有某项优惠的会员时,可以通过接口从微服务获取会员信息。对于复杂的查询(非统计分析),例如需要对会员和优惠信息都进行过滤、排序、汇总,可以用前面提到的,通过事件来清洗数据宽表的方式满足要求。在迁移到微服务的过程中,还是应该尽量避免出现复杂的跨数据库的功能查询。

    • Cache:单体和微服务使用的缓存尽量分开,因为缓存的易失性,尽量避免通过缓存来共享数据。

    • MQ:在绞杀者模式中,可能会有大量的单体和微服务之间的发布/订阅功能,因此建议在两套架构中共用MQ。如果要分离MQ,就得在发布业务事件时区分是针对单体还是针对微服务,但事件的订阅方可能随着微服务的迁移改变所在的位置,最后不得不在两个MQ都进行发布,所以没有必要分开。当一个领域完成微服务迁移后,领域内的发布/订阅自然会消失。

    • 对象存储:如果原有的业务领域划分比较合理,可以延用之前的结构。如果在微服务迁移时,对领域进行了重新划分,由需要考虑对业务目录进行调整。

    • BigData:在应用大数据技术时,会引入大量的大数据处理中间件,如:HDFS、Spark、Flink等,具体的大数据架构不在我们本次的讨论范围内。

  • DevOps平台:需要对原有的能力进行升级已支撑微服务架构,因为微服务架构将系统的复杂度下沉到了基础设施层,因此需要更强的平台、工具能力进行支撑。主要是CI/CD、服务编排和可观测能力的建设,需要投入大量的资源并持续优化才能够建设好。

部署架构

image.png

在原有单体部署架构的基础上,部署微服务架构,将外网入口放到K8S中,实现之前的分流、限流等功能,架构图如上。

  • 在架构重构的过度阶段,微服和单体需要互相访问:

    • 单体部署API服务,微服务通过内网访问。因为是过度阶段内部访问的接口,随时可能调整或取消,所以不应该暴露到外网,如果被外部使用,可能会导致后面接口调整和取消时需要协调第三方处理。
    • 微服务提供内网网关供单体服务调用。随着迁移的进行,原有在单体内部不同模块通过本地代码的依赖,需要重构成远程依赖,同是需要处理好分布式相关的问题,有可能需要修改设计。
    • 使用K8S外部服务(External Service)访问单体中的服务。对于单体中相对独立且规模较小的服务(比如通用域的服务),如果评估在短时间内不会重构为微服务,使用代理服务和重新开发接口显得没有必要,可以使用外部服务的方式提供给K8S中的服务进行访问。
  • 大数据集群单独部署,大数据集群因为使用资源方式不同,高峰时段占用资源较多,建议单独部署,避免对业务造成影响。可以使用单独的K8S集群或设置污点等方式来达到隔离的目的。

  • 对于集群内部需要访问第三方应用和服务时,建议通过SNAT等技术使用统一的出口IP,这样便于第三方安全IP白名单的设置。

如果原有单体能够进行容器化部署到K8S中,则部署架构则会更统一和简单,架构图如下:

image.png

我们可以看到,通过将单体容器化部署到K8S中,可以简化单体和微服务的网络通讯,而且设计上也会有所改变。可能的变化之一就是接口的设计,之前通过统一的API服务提供单体的接口,容器化部署后,可能更倾向于每个单体服务提供自己的接口来配合各自的单体代理,能够实现更内聚的设计。

运行架构

以下是一个简单的微服务运行的架构示意图:

image.png

这里我们关注以下几个方面的设计:

  • 流控、鉴权等通用的基础能力通过网关插件实现。在单体模式下,可能更倾向于在代码框架层面进行鉴权;在微服务架构下,可以考虑使用网关插件加对应服务来实现鉴权。可以进一步将类似的基础能力更内聚的使用服务进行处理。这样有利于不同的团队根据业务选择不同的代码框架。基础框架的统一和放权平衡又是一个单独的话题,后面有机会再专题讨论。
  • 通过BFF层对外提供服务。BFF层是微服务架构中常见的一种实践,用于聚合多个微服务的能力针对客户端来提供后端能力。可以考虑一个端一个BFF的方式。这里会涉及到更深层次的前后端开发协作模式的改变,后面有机会再专题讨论。
  • 多租户叠加微服务体系下,数据库访问连接数可能不够。前期体量不大时,可以考虑使用分布式数据库,通过增加计算节点来增加连接数;后续体量变大时如果为了增加连接数而增加节点,可能导致节点资源利用率很低,此时可以考虑使用数据库代理来减少数据库连接数需求。
    • 根据我们之前的架构设计建议,数据库按微服务+租户进行隔离,所以系统数据库的数据为: 微服务数X租户数 。假设有300个服务,3,000个租户,则数据库数据会达到90万个;
    • 在ToB的场景中,大部分业务的请求量并不会太大,这些库会分布在上百个数据库实例中。在考虑资源利用率合理的情况下,集中式数据库,每个实例可以提供上万的连接数。
    • 因为所有租户都共用一套程序,每个服务都需要访问对应数据库所有租户的库,每个服务需要的连接数为: 副本数X连接池连接数X租户数 。按一个服务平均2个副本,每个副本单个连接池最低连接数为10算,单个服务需要2X10X3000=6万个连接。
    • 300个服务需要的总连接数可能会达到1800万,通过增加计算资源来满足要求,会导致成本过高且资源利用率太低。

      有的数据库可能无法创建太多数据库(比如SqlServer实例只能创建数百个数据库),就无法采用之前的设计方式,此时建议按一个租户一个数据库的方式进行数据隔离,然后再想办法限制不同的业务使用不同的表(例如人为约定或在框架中使用固定前缀限制)。

使用数据库代理后的的部署架构如下:

image.png

使用数据库代理后,可以有限降低对数据库连接量的需求,同时还可以在代理层进行性能和安全的优化:

  • 可以将数据库代理(DBPorxy)服务看成是微服务共享的数据库连接池,避免了每个服务最低连接空转的情况,从而整体上更加优化了连接资源的使用。
  • 使用数据库代理后,可以根据数据库实例的实际情况进行请求限流,也可以针对租户进行限流,避免一个租户打垮整个实例。
  • 通过数据库代理,可以拦截高危SQL(比如删除表、删除数据的语言没有过滤条件),也可以对低性能SQL进行熔断处理。

同时使用数据库代理可能带来一定的成本和风险:

  • 性能风险:因为所有的SQL执行都会通过数据库代理执行,所以数据库代理必须采用高性能的方案实现,且代理服务具有较高的资源支撑;
  • 稳定性风险:尽管数据库代理可以采用多副本的方式来避免单点,但使用方情况复杂,可能会有瞬时大流量进入,所以需要做好限流、熔断、自动伸缩的管理。对代理的更新、配置变更等场景可能会导致服务不可用,所以需要支持灰度发布和变更。
  • 加强可观测性:对数据库代理需要有较强的可观测性,配置完善的告警,以便于及时发现和解决问题。

个性化扩展

Headless方式

基于微服务的体系,可以在系统的多个层面进行扩展。如果业务模型相对成熟,只是前端展示变化较大,对于未进行前端PaaS化的应用,也可以采用Headless的方式将服务开放出去。前提是业务模型相对稳定,不会有大的变更。以下是Headless方式扩展的示意图:

image.png

  • 用户通过扩展的客户端来访问系统。
  • 扩展的客户端主要访问扩展的BFF服务,可能部分公共/通用功能会访问SaaS原有的BFF服务。
  • 业务服务通过开放平台将能力开放给扩展的BFF进行访问。

SPI扩展

如果要针对服务内部的逻辑进行扩展,比如扩展、修改或替换业务流程中某个步骤的逻辑,可以使用扩展点的方案来满足要求。前提是业务模型本身相对稳定,并对业务流程进行了抽象,通过修改服务的SPI实现来进行扩展,示意图如下:

image.png

  • 上图中微服务基于DDD的战术实现,通过在Repository中获取不同的SPI实现来改变系统行为。
  • 应用可在扩展点管理服务注册扩展点,并提供SPI接入、接口定义相关的文档,扩展方可根据租户配置扩展实践的接口。
  • 扩展点需要考虑性能影响,特别是SPI实现在第三方环境的情况,同时需要考虑调用失败、异常等情况的处理策略。
  • 在实践中,扩展点服务会发布对应的SDK给业务方使用,用于处理鉴权、异常处理、获取配置信息等。

基于微服务的扩展模型

前端也可以采用PaaS或扩展点的模式进行扩展,结合前面的Headless模式和SPI模式,还有开放平台本身具备的API和事件开放能力,基于微服务的扩展模型如下:

image.png

  • 针对不同的业务类型和业务成熟度,应该采用不同的扩展方式。

    • 交互界面需要整体个性化的业务,可以采用PaaS平台或Headless的方式;
    • 交互界面只需要局部个性化的业务,可以采用前端扩展点的方式;
    • 对于需要改变后端业务逻辑的业务,可以采用后端扩展点的方式;
    • 通常需要结合扩展点、开放API和事件等多种方式来应对个性化需求。
  • 开放和扩展的内容需要保持较好的兼容性和持续性,前面我们也讨论过开放接口的一些原则,这里不再赘述。

  • 扩展的前提大多需要在业务相对成熟,业务模型清晰稳定,实现抽象合理的基础上进行。如果业务模型后续变化较大,基于现有模型的开放和扩展由于存在外部依赖将很难进行调整。