限时福利领取


MCP智能客服业务划分的架构设计与工程实践

关键词:MCP、智能客服、多租户、事件总线、Spring Cloud Stream、Redis 分片、灰度发布


先放一张上线当晚的监控大屏,流量一上来,分区数不够直接打爆,老刺激了。

监控大屏

一、背景痛点:业务一多,客服系统就“串味”

MCP(Multi--Channel & -Platform)智能客服要接的不只是自家 App,还有小程序、网页、第三方呼叫中心。每来一个租户,就要:

  1. 数据物理隔离:A 公司工单不能跑到 B 公司库里
  2. 会话跨业务流转:用户先问订单、再问发票,得把上下文带过去
  3. 资源竞争:大促期间 1000 个并发机器人,CPU 和 Redis 连接池瞬间见底

早期“一个大服务 + 字段隔离”的方案,在 5 个租户时还能跑,租户一到 30+,慢查询、锁等待、OOM 三连击,客服同学直接原地爆炸。


二、技术选型:微服务 vs DDD,为什么最后上了事件总线?

维度 微服务切分 领域驱动设计(DDD) 事件总线混合架构
隔离性 服务+库物理隔离,好 聚合根内隔离,略弱 按租户分片,物理隔离
事务一致性 分布式事务,难 聚合内本地事务 最终一致性,幂等补偿
上下文流转 跨服务调用,链路长 聚合事件传递 统一事件总线,松耦合
灰度发布 服务级灰度,粒度粗 聚合级灰度,复杂 事件+路由键,细粒度
运维成本 服务爆炸,成本高 边界模糊,沟通贵 服务数可控,监控集中

结论:

  • 微服务切太碎——发布 50 个容器,凌晨三点眼睛都花;
  • 纯 DDD——边界不好划,开发天天吵“这是哪个聚合的”;
  • 事件总线混合架构——用领域事件做业务划分,用微服务做资源分层,既隔离又解耦,还能顺带给 Kafka 背压机制兜底,真香。

三、实现细节:Spring Cloud Stream + Redis 分片 + 租户级监控

1. 事件路由核心配置

spring:
  cloud:
    stream:
      bindings:
        order-in-0:
          destination: mcp.tenant.${tenant-id}.order
          group: ${spring.application.name}
          consumer:
            partitioned: true
            concurrency: 3
        invoice-in-0:
          destination: mcp.tenant.${tenant-id}.invoice
          group: ${spring.applicationName}

注意 ${tenant-id} 在运行期由路由键动态替换,保证同一租户事件落在同一分区,顺序性幂等性都好做。

2. 带分片策略的 Redis 多租户存储

@Component
public class TenantRedisTemplate {
    @Autowired
    private RedisProperties props;

    private final Map<String, LettuceConnectionFactory> factoryMap = new ConcurrentHashMap<>();

    // 根据租户 ID 选择分片
    private LettuceConnectionFactory getFactory(String tenantId) {
        String shard = ShardUtil.shard(tenantId); // 一致性哈希
        return factoryMap.computeIfAbsent(shard, k -> {
            RedisStandaloneConfiguration cfg = new RedisStandaloneConfiguration();
            cfg.setHostName(props.getShard(shard).getHost());
            cfg.setPort(props.getShard(shard).getPort());
            cfg.setDatabase(props.getShard(shard).getDb());
            return new LettuceConnectionFactory(cfg);
        });
    }

    public RedisTemplate<String, Object> ops(String tenantId) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(getFactory(tenantId));
        template.setKeySerializer(new StringRedisSerializer());
        template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
        template.afterPropertiesSet();
        return template;
    }
}

异常处理:

  • 分片宕机时抛 ShardUnavailableException,由事件监听器捕获后写入死信队列,异步重试 3 次,仍失败发企业告警。
  • 所有写操作带 tenant-id 前缀,防止 key 冲突

3. 幂等性设计

事件体里带 uuid 字段,消费端用 Redis SETNX 做去重:

Boolean absent = redisTemplate.opsForValue()
                             .setIfAbsent("idemp:" + uuid, "1", Duration.ofMinutes(5));
if (Boolean.TRUE.equals(absent)) {
    // 真正处理业务
} else {
    log.warn("duplicate event dropped: {}", uuid);
}

4. 租户级 Prometheus 监控

# 业务埋点
mcp_tenant_event_total{tenant="$tenant",status="success"} 1024
mcp_tenant_event_total{tenant="$tenant",status="dropped"} 3

# Grafana 变量
- name: tenant
  query: label_values(mcp_tenant_event_total, tenant)

面板按租户下拉框切换,谁家的机器人掉线一眼就能定位,再也不甩锅。

四、避坑指南:那些踩到怀疑人生的坑

  1. 会话状态跨业务传递时,千万别直接用 Java 原生序列化

    • 一个包升级,serialVersionUID 对不上,反序列化直接跪;
    • 解决:统一用 Protostuff / JSON,字段兼容表升级。
  2. 动态扩缩容导致会话粘性失效:

    • 早期用 IP-hash,K8s 一弹pod,用户被踢到别的副本,上下文全丢;
    • 解决:网关层做 sticky cookie + 分布式缓存,会话索引落到 Redis,无状态化才是正道。
  3. Kafka 分区数 < 消费并发数:

    • 分区只有 6 个,并发开到 12,背压机制直接失效,CPU 空转;
    • 解决:压测得出“分区 = 2 × 并发”经验值,先测再上,别拍脑袋。

五、性能考量:Kafka 分区数与吞吐量的曲线

我们 8C16G 容器,单并发 1k 消息/s,分区数从 3 加到 24,吞吐变化如下:

分区数 3 6 12 18 24
吞吐(k/s) 3.2 6.1 10.8 12.5 12.6

18 分区后基本到顶,网络带宽先成瓶颈。所以别迷信“分区越多越好”,先压测再上线,省得半夜起来扩容。

六、灰度发布实战:按租户+事件类型双维度

  1. 在配置中心加开关 gray.tenant.list=tenantA,tenantB
  2. 事件发布时,路由键带上 gray=true 后缀;
  3. 消费端通过 Spring Cloud Stream routing 标签过滤:
    @StreamListener(condition = "headers['gray'] == 'true'")
  4. 新版本只消费灰度流量,老版本继续服务稳定租户,回滚秒级完成。

七、开放问题:业务隔离 vs 全局知识库,如何平衡?

目前每个租户独立索引,数据隔离爽了,但新知识图谱训练成本高,小租户用不起。
如果做共享知识库 + 租户私有层,又要解决:

  • 数据权限:不能让 A 租户搜到 B 租户的答案;
  • 模型热更新:共享层升级,不能影响私有层推理;
  • 冷启动:新租户没有数据,怎样快速借用共享语义?

这块我们还在踩坑,欢迎评论区一起头脑风暴,有好的方案我请你喝快乐水!


写完收工,事件总线不是银弹,但把业务划清楚、监控做细致、幂等写到位,MCP 智能客服就能在 30 个租户、万级并发下稳稳跑。
如果你也在做多租户客服,欢迎留言聊聊踩坑史,一起少掉点头发。

限时福利领取


Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