从微服务转回单体:服务个数从21下降到2,版本类运维量降为0

2023-11-10 13:48:46 浏览数 (3)

# 关注并星标腾讯云开发者

# 每周3 | 谈谈我在腾讯的架构设计经验

# 第9期 | 如何优雅兼容公有云和私有化?

大家好,我是 anson。做过 To B 项目的开发者朋友都知道,传统行业为了满足安全合规等问题,大多会要求采购的软件需要在本地化环境部署运行。

在这种场景下,传统的私有化部署依赖客户的 IDC 资源,若资源需求量太重,对客户来讲直接增加了大量成本,那么 To B 产品优雅地进行架构设计就非常重要了。

基于此,微搭低代码是如何设计的,使最终资源需求量下降90%、版本类运维量降为0,让这款服务海量企业的低代码产品实现“轻装上阵”的呢?

容我先简单介绍「微搭低代码」、「混合云」、「微搭混合云」几个概念。

微搭低代码作为腾讯云自研、为开发者打造的高效低代码平台,可通过可视化拖拉拽的方式轻松搭建微信小程序和企业生产管理系统。

混合云是一种混合计算环境,其中结合使用不同环境(公有云和私有云,包括本地数据中心或“边缘”位置)中的计算、存储空间和服务来运行应用。

微搭混合云这里指的是应用的开发在公有云上,开发完后的应用产物运行部署在客户 IDC 内。

1.1 微搭为什么要做混合云

传统的私有化是将所有的计算 存储私有化,但是微搭低代码平台作为开发 运行平台,不仅承担了开发工具的职责,也承担了运行部署平台。在传统行业渗透数字化的过程中,除了特定金融等行业,大部分行业需要的只是产物私有化(也即开发后的应用包部署和运行在私有化环境),如果开发工具能够继续使用公有云的平台,这样开发效率还可以随着公有云迭代随时享受最新的便利性。基于此,重点来了,微搭推出混合云模式,也就是将开发工具公有云,应用部署私有化。

1.2 混合云的开发模式

微搭是集应用页面设计和后台运行环境一体化低代码应用构建平台,默认方式支持公有云模式;同时微搭也支持私有化方式支持数据不能存在公网的情况,是微搭应用私有化部署方式,为混合云模式。

混合云的开发模式,包含两大核心模板:公有云设计开发和私有化应用运行。从应用的开发中,两个场景覆盖的应用生命周期如下:

2.1 混合云形态

微搭低代码整体上分为两部分:设计态和运行态。

设计态在公有云,运行态在私有云的特性,称为混合云。

  • 设计态:应用工作区,用来开发应用 ,把开发好的应用构建发布成物料;
  • 运行态:应用运行环境底座,基于设计态开发好的物料,做部署安装。
2.2 混合云架构

基于这种业务形态,结合我们公有云本身就是微服务的架构体系。我们快速落地了混合云(运行态)的架构(1.0版本),微搭低代码混合云的整体架构,包含基础服务、网关服务、服务控制、容器平台以及支撑组件。

  • 基础服务负责前端和后端 api 的接入,通过 DNS 、 LB 将用户请求分发到不同区域的控制台;
  • 网关服务负责对后端和前端流量分别进行处理;
  • 服务控制包含了所有微搭所有的服务组件(微服务粒度20 ),包含权限控制,数据模型,流程,消息中心,应用管理,企业工作台管理等;
  • 支撑组件,包含 Mysql 、 MongoDB 、 Redis 、 Kafka 、 S3 对象存储等。

我们1.0的架构推进过程中,也遇到了一些问题挑战:

1、微服务粒度20 ,针对私有化场景下客户机器数量有限。有没有一种方案可实现可以同时支持微服务和单体架构?

2、客户在公有云设计态开发的应用(公有云底座产品能力实时迭代),应用发布到混合云上,由于混合云底座是部署时的老版本 ,导致开发的应用发布到混合云功能会出现因为功能不支持导致的报错。

下面我们来看下,微搭在这两个挑战上是如何解决的?

3.1 概念

有没有一种方案可实现同时支持微服务和单体架构?

  • 微服务 是「可分」;
  • 单体架构 是「可合」。

我们要支持公有云的架构是「可分」,私有化的架构是「可合」,并且原则是:以公有云的微服务架构为基准。

3.2 架构演进

那这块我们的思路是:「可分」单个微服务可以编译成单个进程,「可合」也可以编译成子模块集成到大进程中这样相当于把 N 个进程从架构上演变成了1个进程。

