Skip to content

双模部署

MolanDev Cloud 的核心创新是双模部署架构:同一套代码可以在单体和微服务之间自由切换,业务代码零改动。


什么是双模部署

双模部署指同一套代码可以以单体模式微服务模式部署运行,只需修改一行配置:

yaml
molandev:
  run-mode: single  # 单体模式
  # run-mode: cloud  # 微服务模式
同一套代码

┌───────────────┬───────────────┐
│   单体模式     │   微服务模式    │
│  (开发/小规模) │  (生产/大规模)  │
└───────────────┴───────────────┘

为什么这样设计

传统困境

开发企业级系统时,团队常常面临选择困难:

选择单体:

  • ✅ 部署简单,运维成本低
  • ✅ 开发快速,调试方便
  • ✅ 资源占用少
  • ❌ 业务增长后难以扩展
  • ❌ 代码耦合,维护困难
  • ❌ 无法独立部署模块

选择微服务:

  • ✅ 服务独立,可独立扩展
  • ✅ 技术栈灵活,故障隔离
  • ✅ 团队可并行开发
  • ❌ 架构复杂,初期成本高
  • ❌ 运维困难,需要 DevOps
  • ❌ 分布式事务、网络延迟

双模部署的优势

MolanDev Cloud 的解决方案是:为什么要选?我全都要!

  • 初期快速开发:单体模式,极简部署,快速验证业务
  • 后期平滑演进:业务增长后切换到微服务,无需重构代码
  • 灵活切换:根据业务规模自由选择,不被架构绑架
  • 降低风险:避免过度设计,也避免后期推倒重来

典型应用场景

场景一:创业公司

第 1 天     → 单体模式开发,本地调试,快速迭代
第 100 天   → 业务增长,修改配置切换微服务,拆分订单服务
第 365 天   → 持续演进,按需拆分支付、库存等服务

优势: 初期不过度设计,业务增长后平滑演进。

场景二:内部管理系统

用户少、并发低 → 单体模式,一台服务器搞定
团队扩大后    → 按需拆分模块,不同团队负责不同服务

优势: 根据实际负载选择架构,不盲目追求微服务。

场景三:学习与最佳实践

学习单体架构 → 理解分层架构、业务逻辑
学习微服务   → 理解服务拆分、分布式系统

优势: 一套代码,两种架构,学习成本减半。


核心实现原理

双模部署的实现依赖三个核心设计:接口即服务配置隔离单体合并

1. 接口即服务

核心思想: 所有服务间调用都通过 Feign 接口,不直接调用 Service。

java
// 定义 Feign 接口
@FeignClient(
    name = "${molandev.service-name.molandev-base:molandev-base}", 
    path = "/feign/user"
)
public interface SysUserApi {
    @GetMapping("/{id}")
    UserDto getById(@PathVariable String id);
}

// 业务代码调用(单体/微服务完全一致)
@Service
public class OrderService {
    @Autowired
    private SysUserApi userApi;  // 注入接口

    public void createOrder(OrderDTO order) {
        // 获取用户信息 - 单体本地调用,微服务HTTP调用
        UserDto user = userApi.getById(order.getUserId());
    }
}

单体模式: Feign 接口通过 molandev-rpc 框架自动转为本地 Bean 调用
微服务模式: Feign 接口通过 HTTP 远程调用

为什么不用直接调用 Service?

方式单体模式微服务模式切换成本
直接调用 Service✅ 可以❌ 不行需要重构代码
Feign 接口✅ 自动转本地✅ HTTP 远程零改动

2. 配置隔离

通过配置文件控制部署模式,不同模式使用不同的中间件配置。

单体模式配置:

yaml
molandev:
  run-mode: single          # 单体模式
  lock:
    type: memory            # 使用内存锁
  security:
    mode: LOCAL             # 本地认证模式

spring:
  cloud:
    nacos:
      discovery:
        enabled: false      # 不需要注册中心

微服务模式配置:

yaml
molandev:
  run-mode: cloud           # 微服务模式
  security:
    mode: CLOUD             # 云端认证模式

spring:
  application:
    name: molandev-base
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848  # 注册中心

3. 事件驱动适配

java
// 发布事件(单体/微服务完全一致)
EventUtil.publish(new UserRegisteredEvent(user));

// 监听事件
@MolanListener
public void onUserRegistered(UserRegisteredEvent event) {
    emailService.sendWelcomeEmail(event.getUser().getEmail());
}
  • 单体模式: 本地事件总线(Spring Event),进程内分发
  • 微服务模式: RabbitMQ 分布式消息,跨服务通信

4. 定时任务双模

java
@TaskSchedule("NotifyExpiredUser")
public void execute() {
    // 查询即将过期的用户
    List<SysUserEntity> users = userService.findUsersSoonExpire();
    for (SysUserEntity user : users) {
        // 发送邮件提醒
        msgSendApi.send(buildNotifyEvent(user));
    }
}
  • 单体模式: 本地直接调用方法
  • 微服务模式: HTTP 远程调度到对应服务
  • 分布式锁: 多节点部署时防止重复执行

5. 单体合并

单体模式通过 molandev-standalone-service 模块启动,合并多个业务模块。

