这 5 个设计模式在微服务架构中最好用——《Head First 设计模式》精读笔记

为什么是这 5 个

GoF 23 个设计模式中,很多在单体应用中很好用,但在微服务架构中变得不那么重要(比如 Flyweight、Bridge)。而有些模式在分布式场景下反而更加关键。

经过多个微服务项目的实战积累,以下 5 个模式的使用频率最高:

| 模式 | 类型 | 微服务核心场景 |

|------|------|--------------|

| 策略模式(Strategy) | 行为型 | 多租户差异化逻辑、支付路由 |

| 观察者模式(Observer) | 行为型 | 事件驱动架构、异步通知 |

| 工厂方法(Factory Method) | 创建型 | 多数据源适配、插件化扩展 |

| 代理模式(Proxy) | 结构型 | Feign 客户端、API Gateway、熔断 |

| 装饰器模式(Decorator) | 结构型 | 中间件链、日志增强、重试机制 |

下面逐一拆解。

一、策略模式(Strategy)

原理简述

策略模式将算法族封装成独立的类,使得它们可以互相替换。核心思想是"把变化的部分抽出来,让使用方不感知具体实现"。


UML 结构:

┌─────────────────┐
│    Context       │
│  ┌─────────────┐ │
│  │  strategy    │◄──────┐
│  └─────────────┘ │      │
│  execute()      │      │
└─────────────────┘      │
                         │ implements
        ┌────────────────┼────────────────┐
        ▼                ▼                ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ StrategyA    │ │ StrategyB    │ │ StrategyC    │
│ execute()    │ │ execute()    │ │ execute()    │
└──────────────┘ └──────────────┘ └──────────────┘

微服务场景:支付路由

在电商微服务中,支付服务需要根据不同条件(金额、地区、用户等级)选择不同的支付渠道。这是策略模式最典型的应用。


// 1. 定义策略接口
public interface PaymentStrategy {
    /**
     * 是否支持该支付条件
     */
    boolean supports(PaymentContext context);
    
    /**
     * 执行支付
     */
    PaymentResult pay(PaymentRequest request);
    
    /**
     * 优先级(数值越小优先级越高)
     */
    int priority();
}

// 2. 具体策略实现
@Component
public class AlipayStrategy implements PaymentStrategy {
    
    @Override
    public boolean supports(PaymentContext context) {
        return context.getRegion().equals("CN") 
            && context.getAmount().compareTo(new BigDecimal("50000")) <= 0;
    }
    
    @Override
    public PaymentResult pay(PaymentRequest request) {
        // 调用支付宝 API
        return alipayClient.tradePay(request);
    }
    
    @Override
    public int priority() {
        return 10;
    }
}

@Component
public class WechatPayStrategy implements PaymentStrategy {
    
    @Override
    public boolean supports(PaymentContext context) {
        return context.getRegion().equals("CN")
            && context.getChannel().equals("MOBILE");
    }
    
    @Override
    public PaymentResult pay(PaymentRequest request) {
        return wechatPayClient.unifiedOrder(request);
    }
    
    @Override
    public int priority() {
        return 20;
    }
}

@Component
public class StripeStrategy implements PaymentStrategy {
    
    @Override
    public boolean supports(PaymentContext context) {
        return !context.getRegion().equals("CN"); // 海外走 Stripe
    }
    
    @Override
    public PaymentResult pay(PaymentRequest request) {
        return stripeClient.charge(request);
    }
    
    @Override
    public int priority() {
        return 30;
    }
}

// 3. 策略上下文(路由器)
@Service
public class PaymentRouter {
    
    private final List<PaymentStrategy> strategies;
    
    // Spring 自动注入所有 PaymentStrategy 实现
    public PaymentRouter(List<PaymentStrategy> strategies) {
        // 按优先级排序
        this.strategies = strategies.stream()
            .sorted(Comparator.comparingInt(PaymentStrategy::priority))
            .collect(Collectors.toList());
    }
    