3.3 方案实现
3.3.1 方案设计

原则是:以公有云的微服务架构为基准,通过搭建一个单体应用的 Spring 启动框架,通过 dependency 将微服务的 jar 引入进来,通过 @ComponentScan 来排除掉各个子进程的启动类只启动单体应用的进程,进而达到 N 个进程合并成1个进程的效果。

单体架构应用:没有任何逻辑,只有启动类和一个配置文件。

单体应用启动类伪代码如下:

代码语言:javascript复制
@EnableFeignClients
@ComponentScan(basePackages = {"com.xxx"},
        excludeFilters = {@ComponentScan.Filter(
        type = FilterType.ASSIGNABLE_TYPE,
        classes = {
                com.xxx.AApplication.class,
                com.xxx.BApplication.class,
                com.xxx.CApplication.class,
                com.xxx.DApplication.class,
                com.xxx.EApplication.class,
                com.xxx.FApplication.class,
                省略xxxxxxx.class 
        }
)}
)
@SpringBootApplication
public class AllInOneApplication {

    public static void main(String[] args) {
        SpringApplication.run(AllInOneApplication.class, args);
    }
}
3.3.2 具体改造

1、每个微服务的数据库合成一个数据库

针对微服务使用不同的 database ,这块混合云统一合成一个 database ,由统一的 datainit 服务来完成数据库的创建和初始化。

2、每个微服务 yaml 合并一个 yaml

所有微服务的 yaml 针对存量的 key 统一做合并,新增的 key 统一加下微服务前缀标识来区分,确保每个微服务的 key 各不相同。

3、bean 冲突改造

如果各个业务中存在同名的 bean ,在 Spring 启动的时候,会基于 name 加载到 Spring 容器中,造成 Spring 启动失败 ,为了避免 Bean 冲突。

有以下处理策略:

1、每个微服务修改包名 ,基于「领域」修改包路径。例如:权限加 auth 、流程加 flow 等。

2、如果业务中存在通过之前 name 来注入 Bean 的。比如 @Bean(name="") 、@Resource(name="")、@Autowired(name="")、@Service(name="")、@Qualifier(name="")等,建议加上业务「领域」属性,避免冲突。

4、pom.xml 改造

原来公有云 pom.xml 有 dev 、 test 、 pre 和 prod 的对应的 profile ,增加一个私有化所对应的 profile 节点 private ,这样对公有云构建和编译也不受任何影响。

在单体流水线构建出包的时候,各个微服务的 SPRING_PROFILE = private

mvn clean install-DskipTests-P$SPRING_PROFILE-s../settings.xml

另外需要注意的地方,需要在 private 节点中移除 spring-boot-maven-plugin 的maven plugin 和 排除掉微服务中的 resources 配置文件。

(因为配置文件已经统一合并成一个了,在单体项目中引入即可)

代码语言:javascript复制
# 私有化环境
<profile>
    <id>private</id>
    <build>
         <resources>
                    <resource>
                        <directory>src/main/resources</directory>
                        <filtering>true</filtering>
                        <excludes>
                        <exclude>application.properties</exclude>
                            <exclude>bootstrap.yaml</exclude>
                            <exclude>bootstrap.yml</exclude>
                            <exclude>application.yaml</exclude>
                            <exclude>application-*.yaml</exclude>
                            <exclude>application.yml</exclude>
                            <exclude>application-*.yml</exclude>
                        </excludes>
                    </resource>
                </resources>
    </build>
<profile>
3.4 合流 自动化测试保障单体架构

为了保证单体应用功能的正确性,我们在公有云合流阶段做了流水线,当公有云微服务代码合并主干分支时触发「单体应用」构建&部署 。如果部署成功,会执行自动化测试用例来提前发现私有化功能上的一些问题(通过技术手段来保证:用来检测每个微服务 yaml、Bean 要互斥,不能出现同名的 key 和同名的 Bean )。

3.5 可合的服务如何与可分微服务对应

下面我们再来看下「可合的服务如何与可分微服务对应」,在混合云运维中,如果说单体架构中某个业务模块出现问题/报错了,如何定位到其错误信息具体对应公有云的 git 仓库上的 commit 代码片段?

我们在混合云出包的时候,会实时获取到每个微服务对应的 commitId (微服务出包时对应的分支/ tag ),实时写入/推送到固定的 git 上,业务研发同学可基于当前部署包的版本号来统一查找各个微服务对应的 commitId ,从而快速定位到具体公有云微服务对应的代码片段。