xml
<!-- molandev-standalone-service/pom.xml -->
<dependencies>
    <!-- 引入基础服务,排除微服务依赖 -->
    <dependency>
        <groupId>com.molandev</groupId>
        <artifactId>molandev-base</artifactId>
        <exclusions>
            <exclusion>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
            </exclusion>
            <exclusion>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-openfeign</artifactId>
            </exclusion>
        </exclusions>
    </dependency>
    <!-- 引入知识库服务 -->
    <dependency>
        <groupId>com.molandev</groupId>
        <artifactId>molandev-knowledge</artifactId>
    </dependency>
    <!-- 引入 AI 修仙服务 -->
    <dependency>
        <groupId>com.molandev</groupId>
        <artifactId>molandev-xiuxian</artifactId>
    </dependency>
</dependencies>

架构对比

特性单体模式微服务模式
部署形态单个 JAR多个独立服务
接口调用本地 Bean 注入HTTP 远程调用
事件通信进程内内存分发RabbitMQ 消息队列
定时任务本地直接调用HTTP 远程调度
分布式锁内存锁Redis 分布式锁
配置管理本地配置文件Nacos 集中管理
中间件MySQL + RedisMySQL + Redis + RabbitMQ + Nacos
启动时间~10 秒~1 分钟
内存占用~500MB~1.5GB
调试难度简单中等
水平扩展不支持支持
故障隔离不支持支持
运维复杂度

切换流程

从单体切换到微服务

  1. 修改配置

    yaml
    molandev:
      run-mode: single → cloud
  2. 添加中间件

    • 启动 RabbitMQ
    • 启动 Nacos
    • 导入配置文件到 Nacos
  3. 打包各个服务

    bash
    cd molandev-backend
    mvn clean package -DskipTests
  4. 部署各个服务

    • 部署 Gateway
    • 部署 molandev-base
    • 部署 molandev-knowledge
    • 部署 molandev-xiuxian
  5. 业务代码零改动!

从微服务切换到单体

  1. 修改配置

    yaml
    molandev:
      run-mode: cloud → single
  2. 使用 standalone 模块启动

    bash
    cd molandev-standalone-service
    mvn spring-boot:run
  3. 业务代码零改动!


技术决策背后的思考

为什么选择 Feign 而不是 RPC 框架?

选择 Feign 的理由:

  • Spring Cloud 原生支持,生态完善
  • 声明式 HTTP 客户端,代码简洁
  • 单体模式下可通过自定义 Client 转为本地调用
  • 学习成本低,团队容易接受

不使用 gRPC/Dubbo 的原因:

  • 增加额外学习成本
  • 单体模式下优势不明显
  • 对于企业级管理系统,HTTP 性能足够

为什么禁止数据库外键?

sql
-- ❌ 禁止:不要在数据库中配置外键约束
ALTER TABLE sys_user_role
ADD CONSTRAINT fk_user FOREIGN KEY (user_id) REFERENCES sys_user(id);

-- ✅ 正确:通过代码维护关联关系

设计理由:

  • 灵活性: 保持表结构灵活,便于后期扩展
  • 性能: 避免级联操作影响性能
  • 微服务友好: 微服务架构下无法使用跨库外键
  • 代码可控: 关联关系由业务代码维护,逻辑更清晰

为什么不使用 XML 映射 SQL?

java
// ✅ 正确:使用 JDK 17 三引号 + @Select 注解
@Select("""
    SELECT u.*
    FROM sys_user u
    INNER JOIN sys_user_role ur ON u.id = ur.user_id
    WHERE ur.role_id = #{roleId}
    """)
List<SysUserEntity> listByRoleId(@Param("roleId") String roleId);

// ❌ 禁止:使用 XML 文件

设计理由:

  • 减少文件数量: 无需维护 XML 文件
  • SQL 更直观: 直接在看代码时看到 SQL
  • JDK 17 三引号: 使多行 SQL 更易读
  • 单表查询用 Wrapper: MyBatis Plus 的 LambdaQueryWrapper 更简洁

为什么网关不执行权限校验?

旧版本问题:

  • 网关需要从各服务拉取路径与权限码的映射
  • 服务上下线时需要刷新映射缓存
  • 路径匹配规则复杂,容易出错

新架构优势:

  • 网关只做认证和权限转发
  • 后端通过 AOP 直接读取 @HasPermission 注解
  • 无需维护路径映射,职责清晰
  • 扩展性更好,新增服务无需配置网关映射

最佳实践

开发环境使用单体

yaml
# application-local.yml
molandev:
  run-mode: single

优势:

  • 快速启动,调试方便
  • 无需启动多个中间件
  • 断点调试简单

生产环境使用微服务

yaml
# application-prod.yml
molandev:
  run-mode: cloud

优势:

  • 服务独立部署,可独立扩展
  • 故障隔离,一个服务异常不影响其他
  • 不同服务可使用不同技术栈

按需拆分

不要一开始就拆分所有服务,建议按以下顺序逐步拆分:

  1. 第一阶段: 保持单体,快速验证业务
  2. 第二阶段: 拆分负载高的模块(如文件服务)
  3. 第三阶段: 拆分业务独立的模块(如订单服务)
  4. 第四阶段: 按团队职责拆分

总结

MolanDev Cloud 的双模部署实现了:

  • 一套代码: 单体/微服务自由切换,业务代码零改动
  • 接口即服务: Feign 接口自动适配本地/远程调用
  • 事件驱动: 统一事件总线,自动适配内存/MQ
  • 灵活切换: 一行配置完成切换
  • 降低风险: 避免过度设计,平滑演进

这是 MolanDev Cloud 的核心创新,也是区别于其他管理系统框架的最大优势。

下一步