面对十几万用户 SaaS 服务迁移的巨大挑战,如何在单台服务器上实现高效、稳定、安全的部署运行? 本文详细探讨了这一挑战背后的技术实践过程,包括技术架构的演进、面临的挑战及解决方案,以及最终实现的架构维度和资源维度的显著收益。通过这一创新实践,微服务数量从30+锐减至个位数,资源占用大幅下降,仅需 8C16G 即可轻松应对,为企业在成本、效率及灵活性上带来前所未有的突破。
关注腾讯云开发者,一手技术干货提前解锁👇
1.1 前言
为了满足不同场景、不同的行业的用户诉求、以及为了满足传统行业安全合规等问题,都会要求采购的软件需要在本地化环境部署运行。在这种场景下,我们如何把公有云的 SaaS 平台服务搬迁到微搭单台服务器上?
传统的私有化是将所有服务涉及的计算+存储私有化,微搭低代码平台作为开发/运行平台,不仅承担了开发工具的职责,也承担了运行部署平台。这就意味着我们同时要把开发平台和运行部署平台都要搬迁到单台服务器上。
1.2 微搭是什么
腾讯云微搭低代码是一个高性能的低代码开发平台。免前端开发,支持一码多端,自由定制管理系统和小程序,PC/H5 应用,支持连接本地和外部数据库,支持 AI 大模型自动生成页面,快速集成各类 AI 模型,支持被集成,一条命令将系统私有部署到任意服务器。支持打通企业内外部数据,轻松创建数据分析、业务管理、消息推送、用户权限等能力的企业管理系统。快速连接腾信生态,支持原生小程序,助力企业内外部运营协同和营销管理。
2.1 产品的形态
微搭低代码产品整体上分为两部分:设计态和运行态。
设计态:应用工作区,用来开发应用 ,把开发好的应用构建发布成物料。
运行态:应用运行环境底座,基于设计态开发好的物料,做部署安装。
2.2 技术架构
基于这种业务形态,结合我们公有云本身就是微服务的架构体系。我们快速落地了技术架构。
微搭低代码混合云的整体架构,包含基础服务、网关服务、服务控制、容器平台以及支撑组件;
我们架构推进过程中,也遇到了一些问题挑战:
下面我们来看下,微搭在这两个挑战上是如何解决的?
3.1 架构挑战
3.1.1 架构演进
有没有一种方案可实现可以同时支持公有云的微服务和软件化可合(单体)架构?
微服务 是「可分」
单体架构 是「可合」
我们要支持公有云的架构是「可分」,私有化的架构是「可合」,并且原则是:以公有云的微服务架构为基准。
那这块我们的思路是:「可分」单个微服务可以编译成单个进程,「可合」也可以编译成子模块集成到大进程中 这样相当于把30+个进程从架构基于领域来大大进行了压缩 。
注意:这里主要是将后端服务来做合并,因为本身前端只有一个微服务进程。
3.1.2 架构方案实现
原则是:以公有云的微服务架构为基准,通过搭建一个单体应用的S pring 启动框架,通过 dependency 将微服务的 jar 引入进来,通过 @ComponentScan来排除掉各个子进程的启动类 。
只启动单体应用的进程,进而达到 N 个进程合并成1个进程的效果。
单体架构应用:没有任何逻辑,只有启动类和一个配置文件。
单体应用启动类伪代码如下:
@EnableFeignClients
@ComponentScan(basePackages = {"com.tencent.weda"},
excludeFilters = {@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = {
com.tencent.weda.WeDaBusinessApplication.class,
com.tencent.weda.ServerApplication.class,
com.tencent.weda.DsServerApplication.class,
com.tencent.weda.DsAdapterApplication.class,
com.tencent.weda.WeDaAuthServerApplication.class,
com.tencent.weda.WedaApiServerApplication.class,
com.tencent.weda.Application.class, com.tencent.weda.WedaWorkflowServerApplication.class,
省略xxxxxxx.class
}
)}
)
@SpringBootApplication
public class WedaApplication {
public static void main(String[] args) {
SpringApplication.run(WedaApplication.class, args);
}
}
3.2 服务合并落地挑战
架构上制定了服务合并的策略,那么接下来看下服务合并存在哪些问题和挑战?
3.2.1 bean 冲突改造
如果各个业务中存在同名的bean,在 Spring 启动的时候,会基于 name 加载到 Spring 容器中,造成 Spring 启动失败 ,为了避免 Bean 冲突。
有以下处理策略:
1.每个微服务修改包名 ,基于「领域」修改包路径。例如:权限加 auth、流程加 workflow 等。
2.如果业务中存在通过之前 name 来注入 Bean 的。比如 @Bean(name="")、@Resource(name="")、@Autowired(name="") @Service(name="") @Qualifier(name="") 等,建议加上业务「领域」属性,避免冲突。
3.2.2 Feign + Consul 调用改造
我们公有云微服务之间的服务注册调用都是使用 Feign +Consul 来实现的。
由于是单体应用,软件化这块就没必要再使用 Consul 了(当然这块不是必须要修改的,不做修改也没问题。我们主要考虑是减少中间件的依赖)。因为单体应用对外提供的接口能力都是127.0.0.1, 所以需要我们把 Feign + consul 调用 ,改成 Feign + localhost (127.0.0.1)。
@FeignClient 注解默认也提供了这样的能力(在 @FeignClient 注解中,url 属性的作用是指定目标服务的 URL 地址)。
通过增加 url 属性值来制定到目标服务的 url 地址。
业务微服务 添加 url="${spring.feign.ip:}",公有云 url 默认为空(还是基于注册中心来进行选择实例),在软件化 yaml 中将该值设置为127.0.0.1:port 即可。
示例:
3.2.3 pom.xml 改造
原来公有云 pom.xml 有 dev、test、pre 和 prod 的对应的 profile,增加一个私有化所对应的 profile 节点 private,这样对公有云构建和编译也不受任何影响。
在单体流水线构建出包的时候,各个微服务的 SPRING_PROFILE=private。
mvn clean install -DskipTests -P$SPRING_PROFILE -s ../settings.xm
另外需要注意的地方,需要在 private 节点中移除 spring-boot-maven-plugin 的 maven plugin 和排除掉微服务中的 resources 配置文件。
(因为配置文件已经统一合并成一个了,在单体项目中引入即可)
# 私有化环境
<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.2.4 配置文件改造
通过制定规范来进行管控和约束。每个微服务 yaml 合并一个 yaml 。所有微服务的 yaml 针对存量的 key 统一做合并,新增的 key 统一加下微服务前缀标识来区分,确保每个微服务的 key 各不相同。
weda.model.xxx:123
weda.auth.xxx:456
weda.connector.xxx:789
3.3 公有云的数据库 DDL 与 DML 如何自动同步到软件化中?
那么技术上如何保证公有云的数据库 DDL 与 DML 自动同步到软件化中呢?在架构上我们设计了一个旁路服务,通过定时扫描监听 公有云数据库的 DDL 与 DML 变动,形成 SQL 物料 - 自动同步到 软件化的物料包中 ,这样软件化的数据库 就能实时与公有云保持同步。
公有云还保持原来的设计和链路,软件化这块做适配。针对微服务使用不同的 database,这块软件化统一合成一个 database,由统一的 init-data 服务来完成数据库的创建和初始化。
3.4 如何简化服务运维以及功能准确性?
3.4.1 合流 + 自动化测试保障单体架构
为了保证单体应用功能的正确性,我们在公有云合流阶段做了流水线,当公有云微服务代码合并主干分支时触发「软件化应用」构建&部署 。如果部署成功,会执行自动化测试用例来提前发现私有化功能上的一些问题(通过技术手段来保证:用来检测每个微服务 yaml、Bean 要互斥,不能出现同名的 key 和同名的 Bean)。
3.4.2 服务做了合并,出问题了如何定位代码分支
下面我们再来看下服务做了合并,出问题了如何定位代码分支?如果说单体架构中某个业务模块出现问题/报错了,如何定位到其错误信息具体对应公有云的 git仓库上的 commit/代码片段?
我们在软件化出包的时候,会实时获取到每个微服务对应的 commitId(微服务出包时对应的分支/tag),实时写入/推送到固定的 git 上,业务研发同学可基于当前部署包的版本号来统一查找各个微服务对应的 commitId,从而快速定位到具体公有云微服务对应的代码片段。
4.1 架构维度
从架构上来看:
我们进行架构改造实践落地后,从之前的微服务架构做了大量的服务合并,微服务数量从 30+个 变成个位数 。
4.2 资源维度
从资源维度来看:新架构下占用的机器资源大幅下降。
公有云微服务架构下30+服务,占用大量的cvm 资源,软件化的架构下由于服务做了大量合并,这块仅需 8C16G 就可以运行起来。
4.3 交付客户
我们能力上把十几万用户的公有云使用的 SaaS 服务搬到只使用1台服务器就可以部署运行,那么我们能够满足什么的客户呢?我理解有以下诉求的客户都可以考虑来使用我们。
总而言之,腾讯云微搭低代码提供了应用开发的一站式低代码开发服务,从底层能力迭代至行业级方案,前后端一体可视化构建发布,让您能够完全专注于业务场景,小白也可以极速搭建出成熟、专业的应用。
我们把十几万用户的公有云 SaaS 搬到单台服务器的实践来看,对于客户来说有以下优势:
当然了,也不是说软件化可合并的架构比微服务架构要好,可合并架构在运维方面相对(微服务)而言还是有些弊端。至于是在交付业务时选择软件化架构还是微服务架构,我想这块没有一个严格确切的答案。主要还是要基于业务场景来权衡利弊,在业务发展中不断探索最合适的模式。
软件设计的首要目标应该是满足业务需求,而不是为了设计而设计。如果一个设计没有为业务需求服务,那么它就是过渡设计,是没有意义的。
-End-
原创作者|谢艳祥
本文分享自 腾讯云开发CloudBase 微信公众号,前往查看
如有侵权,请联系 cloudcommunity@tencent.com 删除。
本文参与 腾讯云自媒体同步曝光计划 ,欢迎热爱写作的你一起参与!