    public PaymentResult routeAndPay(PaymentContext context, PaymentRequest request) {
        PaymentStrategy selected = strategies.stream()
            .filter(s -> s.supports(context))
            .findFirst()
            .orElseThrow(() -> new UnsupportedPaymentException("无可用支付渠道"));
        
        log.info("路由到支付策略: {}", selected.getClass().getSimpleName());
        return selected.pay(request);
    }
}

踩坑点

1. 策略选择不要硬编码 if-else:上面的实现通过 supports() + priority() 自动路由,新增支付渠道只需添加新的 @Component,符合开闭原则。

2. Spring 注入顺序问题List 的注入顺序不确定,必须显式排序(用 @Orderpriority() 方法)。

3. 策略状态管理:策略对象应该是无状态的(Stateless),如果涉及状态(如支付会话),使用 ThreadLocal 或外部存储。

二、观察者模式(Observer)

原理简述

观察者模式定义了对象间的一对多依赖关系。当一个对象状态改变时,所有依赖者都会收到通知。在微服务中,这就是事件驱动架构(EDA)的基础。


UML 结构:

┌──────────────┐         ┌──────────────────┐
│   Subject    │────────▶│    Observer       │
│              │         │  update(event)    │
│  attach()    │         └──────────────────┘
│  detach()    │              ▲
│  notify()    │              │
└──────────────┘              │ implements
       │              ┌───────┴────────┐
       │              ▼                ▼
       │     ┌──────────────┐ ┌──────────────┐
       └────▶│ ObserverA    │ │ ObserverB    │
             │ update()     │ │ update()     │
             └──────────────┘ └──────────────┘

微服务场景:领域事件发布

在订单微服务中,订单状态变更时需要通知多个下游服务(库存、物流、积分、通知)。使用 Spring 的 ApplicationEvent 实现进程内观察者:


// 1. 定义领域事件
public class OrderStatusChangedEvent extends ApplicationEvent {
    private final String orderId;
    private final OrderStatus oldStatus;
    private final OrderStatus newStatus;
    private final LocalDateTime occurredAt;
    
    public OrderStatusChangedEvent(Object source, String orderId,
            OrderStatus oldStatus, OrderStatus newStatus) {
        super(source);
        this.orderId = orderId;
        this.oldStatus = oldStatus;
        this.newStatus = newStatus;
        this.occurredAt = LocalDateTime.now();
    }
    
    // getters...
}

// 2. 事件发布者(Subject)
@Service
public class OrderService {
    
    @Autowired
    private ApplicationEventPublisher eventPublisher;
    
    @Autowired
    private OrderRepository orderRepository;
    
    @Transactional
    public void confirmOrder(String orderId) {
        Order order = orderRepository.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
        
        OrderStatus oldStatus = order.getStatus();
        order.setStatus(OrderStatus.CONFIRMED);
        orderRepository.save(order);
        
        // 发布领域事件
        eventPublisher.publishEvent(
            new OrderStatusChangedEvent(this, orderId, oldStatus, OrderStatus.CONFIRMED)
        );
    }
}

// 3. 事件监听者(Observers)
@Component
public class InventoryObserver {
    
    @Async
    @EventListener
    public void onOrderConfirmed(OrderStatusChangedEvent event) {
        if (event.getNewStatus() == OrderStatus.CONFIRMED) {
            log.info("库存扣减 - 订单: {}", event.getOrderId());
            inventoryService.deductStock(event.getOrderId());
        }
    }
}

@Component
public class LogisticsObserver {
    
    @Async
    @EventListener
    public void onOrderConfirmed(OrderStatusChangedEvent event) {
        if (event.getNewStatus() == OrderStatus.CONFIRMED) {
            log.info("物流创建 - 订单: {}", event.getOrderId());
            logisticsService.createShipment(event.getOrderId());
        }
    }
}

@Component
public class NotificationObserver {
    
