JeecgBoot 单体与微服务自由切换架构
JeecgBoot 单体与微服务自由切换架构
学习目标
通过本章学习,你将能够:
- 理解 JeecgBoot 如何实现同一代码库的单体/微服务双模式切换
- 掌握 API 层抽象隔离的设计模式
- 了解微服务组件(Nacos、Gateway、Sentinel)的集成方案
- 掌握切换机制对业务代码的零影响设计
项目实践
模块总览
切换核心:API 层双实现
切换的核心在于 jeecg-system-api 模块的双实现:
jeecg-system-api├── jeecg-system-local-api # 单体模式:本地方法调用│ └── ISysBaseAPI.java # 纯接口定义,无远程调用│└── jeecg-system-cloud-api # 微服务模式:Feign 远程调用 └── ISysBaseAPI.java # 同接口名,@FeignClient 注解标记接口定义一致,实现方式不同:
// local-api:纯接口,由 biz 模块实现public interface ISysBaseAPI { Result<?> addSysAnnouncement(SysAnnouncement announcement); void sendSysAnnouncementPush(String message); // ... 更多方法}
// cloud-api:Feign 客户端,由调用方直接注入@FeignClient("jeecg-system")public interface ISysBaseAPI { @PostMapping("/sys/api/addSysAnnouncement") Result<?> addSysAnnouncement(@RequestBody SysAnnouncement announcement);
@PostMapping("/sys/api/sendSysAnnouncementPush") void sendSysAnnouncementPush(@RequestParam String message); // ... 同接口名,远程实现}切换方式:修改启动模块(jeecg-system-start 或 jeecg-system-cloud-start)的 POM 依赖:
<!-- 单体模式 --><dependency> <groupId>org.jeecgframework.boot3</groupId> <artifactId>jeecg-system-local-api</artifactId></dependency>
<!-- 微服务模式 --><dependency> <groupId>org.jeecgframework.boot3</groupId> <artifactId>jeecg-system-cloud-api</artifactId></dependency>业务代码无感知:业务模块(jeecg-system-biz、jeecg-boot-module-airag)只依赖接口,不关心是本地调用还是远程调用。
单体启动
jeecg-system-start├── 依赖: jeecg-system-local-api(本地调用)├── 依赖: jeecg-system-biz(业务逻辑)├── 依赖: jeecg-boot-module-airag(AI 模块)├── 依赖: jeecg-online(低代码引擎)└── 启动类: JeecgSystemApplication.java所有模块在同一个 JVM 进程中运行,方法调用是本地调用。
微服务启动
启动命令: mvn clean package -P SpringCloud
jeecg-cloud-nacos → Nacos 注册/配置中心(端口 8848)jeecg-cloud-gateway → API 网关(端口 9999)jeecg-system-cloud-start → System 服务(端口 8080)jeecg-demo-cloud-start → Demo 服务(独立端口)jeecg-visual/monitor → 监控服务(端口 9111)微服务组件:
| 组件 | 端口 | 职责 |
|---|---|---|
| Nacos | 8848 | 服务注册与发现、统一配置中心 |
| Gateway | 9999 | API 网关、路由转发、限流 |
| Sentinel | 9000 | 熔断降级限流 |
| System Cloud Start | 8080 | 系统服务(用户、角色、权限、AI 模块) |
| XXL-Job | 9080 | 分布式任务调度 |
| 监控 | 9111 | Spring Boot Admin 监控面板 |
Gateway 网关配置
网关是微服务的统一入口:
# Gateway 路由配置spring: cloud: gateway: routes: - id: jeecg-system uri: lb://jeecg-system # 通过 Nacos 服务名路由 predicates: - Path=/jeecg-boot/sys/** - id: jeecg-demo uri: lb://jeecg-demo predicates: - Path=/jeecg-boot/demo/**限流配置(基于 Redis 令牌桶):
spring: cloud: gateway: default-filters: - name: RequestRateLimiter args: redis-rate-limiter.replenishRate: 10 redis-rate-limiter.burstCapacity: 20配置切换机制
通过 Maven Profile 切换:
<!-- 根 pom.xml --><profiles> <profile> <id>SpringCloud</id> <activation> <activeByDefault>false</activeByDefault> </activation> <modules> <module>jeecg-server-cloud</module> </modules> </profile></profiles>- 默认构建(
mvn clean package):只构建单体模块 - 微服务构建(
mvn clean package -P SpringCloud):额外构建微服务模块
统一权限在两种模式下的表现
| 模式 | 认证方式 | 权限传递 |
|---|---|---|
| 单体 | Shiro Filter 拦截,用户信息存 ThreadLocal | 直接读取 ThreadLocal |
| 微服务 | Gateway JWT 验证,用户信息放请求头 | 通过 X-Access-Token 请求头传递 |
关键设计:权限验证逻辑在 jeecg-boot-base-core 中统一实现,通过判断是否为微服务模式选择不同的验证路径:
// 伪代码:权限验证if (isMicroService()) { // 微服务:从请求头获取 Token token = request.getHeader("X-Access-Token");} else { // 单体:从 Session 获取 token = TokenUtils.getTokenByRequest(request);}问题与规避
| 陷阱 | 表现 | 对策 |
|---|---|---|
| local-api 与 cloud-api 接口不一致 | 切换后编译错误或运行时异常 | 保持两套 API 接口定义完全一致 |
| 微服务模式下 Nacos 未启动 | Gateway 无法路由,服务注册失败 | 启动顺序:Nacos → Gateway → System |
| 单体模式下 Shiro 与 Flyway 冲突 | 懒初始化干扰 Flyway 自动配置 | 在 application.yml 中显式排除 Flyway 自动配置 |
| Feign 调用超时 | 大文件上传或长流程操作超时 | 配置 feign.client.config.default.connect-timeout 和 read-timeout |
| Gateway 限流误伤 | 正常请求被限流拦截 | 配置白名单路径(如静态资源、健康检查) |
设计取舍
单体优先 vs 微服务优先
JeecgBoot 选择:单体优先,微服务可选。
| 方案 | 适用场景 |
|---|---|
| 单体优先 | 中小项目、快速原型、团队规模小 |
| 微服务优先 | 大型企业级项目、多团队协作 |
JeecgBoot 定位为低代码平台,目标用户多数是中小团队。单体优先降低了入门门槛,当业务增长需要拆分时,平滑切换为微服务。
API 接口双实现 vs 统一接口适配层
JeecgBoot 选择:接口名一致,通过依赖切换。
| 方案 | 优势 | 代价 |
|---|---|---|
| 接口名一致(当前方案) | 业务代码零修改,依赖替换即可 | 需要维护两套接口定义文件 |
| 统一接口 + 适配层 | 只需维护一套接口 | 适配层增加复杂度,远程调用性能损耗 |
接口名一致是最简单的方案——业务代码完全不受影响。代价是需要维护两份接口定义,但接口变更频率远低于业务代码,管理成本可控。