4、微服务架构设计:微服务拆分原则、服务边界划分、服务间通信(RPC/消息队列)、API网关设计、服务注册与发现

好,咱们进入第四章。微服务架构设计。

说实话,这一章是CRM系统架构里最考验功力的部分。很多团队不是倒在业务逻辑上,而是倒在服务拆分的「度」上。拆得太细,运维成本爆炸;拆得太粗,又享受不到微服务的红利。

我个人习惯,在动手拆分前,先想清楚三个问题:为什么拆?拆成什么样?拆完后怎么连? 咱们一个一个来聊。

4.1 微服务拆分原则:别为了拆而拆

我见过最典型的反面教材,是把一个用户表拆成「用户基本信息服务」「用户扩展信息服务」「用户权限服务」三个独立部署单元。结果每次查询用户详情,要调三次接口,延迟直接翻倍。

所以,拆分的第一原则是:高内聚、低耦合。说白了,就是把经常一起变化、一起访问的数据,放在同一个服务里。

核心原则清单:

  • 业务能力原则:按业务领域拆分。比如CRM里,客户管理、销售管理、订单管理、营销活动,天然就是不同的业务能力。
  • 闭包原则:一个服务的变更,尽量不要影响其他服务。我曾经把一个「客户评分计算」逻辑放在订单服务里,结果每次调整评分规则,订单服务都要重新发布,风险极高。
  • 数据主权原则:每个服务拥有自己的数据库。别搞共享数据库,那是伪微服务。
  • 团队规模原则:一个服务由一个小团队(2-5人)独立维护。如果服务太大,团队沟通成本会急剧上升。

我的经验:刚开始拆分时,宁粗勿细。先拆成5-8个核心服务,运行稳定后再考虑进一步拆分。我见过一个项目,一开始就拆了30多个服务,结果半年后回退成单体——运维搞不定。

4.2 服务边界划分:用DDD来画圈

服务边界怎么划?我强烈推荐用领域驱动设计(DDD)里的「限界上下文」来指导。

你想想看,CRM系统里,「客户」这个概念,在销售眼里是「商机来源」,在客服眼里是「服务对象」,在财务眼里是「账期主体」。它们虽然都叫客户,但关心的属性、行为完全不同。

所以,边界划分的核心是:一个限界上下文,就是一个微服务

举个例子,CRM里典型的服务边界:

服务名称 核心职责 数据实体
客户服务 客户基本信息管理、客户分层、客户标签 Customer, CustomerTag, CustomerGroup
销售服务 商机管理、销售漏斗、报价单 Opportunity, Lead, Quote
订单服务 订单创建、订单履约、订单状态机 Order, OrderItem, OrderStatus
营销服务 活动配置、优惠券发放、触达记录 Campaign, Coupon, TouchRecord
用户服务 员工账号、角色权限、组织架构 User, Role, Department

注意:我曾经犯过一个错误,把「客户服务」和「销售服务」放在同一个数据库里,因为觉得「客户和商机关系太紧密了」。结果后来销售团队要频繁改商机字段,每次都要和客户团队协调数据库变更,痛苦不堪。记住:服务边界 = 数据库边界

4.3 服务间通信:RPC vs 消息队列

服务拆开了,怎么通信?这是个大问题。我一般分两种情况处理:

4.3.1 同步通信:用RPC

适合需要实时返回结果的场景。比如「查询客户详情时,同时展示该客户的最新订单」。这种场景,用RPC最直接。

我个人习惯用gRPC,原因很简单:

  • 基于HTTP/2,性能比REST高一个量级
  • 强类型接口定义,生成客户端代码,减少沟通成本
  • 支持流式传输,适合大数据量场景
// 示例:客户服务调用订单服务的gRPC接口
service OrderService {
    rpc GetLatestOrder (GetLatestOrderRequest) returns (OrderResponse);
}

message GetLatestOrderRequest {
    string customer_id = 1;
}

message OrderResponse {
    string order_id = 1;
    double total_amount = 2;
    string status = 3;
}

避坑指南:我曾经在RPC调用链上超过3层时,遇到过一次雪崩。A调B,B调C,C挂了,B的线程池被打满,A也跟着挂了。所以,同步调用链不要超过2层。超过2层,考虑用异步。

4.3.2 异步通信:用消息队列