    @Async
    @EventListener
    public void onOrderStatusChanged(OrderStatusChangedEvent event) {
        notificationService.sendOrderUpdate(
            event.getOrderId(), 
            event.getNewStatus()
        );
    }
}

跨服务的观察者:用 Kafka 实现

微服务之间的观察者模式通常借助消息队列实现:


// 发布者 - 订单服务
@Service
public class OrderEventPublisher {
    
    @Autowired
    private KafkaTemplate<String, String> kafkaTemplate;
    
    @Autowired
    private ObjectMapper objectMapper;
    
    @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
    public void publishToKafka(OrderStatusChangedEvent event) {
        try {
            String payload = objectMapper.writeValueAsString(event);
            kafkaTemplate.send("order-events", event.getOrderId(), payload);
            log.info("订单事件已发布: orderId={}", event.getOrderId());
        } catch (Exception e) {
            log.error("事件发布失败: {}", e.getMessage());
            // 记录到本地事件表,由定时任务补偿
            outboxRepository.save(new OutboxEvent("order-events", event.getOrderId(), payload));
        }
    }
}

// 订阅者 - 库存服务
@Component
public class InventoryKafkaListener {
    
    @KafkaListener(topics = "order-events", groupId = "inventory-service")
    public void handleOrderEvent(String message) {
        OrderStatusChangedEvent event = objectMapper.readValue(message, 
            OrderStatusChangedEvent.class);
        
        if (event.getNewStatus() == OrderStatus.CONFIRMED) {
            inventoryService.deductStock(event.getOrderId());
        }
    }
}

踩坑点

1. 事务一致性:事件发布必须在业务事务提交之后(@TransactionalEventListener(phase = AFTER_COMMIT)),否则观察者可能读到未提交的数据。

2. 幂等性:消息可能重复投递,所有观察者必须实现幂等处理。

3. 顺序问题:如果对事件顺序有要求(如先扣库存再创建物流),需要在消息 Key 中编码排序信息,或使用 Kafka 的 Partition 保序。

4. Outbox 模式:上面代码中的 Outbox 表是处理发布失败的关键——先把事件写入本地表,再由后台线程异步发送到 Kafka。

三、工厂方法(Factory Method)

原理简述

工厂方法定义一个创建对象的接口,但将实际创建推迟到子类。在微服务中,工厂方法常用于多数据源适配和多协议通信。


UML 结构:

┌────────────────────┐
│  AbstractFactory   │
│  createProduct()   │
└────────┬───────────┘
         │ extends
    ┌────┴────┐
    ▼         ▼
┌────────┐ ┌────────┐       ┌──────────────┐
│ FactA  │ │ FactB  │──────▶│   Product     │
│create()│ │create()│  uses │  operation()  │
└────────┘ └────────┘       └──────────────┘

微服务场景:多数据源连接工厂

在企业级微服务中,一个服务可能需要连接多种数据库(MySQL、Redis、MongoDB、Elasticsearch),工厂方法可以统一管理连接创建:


// 1. 抽象工厂接口
public interface DataSourceFactory<T> {
    /**
     * 创建数据源连接
     */
    T create(DataSourceConfig config);
    
    /**
     * 支持的数据源类型
     */
    DataSourceType supportedType();
}

// 2. MySQL 数据源工厂
@Component
public class MySQLDataSourceFactory implements DataSourceFactory<DataSource> {
    
    @Override
    public DataSource create(DataSourceConfig config) {
        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl(config.getUrl());
        hikariConfig.setUsername(config.getUsername());
        hikariConfig.setPassword(config.getPassword());
        hikariConfig.setMaximumPoolSize(config.getMaxPoolSize());
        hikariConfig.setConnectionTimeout(config.getTimeoutMs());
        return new HikariDataSource(hikariConfig);
    }
    
    @Override
    public DataSourceType supportedType() {
        return DataSourceType.MYSQL;
    }
}

// 3. Redis 数据源工厂
@Component
public class RedisDataSourceFactory implements DataSourceFactory<RedisConnectionFactory> {
    
