本文讨论中台项目集中部署模式,以某个具体的中台应用为例,如何给不同的业务线提供服务,主要考虑隔离性和部署成本。
在微服务的情况下,中台的应用应该满足下面的基本原则:
- 通过RPC为业务方提供服务
中台为企业内部的其他业务线的应用提供服务,企业内部应用间通信RPC较为合适。 - 同一套代码 微服务12要素--12factor
- 在隔离部署的情况下,同应用的所有数据库都使用相同的数据结构。
共享模式
最简单的模式,对于某个中台应用,统一的集群服务对各个业务线提供服务,通过业务身份进行逻辑层面的区分,共享服务器资源、中间件资源等。
该中台应用部署了4个应用(server1、server2、server3和server4),连接相同的数据库DB、缓存cache和消息队列MQ,为公司内部的4条业务线提供服务(业务1、业务2、业务3、业务4)。
业务接入流程:
- 申请业务身份bizX
- 调用方接口以参数的形式传入bizX,中台代码中通过业务身份bizX做逻辑隔离。
弊端:
不同业务间共享服务和中间件资源,出现故障时可能会影响全部业务线。
服务端可以通过限流的方式,识别某一个业务的流量,达到某个阈值的时候限制其流量,一定程度上保护整个集群。
独享模式
模式1毕竟是软件上的隔离,任何资源出现问题,都会互相影响。
另一条方向就是为每个业务提供独立的服务器资源和中间件资源。如下图所示,为每个应用提供一个集群分组,每个分组提供至少2个应用用于做冗余,不同集群分组使用各自的数据库、缓存和消息队列,做到核心资源的隔离部署。
实现这个部署方式需要考虑下面的问题:
- 服务发现与路由
服务集群如何分组,让各业务线调用不同的分组
- 不同集群连接不同的中间件服务,例如 数据库、缓存、消息队列
应用服务部署启动时,传入参数BizGroup,指定是哪个业务分组
对于java应用来说, 启动命令中加入 -Dapp.bizGroup=bizGrgoup1
- 调用方根据约定的BizGroup,调用对应服务分组
对于Dubbo框架, 可以通过服务分组,即group参数来实现,consumer和provider指定相同的group
- 服务提供方根据BizGroup到配置中心获取对应的中间件连接地址,连接指定的中间件实例
例如数据库的配置信息如下
bizGrgoup1.jdbc.url = jdbc:mysql://host1:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup1.jdbc.username = user_name
bizGrgoup1.jdbc.password = ****
bizGrgoup2.jdbc.url = jdbc:mysql://host2:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup2.jdbc.username = user_name
bizGrgoup2.jdbc.password = ****
bizGrgoup3.jdbc.url = jdbc:mysql://host3:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup3.jdbc.username = user_name
bizGrgoup3.jdbc.password = ****
bizGrgoup4.jdbc.url = jdbc:mysql://host3:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup4.jdbc.username = user_name
bizGrgoup4.jdbc.password = ****
业务介入流程:
- 申请业务身份bizX,定义业务分组xGroup
- 申请新的部署资源
- 至少2个服务器实例
- 数据库实例
- 缓存实例
- 消息队列的topic
- 中间件的地址添加到配置中心
- 数据库初始化
- 初始化通用的表结构
- 初始化基础的配置数据
- 部署服务,设置参数-Dapp.bizGroup=xGroup
- 调用方接口以参数的形式传入bizX,引入consumer时指定group=xGroup
弊端:
新业务接入较慢,需要新增部署的实例,每个分组都需要做冗余,对于请求量较低且业务对稳定性要求不高的应用来说,这个部署模式有一定的浪费。
混合模式1
综合独立部署模式和共享模式,根据业务的需求,提供独立部署或者共享部署。
bizGrgoup1.jdbc.url = jdbc:mysql://host1:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup1.jdbc.username = user_name
bizGrgoup1.jdbc.password = ****
bizGrgoup2.jdbc.url = jdbc:mysql://host2:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
bizGrgoup2.jdbc.username = user_name
bizGrgoup2.jdbc.password = ****
shareGrgoup.jdbc.url = jdbc:mysql://host3:port/db_name?useUnicode=true&&characterEncoding=UTF-8&useSSL=false
shareGrgoup.jdbc.username = user_name
shareGrgoup.jdbc.password = ****
业务接入流程:
场景:新增一个业务X,该业务新启动,初期流量较小,先加入共享集群,共享集群新增一台机器
新增业务,申请业务身份bizX
- 部署服务,设置参数-Dapp.bizGroup=xGroup
- 应用将IP注册到“业务配置”中
- 管理人员将该IP加入到“shareGrgoup”分组中,中间件的配置信息推送给新增的应用,新增应用初始化中间件连接池
- 业务X的调用方配置自身consumer service,设置group=shareGrgoup,接入到共享集群分组中。
- 申请业务身份bizX,定义业务分组xGroup
- 申请1个
- 中间件的地址添加到配置中心
- 数据库初始化
- 初始化通用的表结构
- 初始化基础的配置数据
- 部署服务,设置参数-Dapp.bizGroup=xGroup
- 调用方接口以参数的形式传入bizX,引入consumer时指定group=xGroup
业务X发展越来越好,为其设置独立集群
- 定义业务分组xGroup
- 申请新的部署资源
- 至少2个服务器实例,用于冗余
- 数据库实例
- 缓存实例
- 消息队列的topic
- 配置中间件连接信息到xGroup中
- 数据库初始化
- 初始化通用的表结构
- 初始化基础的配置数据
- 迁移shareGroup中和X业务相关的所有数据到xGroup的数据库中
- 部署服务
- 业务X的调用方修改group=xGroup
混合模式2
独享模式和混合模式1都存一个问题:基于环境变量配置不同的应用,部署上会造成一些麻烦,同一个集群内的不同应用时有差异的,自动化部署时需要为应用需要区分具体的分组。
解决方案是将分组的配置从应用部署时的参数迁移到独立的“业务配置服务”中,应用启动时从配置服务中读取对应的中间件配置信息,然后完成自身连接池的初始化。“业务配置服务”为所有中台应用提供服务,一个中台集群有多个分组,一个IP归属于某一个分组,该服务存储的对象如下
业务接入流程: