DDD-008:DDD 与微服务架构
8.1 微服务架构概述
8.1.1 微服务定义
【原理】
微服务架构是一种架构风格,将单一应用程序划分成一组小的服务,每个服务运行在独立的进程中,服务间通过轻量级通信机制(通常是HTTP资源API)进行协作。
核心特征:
- 服务自治:每个服务独立开发、测试、部署
- 去中心化:数据管理去中心化,每个服务管理自己的数据库
- 轻量通信:服务间通过REST、gRPC或消息队列通信
- 按业务能力划分:服务边界基于业务能力而非技术层
【历史架构问题】
传统单体架构:
// ❌ 单体架构:所有功能在一个应用中
@RestController
public class MonolithicController {
@Autowired private UserRepository userRepository;
@Autowired private OrderRepository orderRepository;
@Autowired private ProductRepository productRepository;
@Autowired private PaymentRepository paymentRepository;
@Autowired private ShippingRepository shippingRepository;
@PostMapping("/orders")
public Order createOrder(@RequestBody OrderDTO dto) {
// 用户验证
User user = userRepository.findById(dto.getUserId());
// 商品查询
Product product = productRepository.findById(dto.getProductId());
// 订单创建
Order order = orderRepository.save(new Order(user, product));
// 支付处理
paymentRepository.save(new Payment(order));
// 物流创建
shippingRepository.save(new Shipping(order));
return order;
}
}
存在问题:
- ❌ 代码耦合:所有模块在同一代码库,相互依赖
- ❌ 部署风险:修改一行代码需要重新部署整个应用
- ❌ 技术栈绑定:所有模块必须使用相同技术栈
- ❌ 扩展受限:无法按需扩展单个功能模块
- ❌ 团队协作困难:多人修改同一代码库,冲突频繁
【DDD 如何解决】
基于限界上下文的微服务拆分:
// ✅ 订单服务:独立部署
@RestController
@RequestMapping("/orders")
public class OrderServiceController {
private final OrderApplicationService orderService;
private final EventPublisher eventPublisher;
@PostMapping
public OrderDTO createOrder(@RequestBody CreateOrderCommand cmd) {
Order order = orderService.createOrder(cmd);
eventPublisher.publish(new OrderCreatedEvent(order.getId()));
return OrderDTO.from(order);
}
}
// ✅ 支付服务:独立部署,通过事件驱动
@Component
public class PaymentEventHandler {
@EventListener
public void handle(OrderCreatedEvent event) {
// 异步处理支付
}
}
【设计优势】
| 维度 | 单体架构 | 微服务架构(DDD指导) |
|---|---|---|
| 部署 | 整体部署 | 独立部署 |
| 扩展 | 整体扩展 | 按需扩展 |
| 技术栈 | 统一 | 多样化 |
| 团队协作 | 集中式 | 分布式自治 |
| 故障影响 | 全局影响 | 局部影响 |
8.1.2 微服务优势与挑战
优势:
- 独立部署:服务独立部署,不影响其他服务
- 技术多样性:不同服务可选择最适合的技术栈
- 弹性扩展:按需扩展单个服务,资源利用率高
- 团队自治:不同团队负责不同服务,并行开发
- 故障隔离:单个服务故障不影响整体系统
挑战:
- 分布式复杂性:网络延迟、部分失败、分布式事务
- 服务发现与治理:服务注册、负载均衡、熔断降级
- 数据一致性:跨服务事务、最终一致性保证
- 运维复杂性:监控、日志、链路追踪
- 测试复杂性:集成测试、端到端测试
8.1.3 与单体架构的对比
| 维度 | 单体架构 | 微服务架构 |
|---|---|---|
| 部署 | 整体部署 | 独立部署 |
| 扩展 | 整体扩展 | 按需扩展 |
| 技术栈 | 统一 | 多样化 |
| 团队协作 | 集中式 | 分布式自治 |
| 复杂性 | 代码复杂 | 运维复杂 |
| 故障影响 | 全局影响 | 局部影响 |
| 启动时间 | 较长 | 较短 |
| 开发调试 | 简单 | 复杂 |
8.2 限界上下文与微服务的映射
8.2.1 理想情况:一对一映射
【原理】
DDD的限界上下文与微服务存在天然的映射关系。理想情况下,一个限界上下文对应一个微服务。
// 限界上下文与微服务一一对应
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 订单上下文 │ │ 商品上下文 │ │ 用户上下文 │
│ OrderContext │ │ ProductContext │ │ UserContext │
└────────┬────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ 订单服务 │ │ 商品服务 │ │ 用户服务 │
│ Order Service │ │ Product Service │ │ User Service │
└─────────────────┘ └─────────────────┘ └─────────────────┘
【设计优势】
- 清晰的边界:每个服务职责明确
- 独立演进:服务可独立迭代
- 团队对齐:一个团队负责一个上下文
8.2.2 现实考量
【历史架构问题】
实际项目中,简单的一对一映射往往不够:
// ❌ 过度拆分问题:小功能单独成服务
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ 地址服务 │ │ 手机号服务 │ │ 邮箱服务 │
│ AddressSvc │ │ PhoneSvc │ │ EmailSvc │
└──────────────┘ └──────────────┘ └──────────────┘
// 问题:服务数量过多,运维负担重,调用链过长
现实考量因素:
-
团队规模
- 小团队:合并多个上下文到一个服务
- 大团队:一个上下文一个服务,甚至多个服务
-
性能要求
- 高频调用:考虑合并到同一服务减少网络开销
- 低频调用:可独立部署
-
部署频率
- 高频变更:独立服务,快速迭代
- 稳定模块:可合并到其他服务
-
数据依赖
- 强依赖:考虑合并
- 弱依赖:可独立
【DDD 如何解决】
灵活的映射策略:
// ✅ 合理的服务划分
┌─────────────────────────────────────────┐
│ 用户上下文 (User Context) │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ 用户 │ │ 地址 │ │ 认证 │ │
│ │User │ │Address │ │Auth │ │
│ └────────┘ └────────┘ └────────┘ │
└─────────────────┬───────────────────────┘
▼
┌─────────────────┐
│ 用户服务 │
│ User Service │
│ (合并部署) │
└─────────────────┘
// 订单上下文独立部署(高频变更)
┌─────────────────┐
│ 订单上下文 │
└────────┬────────┘
▼
┌─────────────────┐
│ 订单服务 │
│ Order Service │
│ (独立部署) │
└─────────────────┘
8.2.3 演进策略
从单体到微服务的演进:
// 阶段1:单体模块化
┌─────────────────────────────────────────────┐
│ Monolithic Application │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Order │ │ Product │ │ User │ │
│ │ Module │ │ Module │ │ Module │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────────┘
// 阶段2:模块边界清晰化(DDD限界上下文)
┌─────────────────────────────────────────────┐
│ Modular Monolith (DDD) │
│ ┌─────────────┐ ┌─────────────┐ │
│ │OrderContext │ │ProductContext│ │
│ │ (Bounded) │ │ (Bounded) │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────┘
// 阶段3:提取高频变更的上下文为微服务
┌──────────────────────┐ ┌──────────────┐
│ Modular Monolith │ │ Order Service │
│ ┌────┐ ┌────┐ │ │ (独立部署) │
│ │User│ │Pay │ │◄──►└──────────────┘
│ └────┘ └────┘ │
└──────────────────────┘
// 阶段4:完整微服务架构
┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐
│ Order │ │ Product│ │ User │ │Payment │
│Service │ │Service │ │Service │ │Service │
└────────┘ └────────┘ └────────┘ └────────┘
8.3 微服务的边界划分原则
8.3.1 业务边界优先
【原理】
微服务的划分应基于业务能力,而非技术分层。
【历史架构问题】
按技术层拆分:
// ❌ 错误:按技术层拆分
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ Web Service│ │API Service │ │ Data Service │
│ (前端) │ │ (接口层) │ │ (数据层) │
└─────────────┘ └─────────────┘ └─────────────┘
// 问题:
// 1. 所有业务变更都需要修改多个服务
// 2. 跨服务事务复杂
// 3. 服务职责不清晰
【DDD 如何解决】
按业务能力拆分:
// ✅ 正确:按业务能力拆分
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ OrderService│ │ProductSvc │ │PaymentService│
│ (订单业务) │ │(商品业务) │ │ (支付业务) │
└─────────────┘ └─────────────┘ └─────────────┘
// 优势:
// 1. 业务变更只需修改一个服务
// 2. 服务职责清晰
// 3. 团队自治
8.3.2 数据边界
【原理】
每个微服务应该有自己独立的数据库,避免数据层耦合。
【历史架构问题】
共享数据库:
// ❌ 共享数据库:强耦合
┌─────────────┐ ┌─────────────┐
│ OrderService│ │ProductService│
└──────┬──────┘ └──────┬──────┘
│ │
└───────┬───────┘
▼
┌───────────────┐
│ Shared DB │
│ ┌────┐┌────┐ │
│ │order││product│ │
│ └────┘└────┘ │
└───────────────┘
// 问题:
// 1. 表结构变更影响多个服务
// 2. 数据库成为性能瓶颈
// 3. 无法独立扩展
【DDD 如何解决】
数据库独占:
// ✅ 独占数据库:解耦
┌─────────────┐ ┌─────────────┐
│ OrderService│ │ProductService│
└──────┬──────┘ └──────┬──────┘
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
│ Order DB │ │ Product DB │
│ (独立) │ │ (独立) │
└─────────────┘ └─────────────┘
// 需要数据时通过API调用
ProductDTO product = productClient.getProduct(productId);
8.3.3 团队边界(康威定律)
【原理】
康威定律:系统架构反映组织的沟通结构。
“设计系统的组织,其产生的设计等同于组织间的沟通结构。” —— Melvin Conway
实践建议:
- 一个团队负责一个或多个服务
- 服务边界与团队边界对齐
- 避免跨团队频繁协作的服务
// 团队与服务对齐
┌─────────────────────────────────────────┐
│ 订单团队 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ OrderService│ │CartService │ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────┘
┌─────────────────────────────────────────┐
│ 商品团队 │
│ ┌─────────────┐ ┌─────────────┐ │
│ │ProductService│ │InventorySvc│ │
│ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────┘
8.3.4 技术边界(谨慎使用)
【历史架构问题】
过度按技术拆分:
// ❌ 错误:按技术组件拆分
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ Auth Svc │ │ Log Svc │ │ Cache Svc │
│ (认证服务) │ │ (日志服务) │ │ (缓存服务) │
└──────────────┘ └──────────────┘ └──────────────┘
// 问题:这些是技术关注点,不是业务能力
【DDD 如何解决】
技术能力作为支撑,嵌入业务服务:
// ✅ 技术能力作为基础设施
┌─────────────────────────────────────────┐
│ Order Service │
│ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │ Auth │ │ Log │ │ Cache │ │
│ │(内部) │ │(内部) │ │(内部) │ │
│ └────────┘ └────────┘ └────────┘ │
└─────────────────────────────────────────┘
8.4 微服务间的通信模式
8.4.1 同步通信
REST API
// Feign 声明式调用
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/api/products/{id}")
ProductDTO getProduct(@PathVariable Long id);
@PostMapping("/api/products/{id}/reserve")
void reserveStock(@PathVariable Long id, @RequestParam int quantity);
}
// 订单服务调用商品服务
@Service
public class OrderApplicationService {
private final ProductClient productClient;
public Order createOrder(CreateOrderCommand cmd) {
// 同步调用商品服务
ProductDTO product = productClient.getProduct(cmd.getProductId());
// 检查库存
if (product.getStock() < cmd.getQuantity()) {
throw new InsufficientStockException();
}
// 预留库存
productClient.reserveStock(cmd.getProductId(), cmd.getQuantity());
// 创建订单...
}
}
gRPC
// product.proto
syntax = "proto3";
package com.example.product;
service ProductService {
rpc GetProduct(GetProductRequest) returns (ProductResponse);
rpc ReserveStock(ReserveStockRequest) returns (ReserveStockResponse);
}
message GetProductRequest {
int64 id = 1;
}
message ProductResponse {
int64 id = 1;
string name = 2;
int32 stock = 3;
}
// gRPC 客户端
@Service
public class ProductGrpcClient {
private final ProductServiceGrpc.ProductServiceBlockingStub stub;
public ProductDTO getProduct(Long id) {
GetProductRequest request = GetProductRequest.newBuilder()
.setId(id)
.build();
ProductResponse response = stub.getProduct(request);
return new ProductDTO(response.getId(), response.getName(), response.getStock());
}
}
适用场景:
- 需要实时响应的场景
- 查询类操作
- 简单的业务流程
问题:
- 调用链长时延迟累积
- 服务提供方故障影响调用方
- 紧耦合
8.4.2 异步通信
消息队列
// 订单服务发布事件
@Service
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final MessageQueue messageQueue;
@Transactional
public Order createOrder(CreateOrderCommand cmd) {
Order order = Order.create(cmd);
orderRepository.save(order);
// 发布订单创建事件
OrderCreatedEvent event = new OrderCreatedEvent(
order.getId(),
order.getUserId(),
order.getTotalAmount()
);
messageQueue.publish("order.created", event);
return order;
}
}
// 库存服务订阅事件
@Component
public class InventoryEventHandler {
@RabbitListener(queues = "order.created")
public void handleOrderCreated(OrderCreatedEvent event) {
// 异步扣减库存
inventoryService.reserveStock(event.getOrderId());
}
}
// 支付服务订阅事件
@Component
public class PaymentEventHandler {
@RabbitListener(queues = "order.created")
public void handleOrderCreated(OrderCreatedEvent event) {
// 异步创建支付单
paymentService.createPayment(event.getOrderId(), event.getAmount());
}
}
事件驱动架构
// 领域事件定义
public abstract class DomainEvent {
private final LocalDateTime occurredOn;
private final String eventId;
protected DomainEvent() {
this.occurredOn = LocalDateTime.now();
this.eventId = UUID.randomUUID().toString();
}
}
// 订单创建事件
public class OrderCreatedEvent extends DomainEvent {
private final Long orderId;
private final Long userId;
private final BigDecimal totalAmount;
private final List<OrderItemDTO> items;
}
// 事件总线
@Service
public class EventBus {
private final ApplicationEventPublisher publisher;
public void publish(DomainEvent event) {
publisher.publishEvent(event);
// 同时发送到消息队列(跨服务)
messageQueue.publish(event.getClass().getSimpleName(), event);
}
}
适用场景:
- 不需要实时响应的场景
- 解耦服务依赖
- 削峰填谷
- 最终一致性场景
优势:
- 服务解耦
- 故障隔离
- 异步处理提高响应速度
- 天然支持重试
8.5 避免分布式单体
8.5.1 什么是分布式单体?
【原理】
分布式单体(Distributed Monolith):虽然拆分成了多个服务,但服务间紧密耦合,每次变更都需要修改多个服务,部署时需要协调多个服务。
【历史架构问题】
// ❌ 分布式单体的典型特征
// 1. 跨服务的"大事务"
@Service
public class OrderService {
@Transactional // 这个事务根本不起作用!
public void createOrder(OrderDTO dto) {
orderRepository.save(order);
productService.decreaseStock(productId, quantity); // 跨服务
couponService.useCoupon(couponId); // 跨服务
pointsService.addPoints(userId, amount); // 跨服务
notificationService.sendSMS(userId, "订单创建成功"); // 跨服务
}
}
// 2. 服务间的循环依赖
OrderService -> ProductService -> InventoryService -> OrderService
// 3. 共享数据库
OrderService ──┐
ProductService ─┼──► Shared Database
UserService ────┘
// 4. 每次发布需要协调多个服务
常见表现:
- 变更传播:修改一个功能需要改动多个服务
- 部署依赖:发布时必须按特定顺序部署多个服务
- 测试困难:修改一个服务需要测试所有相关服务
- 故障扩散:一个服务故障导致整个系统不可用
8.5.2 如何避免分布式单体?
【DDD 如何解决】
1. 正确的限界上下文划分
// ✅ 高内聚、低耦合的上下文划分
// 订单上下文:包含订单、购物车、订单项
// 所有订单相关的业务逻辑都在这个上下文内
public class Order extends AggregateRoot<OrderId> {
private OrderId id;
private CustomerId customerId;
private OrderStatus status;
private List<OrderItem> items;
private Money totalAmount;
// 业务逻辑封装在聚合内
public void addItem(ProductId productId, Money price, int quantity) {
// 验证
if (this.status != OrderStatus.DRAFT) {
throw new IllegalStateException("只能修改草稿订单");
}
// 添加商品
OrderItem item = new OrderItem(productId, price, quantity);
this.items.add(item);
// 重新计算总额
this.totalAmount = calculateTotal();
}
public void submit() {
if (items.isEmpty()) {
throw new IllegalStateException("订单不能为空");
}
this.status = OrderStatus.SUBMITTED;
// 发布领域事件,而不是直接调用其他服务
registerEvent(new OrderSubmittedEvent(this.id, this.totalAmount));
}
}
2. 使用领域事件解耦
// ✅ 事件驱动:服务解耦
// 订单服务只关心订单提交,不关心后续流程
public class OrderApplicationService {
public void submitOrder(OrderId orderId) {
Order order = orderRepository.findById(orderId);
order.submit();
orderRepository.save(order);
// 发布事件,不调用其他服务
eventPublisher.publish(new OrderSubmittedEvent(order));
}
}
// 库存服务独立响应
@Component
public class InventoryEventHandler {
@EventListener
@Async
public void handle(OrderSubmittedEvent event) {
inventoryService.reserveStock(event.getItems());
}
}
// 支付服务独立响应
@Component
public class PaymentEventHandler {
@EventListener
@Async
public void handle(OrderSubmittedEvent event) {
paymentService.createPayment(event.getOrderId(), event.getAmount());
}
}
3. 每个服务独占数据库
// ✅ 数据库独占
@SpringBootApplication
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
@Bean
@Primary
public DataSource orderDataSource() {
// 订单服务自己的数据库
return DataSourceBuilder.create()
.url("jdbc:mysql://order-db:3306/orders")
.build();
}
}
// 需要商品信息时,通过API获取,而不是直接查询商品库
@Service
public class OrderApplicationService {
private final ProductClient productClient;
public ProductInfo getProductInfo(ProductId productId) {
// 调用商品服务API
return productClient.getProduct(productId);
}
}
4. API版本管理
// ✅ 稳定的API契约
// 商品服务API(版本化)
@RestController
@RequestMapping("/api/v1/products")
public class ProductController {
@GetMapping("/{id}")
public ProductDTO getProduct(@PathVariable Long id) {
// 稳定的API契约
}
}
// 如果需要变更,创建新版本
@RestController
@RequestMapping("/api/v2/products")
public class ProductV2Controller {
@GetMapping("/{id}")
public ProductV2DTO getProduct(@PathVariable Long id) {
// 新版本的API
}
}
5. 使用防腐层
// ✅ 防腐层隔离外部服务变更
// 适配器接口
public interface ProductAdapter {
ProductInfo getProduct(ProductId productId);
void reserveStock(ProductId productId, int quantity);
}
// 实现:调用商品服务
@Component
public class ProductAdapterImpl implements ProductAdapter {
private final ProductClient productClient;
@Override
public ProductInfo getProduct(ProductId productId) {
ProductDTO dto = productClient.getProduct(productId.getValue());
// 转换为内部模型
return new ProductInfo(
new ProductId(dto.getId()),
dto.getName(),
new Money(dto.getPrice())
);
}
@Override
public void reserveStock(ProductId productId, int quantity) {
productClient.reserveStock(productId.getValue(), quantity);
}
}
// 订单服务使用防腐层,而非直接调用
@Service
public class OrderApplicationService {
private final ProductAdapter productAdapter; // 依赖接口
public Order createOrder(CreateOrderCommand cmd) {
ProductInfo product = productAdapter.getProduct(cmd.getProductId());
// ...
}
}
8.6 微服务与 DDD 的最佳实践
8.6.1 服务拆分策略
拆分原则:
- 从限界上下文出发
- 考虑团队规模和能力
- 考虑性能和可用性要求
- 渐进式拆分
拆分决策流程:
// 决策流程
public class ServiceSplitDecision {
public boolean shouldSplit(BoundedContext context) {
// 1. 变更频率分析
if (getChangeFrequency(context) > threshold) {
return true;
}
// 2. 团队规模分析
if (getTeamSize(context) > 8) {
return true;
}
// 3. 性能要求分析
if (getQPS(context) > 10000) {
return true;
}
// 4. 业务独立性分析
if (isIndependentBusiness(context)) {
return true;
}
return false;
}
}
8.6.2 数据一致性保证
最终一致性方案:
// ✅ 基于事件驱动的最终一致性
// 本地消息表方案
@Service
public class OrderApplicationService {
private final OrderRepository orderRepository;
private final OutboxRepository outboxRepository;
@Transactional
public void submitOrder(OrderId orderId) {
// 1. 业务操作
Order order = orderRepository.findById(orderId);
order.submit();
orderRepository.save(order);
// 2. 写入消息表(同一事务)
OutboxMessage message = new OutboxMessage(
"order.submitted",
new OrderSubmittedEvent(order)
);
outboxRepository.save(message);
}
}
// 后台任务轮询发送消息
@Scheduled(fixedRate = 1000)
public void processOutbox() {
List<OutboxMessage> messages = outboxRepository.findUnsent();
for (OutboxMessage msg : messages) {
messageQueue.publish(msg.getTopic(), msg.getPayload());
msg.markAsSent();
outboxRepository.save(msg);
}
}
// 消费者幂等处理
@Component
public class InventoryEventHandler {
@RabbitListener(queues = "order.submitted")
public void handle(OrderSubmittedEvent event) {
// 幂等性检查
if (inventoryRepository.existsByOrderId(event.getOrderId())) {
return; // 已处理
}
// 业务处理
inventoryService.reserveStock(event.getItems());
// 记录已处理
inventoryRepository.saveProcessedOrder(event.getOrderId());
}
}
Saga模式:
// 编排式Saga
@Service
public class OrderSagaOrchestrator {
public void executeCreateOrderSaga(CreateOrderCommand cmd) {
// 步骤1:创建订单
Order order = orderService.createOrder(cmd);
try {
// 步骤2:预留库存
inventoryService.reserveStock(order.getId());
// 步骤3:创建支付
paymentService.createPayment(order.getId());
// 步骤4:确认订单
orderService.confirmOrder(order.getId());
} catch (Exception e) {
// 补偿操作
orderService.cancelOrder(order.getId());
inventoryService.releaseStock(order.getId());
paymentService.cancelPayment(order.getId());
}
}
}
// 协同式Saga(基于事件)
public class OrderSagaCoordinator {
@EventHandler
public void handle(OrderSubmittedEvent event) {
inventoryService.reserveStock(event.getOrderId());
}
@EventHandler
public void handle(StockReservedEvent event) {
paymentService.createPayment(event.getOrderId());
}
@EventHandler
public void handle(PaymentCreatedEvent event) {
orderService.confirmOrder(event.getOrderId());
}
@EventHandler
public void handle(PaymentFailedEvent event) {
inventoryService.releaseStock(event.getOrderId());
orderService.cancelOrder(event.getOrderId());
}
}
8.6.3 服务治理
服务注册与发现:
// Spring Cloud 服务注册
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
public static void main(String[] args) {
SpringApplication.run(OrderServiceApplication.class, args);
}
}
// Feign 客户端(自动服务发现)
@FeignClient(name = "product-service")
public interface ProductClient {
@GetMapping("/api/products/{id}")
ProductDTO getProduct(@PathVariable Long id);
}
熔断降级:
// Resilience4j 熔断配置
@Configuration
public class CircuitBreakerConfig {
@Bean
public CircuitBreaker productCircuitBreaker() {
CircuitBreakerConfig config = CircuitBreakerConfig.custom()
.failureRateThreshold(50) // 失败率阈值50%
.waitDurationInOpenState(Duration.ofSeconds(30)) // 开启状态等待时间
.slidingWindowSize(10) // 滑动窗口大小
.build();
return CircuitBreaker.of("productService", config);
}
}
// 使用熔断
@Service
public class OrderApplicationService {
private final ProductClient productClient;
private final CircuitBreaker circuitBreaker;
public ProductInfo getProductWithFallback(Long productId) {
return circuitBreaker.executeSupplier(() ->
productClient.getProduct(productId)
);
}
// 降级方法
public ProductInfo getProductFallback(Long productId) {
return new ProductInfo(productId, "默认商品", BigDecimal.ZERO);
}
}
限流:
// Resilience4j 限流
@RateLimiter(name = "productService", fallbackMethod = "getProductFallback")
public ProductDTO getProduct(Long productId) {
return productClient.getProduct(productId);
}
8.7 总结与实践建议
核心要点回顾
-
限界上下文是微服务划分的天然边界
- 先做DDD战略设计,再考虑服务拆分
- 限界上下文与服务不是简单的一一对应
-
避免分布式单体
- 服务间通过事件解耦,而非直接调用
- 每个服务独占数据库
- 使用防腐层隔离外部依赖
-
合理选择通信模式
- 查询类操作:同步API
- 命令类操作:异步消息
- 关键路径:考虑同步+补偿
-
数据一致性保证
- 接受最终一致性
- 使用Saga模式处理跨服务事务
- 幂等性设计
-
渐进式演进
- 从模块化单体开始
- 提取高频变更的上下文为服务
- 逐步演进到微服务
实践建议
起步阶段:
- 先实现模块化单体(DDD限界上下文)
- 建立清晰的模块边界
- 练习事件驱动架构
服务拆分时机:
- 团队规模增长,协作成本增加
- 部署频率提高,发布冲突频繁
- 某个模块性能要求高,需要独立扩展
拆分原则:
- 一次只拆一个上下文
- 优先拆分变更频率高的上下文
- 保证每个服务的独立性
思考题
基础题:
- 什么是分布式单体?它有哪些特征?
- 限界上下文与微服务是什么关系?
- 微服务间通信有哪些模式?各有什么优缺点?
进阶题:
- 如何判断一个限界上下文是否应该独立为微服务?
- 跨服务的业务事务如何保证一致性?
- 如何设计防腐层来隔离外部服务?
实战题:
- 设计一个电商系统的服务拆分方案,说明每个服务的职责和边界。
- 实现一个基于本地消息表的最终一致性方案。
- 为订单服务设计熔断降级策略。
章节关联
- 前置知识:DDD-004 领域、子域与限界上下文、DDD-006 上下文映射
- 相关章节:DDD-007 领域事件、DDD-017 六边形架构、DDD-024 事务管理与一致性保证
- 下一章:DDD-009 实体(Entity)- 深入战术设计
关键词: 微服务、服务拆分、分布式单体、服务通信、限界上下文映射、事件驱动、最终一致性
2207

被折叠的 条评论
为什么被折叠?