    @Override
    public RedisConnectionFactory create(DataSourceConfig config) {
        RedisStandaloneConfiguration redisConfig = new RedisStandaloneConfiguration();
        redisConfig.setHostName(config.getHost());
        redisConfig.setPort(config.getPort());
        redisConfig.setPassword(RedisPassword.of(config.getPassword()));
        
        LettuceConnectionFactory factory = new LettuceConnectionFactory(redisConfig);
        factory.afterPropertiesSet();
        return factory;
    }
    
    @Override
    public DataSourceType supportedType() {
        return DataSourceType.REDIS;
    }
}

// 4. 工厂注册中心
@Component
public class DataSourceFactoryRegistry {
    
    private final Map<DataSourceType, DataSourceFactory<?>> factories;
    
    public DataSourceFactoryRegistry(List<DataSourceFactory<?>> factoryList) {
        this.factories = factoryList.stream()
            .collect(Collectors.toMap(
                DataSourceFactory::supportedType,
                Function.identity()
            ));
    }
    
    @SuppressWarnings("unchecked")
    public <T> DataSourceFactory<T> getFactory(DataSourceType type) {
        DataSourceFactory<?> factory = factories.get(type);
        if (factory == null) {
            throw new UnsupportedDataSourceException("不支持的数据源类型: " + type);
        }
        return (DataSourceFactory<T>) factory;
    }
}

// 5. 使用示例
@Service
public class MultiDataSourceManager {
    
    @Autowired
    private DataSourceFactoryRegistry registry;
    
    private final Map<String, Object> activeConnections = new ConcurrentHashMap<>();
    
    public void initDataSource(String name, DataSourceConfig config) {
        DataSourceFactory<?> factory = registry.getFactory(config.getType());
        Object connection = factory.create(config);
        activeConnections.put(name, connection);
        log.info("数据源 {} ({}) 初始化完成", name, config.getType());
    }
}

踩坑点

1. 工厂注册时机:Spring 容器启动时工厂列表可能不完整(特别是涉及 SPI 加载的),建议使用 @DependsOnSmartInitializingSingleton 确保注册完成。

2. 连接生命周期:工厂创建的对象(如 DataSource)需要管理生命周期,实现 DisposableBean 或在 @PreDestroy 中释放资源。

3. 配置热更新:如果数据源配置需要热更新,工厂应支持重新创建连接并替换旧连接(注意优雅关闭)。

四、代理模式(Proxy)

原理简述

代理模式为另一个对象提供一个替身以控制对其的访问。在微服务中,代理模式无处不在:Feign 客户端是远程代理,Spring AOP 是动态代理,API Gateway 是反向代理。


UML 结构:

┌─────────────┐       ┌──────────────────────┐
│   Client    │──────▶│      Subject         │
└─────────────┘       │  request()           │
                      └──────────┬───────────┘
                                 │
                    ┌────────────┴────────────┐
                    ▼                         ▼
            ┌──────────────┐         ┌──────────────┐
            │    Proxy     │────────▶│  RealSubject │
            │  request()   │  calls  │  request()   │
            │  (增强逻辑)   │         │  (真实逻辑)   │
            └──────────────┘         └──────────────┘

微服务场景:自定义 Feign 拦截代理

Spring Cloud OpenFeign 本身就是代理模式的典型实现。在此基础上,我们可以添加自定义代理逻辑(如请求签名、流量染色):


// 1. 定义 Feign 客户端接口
@FeignClient(name = "user-service", configuration = FeignProxyConfig.class)
public interface UserServiceClient {
    
    @GetMapping("/api/users/{id}")
    UserDTO getUser(@PathVariable("id") Long userId);
    
    @PostMapping("/api/users/batch")
    List<UserDTO> batchGetUsers(@RequestBody List<Long> userIds);
}

// 2. Feign 配置 - 添加代理拦截器
public class FeignProxyConfig {
    