我们解决了在客户资源不足的情况,来完成混合云业务服务的部署。那么我们如何解决客户底座运维的问题呢?接下来的章节会讲解混合云的底座是如何保持与公有云同步的。

4.1 概念

客户在公有云设计态开发的应用(公有云产品能力会实时迭代,相对应的底座镜像也会实时更新发布),当公有云开发的应用发布到混合云上,由于混合云底座是部署时的版本(底座版本低于公有云版本) ,会导致开发的应用发布到混合云功能报错。

混合云底座随应用一起发布,来解决混合云的底座未实时同步的情况。

4.2 方案实现
4.2.1 提出方案

前提:客户的cvm(云服务器)可出公网。

那有没有一种策略?在用户安装部署公有云开发的应用的时候,来进行混合云底座对比?进而来确认下混合云的底座镜像否需要升级?

那我们这块的思路是:「应用更新的时候来进行底座 pod 的更新」,相当于我们在安装应用的时候,会从镜像仓库中拉取公有云运行态对应的底座镜像版本来进行部署;

当然了,我们也提供了回滚的能力,如果发布失败,可在安装应用的页面快速回滚到上个版本,用于保证客户的应用不受影响。

4.2.2 方案设计

那我们再来思考下,能否在公有云上实现一键发布到混合云上,并且公有云上能够看到混合云的底座的运行状态?

受限于混合云网络情况,客户允许机器网络出公网,禁止公网访问客户机器。 因此这种需要通过建立长连接保持网络互通。

基于这种网络情况,我们的方案演成了通过混合云 agent (websocket 长链接)来进行「公有云」与「混合云」网络通信的架构。

这样的话,我们也抽象出来了「混合云管理流」和「混合云数据流」的概念。「混合云管理流」:也叫混合云设计态,管理底座集群信息上报、运维、部署、升级等功能

  • 混合云 agent 与公有云服务,建立 websocket 长链接,用于集群信息上报 ;
  • 事件引擎用户路由 agent 下发的指令操作 ;
  • 运行态底座服务用于支撑的应用的运行态相关功能的
  • operator 来 watch 底座的任务部署、升级、回滚底座等功能

「混合云数据流」:也叫混合云运行态,支撑的应用的运行态相关功能的底座服务,与公有云的微服务相对应。

这样的话,客户再公有云就可以进行私有化底座的管理,也能够解决在公有云应用发布一键自动化升级底座。

5.1 架构维度

从架构上来看: 我们进行架构改造实践落地后,从之前的微服务架构变成了单体架构,微服务数量从 21 个变成 1 个(运行态:21 -> 1,设计态:0->1 ) 。

5.2 部署方向

从部署服务角度来看:我们进行架构改造实践落地后,服务个数由 21 个变成了 2 个 ; 从运行态来看:可分-微服务有 21个,可合-单体 1 个; 从设计态来看:可合单体架构下多了 1 个 pod 。

5.3 运维角度

从运维角度来看: 我们进行架构改造实践落地后,解决了混合云底座运维管理事项功能,公有云上可看到混合云底座运行的状态,也可以在公有云一键来管理混合云。

5.4 资源维度

从资源维度来看:单体可合架构下占用的机器资源大幅下降! 「可分」架构下 21 个服务,每个进程 4c8g ,共用 84C168G 。 「可合」架构下 2 个服务,每个进程 4c8g ,共用 8C16G 。

5.5 交付客户

我们新架构落地之后,截止到目前为止,陆续推进引导了多家客户升级,升级之后也没有遇到/反馈私有化底座版本过低,导致功能报错等问题。

同时,客户表示大大减轻了各个业务模块研发人员投入排查【底座镜像版本过低,功能报错问题】的时间。

这次的架构设计升级,让微搭低代码混合云架构「可分可合」,单 POD 交付,对客户 IDC 资源依赖大大降低,最终实现资源需求量下降90%、版本类运维量降为0,让微搭低代码实现轻装上阵,轻量交付!

当然了,也不是说单体架构比微服务架构要好,单体架构在运维方面相对(微服务)而言还是有些弊端。至于是在交付业务时选择单体架构还是微服务架构,我想这块没有一个严格确切的答案。主要还是要基于业务场景来权衡利弊,在业务发展中不断探索最合适的模式。

我认为,软件设计的首要目标应该是满足业务需求,而不是为了设计而设计。如果一个设计没有为业务需求服务,那么它就是过渡设计,是没有意义的。你觉得呢?

-End-

关注并星标腾讯云开发者

第一时间看鹅厂架构设计经验

1 人点赞