适合「最终一致性」的场景。比如「订单创建后,需要通知营销服务发放积分、通知仓储服务准备发货」。这些操作不需要立即完成,但必须保证最终都执行。

我常用的消息队列是RabbitMQ和Kafka:

  • RabbitMQ:适合业务消息,路由灵活,支持死信队列。我一般用它处理「订单状态变更通知」这类业务事件。
  • Kafka:适合高吞吐、日志类数据。比如「用户行为埋点」「操作审计日志」,一天几千万条,Kafka扛得住。
// 示例:订单服务发送消息到RabbitMQ
// 订单创建后,发布事件
OrderCreatedEvent event = new OrderCreatedEvent();
event.setOrderId("ORD20241001");
event.setCustomerId("CUST001");
event.setTotalAmount(2999.00);

rabbitTemplate.convertAndSend("order.exchange", "order.created", event);
// 营销服务和仓储服务各自监听这个队列,做自己的事情

我的选择标准:

  • 需要实时响应 → RPC
  • 不需要实时响应,但必须可靠 → 消息队列
  • 需要广播给多个服务 → 消息队列
  • 需要削峰填谷 → 消息队列

4.4 API网关设计:统一入口,隔离复杂性

服务拆开后,客户端(比如前端、移动端)不可能直接调用几十个微服务。这时候就需要API网关。

API网关的核心职责,我总结为四个字:入口、隔离

  • 统一入口:所有外部请求都走网关,客户端只需要知道网关地址。
  • 路由转发:根据请求路径,转发到对应的微服务。比如 /api/customer/** 转到客户服务。
  • 认证鉴权:在网关层统一做Token校验,避免每个服务都写一遍认证逻辑。
  • 限流熔断:防止某个服务被突发流量打垮。我习惯用Sentinel做限流,配置简单,效果明显。

我常用的网关是Spring Cloud Gateway。它基于WebFlux,性能比Zuul好很多。

// 示例:Spring Cloud Gateway 路由配置
spring:
  cloud:
    gateway:
      routes:
        - id: customer-service
          uri: lb://customer-service
          predicates:
            - Path=/api/customer/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 100
                redis-rate-limiter.burstCapacity: 200
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1

注意:不要把业务逻辑写在网关里。我见过有人把「客户积分计算」写在网关的Filter里,理由是「这样响应快」。结果每次改积分规则,网关都要重启,影响所有服务的访问。网关只做通用能力,业务逻辑下沉到微服务里。

4.5 服务注册与发现:动态感知,弹性伸缩

微服务实例是动态的。今天部署了3个客户服务实例,明天可能扩到5个,后天可能某个实例挂了。服务之间怎么知道彼此在哪里?

这就需要一个「通讯录」——服务注册中心。

我常用的注册中心是Nacos。原因很简单:

  • 支持CP和AP模式切换,灵活
  • 自带配置中心,省得再搭一套
  • 控制台界面清晰,运维友好

工作流程是这样的:

  1. 服务启动时,向Nacos注册自己的IP和端口
  2. 服务每隔一段时间(比如30秒)发送心跳,告诉Nacos「我还活着」
  3. 服务调用方(比如API网关)从Nacos拉取服务列表,缓存到本地
  4. 如果某个实例挂了,Nacos会把它从列表里剔除,调用方下次拉取时就不会再调它
// 示例:Spring Boot 集成 Nacos 注册
spring:
  application:
    name: customer-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.100:8848
        namespace: crm-prod
        group: CRM_GROUP

我的经验:注册中心一定要做高可用部署。我见过一个事故,Nacos单节点挂了,所有服务都找不到彼此,整个CRM系统瘫痪了半小时。后来我们部署了3节点Nacos集群,配合VIP,再没出过类似问题。

嗯,到这里,微服务架构设计的核心内容就讲完了。总结一下:

  • 拆分原则:高内聚低耦合,按业务能力拆
  • 边界划分:用DDD限界上下文画圈
  • 通信方式:实时用RPC,异步用消息队列
  • API网关:统一入口,做认证、限流、路由
  • 注册发现:用Nacos做动态通讯录

下一章,咱们聊聊数据架构设计。CRM系统里数据量最大的就是客户行为数据和订单数据,怎么存、怎么查、怎么保证一致性,都是硬骨头。