    @Bean
    public RequestInterceptor tracingInterceptor() {
        return template -> {
            // 传播 TraceId
            String traceId = MDC.get("traceId");
            if (traceId != null) {
                template.header("X-Trace-Id", traceId);
            }
            
            // 传播用户上下文
            UserContext ctx = UserContextHolder.get();
            if (ctx != null) {
                template.header("X-User-Id", ctx.getUserId().toString());
                template.header("X-Tenant-Id", ctx.getTenantId());
            }
            
            // 流量染色(灰度发布)
            String trafficTag = TrafficRouter.getCurrentTag();
            if (trafficTag != null) {
                template.header("X-Traffic-Tag", trafficTag);
            }
            
            // 请求签名
            String signature = SignatureUtil.sign(template.url(), template.queries());
            template.header("X-Signature", signature);
        };
    }
    
    @Bean
    public Retryer feignRetryer() {
        // 重试策略:初始间隔100ms,最大间隔1s,最多重试3次
        return new Retryer.Default(100, 1000, 3);
    }
    
    @Bean
    public ErrorDecoder errorDecoder() {
        return (methodKey, response) -> {
            int status = response.status();
            if (status == 429) {
                throw new RetryableException(
                    response.status(), "限流,稍后重试", 
                    response.request().httpMethod(), null, response.request());
            }
            if (status >= 500) {
                throw new RetryableException(
                    response.status(), "服务端错误", 
                    response.request().httpMethod(), null, response.request());
            }
            return new FeignClientException(status, "Feign调用失败: " + methodKey);
        };
    }
}

// 3. 更高级的动态代理 - 带熔断的包装
@Component
public class CircuitBreakerProxy<T> {
    
    private final CircuitBreakerRegistry registry;
    
    public CircuitBreakerProxy(CircuitBreakerRegistry registry) {
        this.registry = registry;
    }
    
    @SuppressWarnings("unchecked")
    public T wrap(T target, Class<T> interfaceType, String serviceName) {
        CircuitBreaker cb = registry.circuitBreaker(serviceName);
        
        return (T) Proxy.newProxyInstance(
            interfaceType.getClassLoader(),
            new Class<?>[]{interfaceType},
            (proxy, method, args) -> {
                return cb.executeSupplier(() -> {
                    try {
                        return method.invoke(target, args);
                    } catch (InvocationTargetException e) {
                        throw new RuntimeException(e.getCause());
                    }
                });
            }
        );
    }
}

// 4. 使用带熔断的代理
@Configuration
public class FeignClientWrapper {
    
    @Bean
    public UserServiceClient userServiceClient(
            CircuitBreakerProxy<UserServiceClient> proxy,
            UserServiceClient rawClient) {
        return proxy.wrap(rawClient, UserServiceClient.class, "user-service");
    }
}

踩坑点

1. 代理层级过深:如果同时使用 Feign + Hystrix/Resilience4j + Spring AOP + 自定义代理,方法调用链会非常长,排查问题时注意 stacktrace 中的实际目标方法。

2. 泛型擦除:JDK 动态代理在处理泛型返回类型时可能丢失类型信息,建议使用 ParameterizedTypeReference 或显式类型转换。

3. 性能开销:每增加一层代理,调用开销增加约 0.1-0.5ms。在高 QPS 场景下需要评估代理层数。

五、装饰器模式(Decorator)

原理简述

装饰器模式动态地给对象添加额外职责,比继承更灵活。在微服务中,装饰器模式的典型应用是中间件链(Middleware Chain)和请求增强。


UML 结构:

┌───────────────────┐
│    Component      │
│  operation()      │
└────────┬──────────┘
         │
    ┌────┴─────────────────┐
    ▼                      ▼
┌──────────┐     ┌──────────────────┐
│Concrete  │     │   Decorator      │
│Component │     │  ┌────────────┐  │
│operation │     │  │ component  │  │
└──────────┘     │  └────────────┘  │
                 │  operation() {   │
                 │    // 增强逻辑    │
                 │    component.op() │
                 │  }               │
                 └────────┬─────────┘
                          │ extends
                    ┌─────┴──────┐
                    ▼            ▼
             ┌──────────┐ ┌──────────┐
             │ DecorA   │ │ DecorB   │
             │(日志)     │ │(限流)     │
             └──────────┘ └──────────┘

微服务场景:HTTP 客户端装饰器链

在调用外部服务时,通常需要叠加多种横切关注点(日志、重试、限流、缓存)。装饰器模式让这些关注点可以灵活组合:


// 1. 基础接口
public interface HttpClient {
    HttpResponse execute(HttpRequest request) throws Exception;
}

// 2. 基础实现(委托给 RestTemplate)
public class RestTemplateHttpClient implements HttpClient {
    
    private final RestTemplate restTemplate;
    
    public RestTemplateHttpClient(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
    
    @Override
    public HttpResponse execute(HttpRequest request) {
        ResponseEntity<String> response = restTemplate.exchange(
            request.getUrl(),
            HttpMethod.valueOf(request.getMethod()),
            new HttpEntity<>(request.getBody(), request.getHeaders()),
            String.class
        );
        return new HttpResponse(response.getStatusCodeValue(), response.getBody());
    }
}

// 3. 日志装饰器
public class LoggingDecorator implements HttpClient {
    
    private final HttpClient delegate;
    
    public LoggingDecorator(HttpClient delegate) {
        this.delegate = delegate;
    }
    
    @Override
    public HttpResponse execute(HttpRequest request) throws Exception {
        long start = System.currentTimeMillis();
        log.info("→ {} {}", request.getMethod(), request.getUrl());
        
        try {
            HttpResponse response = delegate.execute(request);
            long elapsed = System.currentTimeMillis() - start;
            log.info("← {} {}ms status={}", request.getUrl(), elapsed, response.getStatus());
            return response;
        } catch (Exception e) {
            long elapsed = System.currentTimeMillis() - start;
            log.error("✗ {} {}ms error={}", request.getUrl(), elapsed, e.getMessage());
            throw e;
        }
    }
}

// 4. 重试装饰器
public class RetryDecorator implements HttpClient {
    
    private final HttpClient delegate;
    private final int maxRetries;
    private final long retryDelayMs;
    
    public RetryDecorator(HttpClient delegate, int maxRetries, long retryDelayMs) {
        this.delegate = delegate;
        this.maxRetries = maxRetries;
        this.retryDelayMs = retryDelayMs;
    }
    
    @Override
    public HttpResponse execute(HttpRequest request) throws Exception {
        Exception lastException = null;
        
        for (int attempt = 0; attempt <= maxRetries; attempt++) {
            try {
                return delegate.execute(request);
            } catch (Exception e) {
                lastException = e;
                if (attempt < maxRetries) {
                    log.warn("重试 {}/{}: {} - {}", attempt + 1, maxRetries, 
                        request.getUrl(), e.getMessage());
                    Thread.sleep(retryDelayMs * (attempt + 1)); // 指数退避
                }
            }
        }
        throw lastException;
    }
}

// 5. 限流装饰器
public class RateLimitDecorator implements HttpClient {
    
    private final HttpClient delegate;
    private final RateLimiter rateLimiter;
    
    public RateLimitDecorator(HttpClient delegate, double permitsPerSecond) {
        this.delegate = delegate;
        this.rateLimiter = RateLimiter.create(permitsPerSecond);
    }
    
    @Override
    public HttpResponse execute(HttpRequest request) throws Exception {
        if (!rateLimiter.tryAcquire(1, 500, TimeUnit.MILLISECONDS)) {
            throw new RateLimitExceededException("请求限流: " + request.getUrl());
        }
        return delegate.execute(request);
    }
}

// 6. 组装装饰器链
@Configuration
public class HttpClientConfig {
    
    @Bean
    public HttpClient httpClient(RestTemplate restTemplate) {
        HttpClient base = new RestTemplateHttpClient(restTemplate);
        
        // 装饰器链:限流 → 重试 → 日志 → 基础实现
        // 执行顺序:日志(最先执行) → 重试 → 限流 → 真实调用
        return new LoggingDecorator(
            new RetryDecorator(
                new RateLimitDecorator(base, 100.0),  // 100 QPS
                3,    // 最多重试 3 次
                500   // 初始重试间隔 500ms
            )
        );
    }
}

进阶:Spring 的 HandlerInterceptor 也是装饰器

Spring MVC 的拦截器链本质上就是装饰器模式的应用:


// 请求处理链:
// PreHandle(Auth) → PreHandle(Log) → Controller → PostHandle(Log) → PostHandle(Auth)

public class AuthInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler) {
        String token = request.getHeader("Authorization");
        if (!jwtService.validate(token)) {
            response.setStatus(401);
            return false; // 终止链
        }
        return true; // 继续下一个装饰器
    }
}

public class RequestLoggingInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, 
            HttpServletResponse response, Object handler) {
        MDC.put("traceId", UUID.randomUUID().toString());
        request.setAttribute("startTime", System.currentTimeMillis());
        return true;
    }
    
    @Override
    public void afterCompletion(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        long elapsed = System.currentTimeMillis() - (Long) request.getAttribute("startTime");
        log.info("{} {} {}ms", request.getMethod(), request.getRequestURI(), elapsed);
        MDC.clear();
    }
}

踩坑点

1. 装饰器顺序很重要:限流应该在重试之前还是之后?如果限流在重试之后,重试请求不会被限流,可能压垮下游。正确顺序:日志 → 限流 → 重试 → 真实调用。

2. 异常透传:装饰器中的异常处理要谨慎,不要让装饰器吞掉原始异常,否则排查问题很困难。

3. 组合爆炸:装饰器数量不宜过多(建议 3-5 层),否则调试复杂度指数增长。超过这个数量时考虑用 AOP 或 Pipeline 模式替代。

5 个模式的协作关系

在实际微服务项目中,这 5 个模式往往不是独立使用的,而是相互配合:


典型请求处理流程中的模式应用:

[客户端请求]
     │
     ▼
┌─────────────────────────────────────────┐
│  API Gateway                            │
│  ┌─────────────────────────────────┐    │
│  │ 装饰器链: 限流→鉴权→日志        │    │ ← 装饰器模式
│  └─────────────────────────────────┘    │
└──────────────────┬──────────────────────┘
                   │
                   ▼
┌─────────────────────────────────────────┐
│  业务服务                                │
│  ┌──────────────┐  ┌──────────────────┐ │
│  │ 工厂创建      │  │ 策略选择         │ │ ← 工厂+策略
│  │ 数据源连接    │  │ 业务处理逻辑     │ │
│  └──────────────┘  └──────────────────┘ │
│                                         │
│  ┌──────────────────────────────────┐   │
│  │ 代理调用下游服务(Feign+熔断)    │   │ ← 代理模式
│  └──────────────────────────────────┘   │
│                                         │
│  ┌──────────────────────────────────┐   │
│  │ 发布领域事件                      │   │ ← 观察者模式
│  │ → Kafka → 下游服务订阅           │   │
│  └──────────────────────────────────┘   │
└─────────────────────────────────────────┘

设计模式不是教条,而是经验的沉淀。在微服务架构中,理解这些模式的核心意图比记住 UML 图更重要。当你在代码中看到 if-else 越来越多时,想想策略模式;当你在服务间传递状态时,想想观察者模式;当你需要灵活组合行为时,想想装饰器模式。


原文链接:https://wenyiblog.top/2026/06/five-design-patterns-microservices/

首发于文艺技术笔记(wenyiblog.top),转载请注明出处。

posted @ 2026-06-22 19:31  软件工程师文艺  阅读(2)  评论(0)    收藏  举报