30-Spring Cloud Stream 消息驱动详解

4 阅读6分钟

Spring Cloud Stream 消息驱动详解

一、知识概述

Spring Cloud Stream 是构建消息驱动微服务的框架,它提供了统一的编程模型,屏蔽了底层消息中间件的差异。开发者可以使用统一的 API 进行消息的生产和消费,支持 RabbitMQ、Kafka、RocketMQ 等多种消息中间件。

消息驱动的核心概念:

  • Binder:消息中间件绑定器
  • Binding:消息通道绑定
  • Message:消息体
  • Channel:消息通道

理解消息驱动的原理,是构建异步、解耦微服务系统的重要技能。

二、知识点详细讲解

2.1 架构模型

应用层
  │
  ├── Input Channel(输入通道)
  │       │
  │       ▼
  │   Binder(绑定器)
  │       │
  │       ▼
  │   消息中间件(RabbitMQ/Kafka/RocketMQ)
  │       │
  │       ▼
  ├── Output Channel(输出通道)
  │
应用层

2.2 核心概念

Binder

负责与消息中间件交互,提供:

  • 连接管理
  • 消息发送/接收
  • 序列化/反序列化
Binding

连接应用与 Binder 的桥梁:

  • Input Binding:消费者绑定
  • Output Binding:生产者绑定
Message

消息的基本单元:

  • Header:消息头
  • Payload:消息体

2.3 消息中间件对比

特性RabbitMQKafkaRocketMQ
吞吐量
延迟
顺序性
事务消息
延迟消息
消息回溯

2.4 消费模型

发布订阅(Publish-Subscribe)
  • 一条消息被多个消费者消费
  • 每个消费者独立消费
消费者组(Consumer Group)
  • 一条消息只被组内一个消费者消费
  • 实现负载均衡

三、代码示例

3.1 基础配置

<!-- pom.xml -->
<!-- RabbitMQ Binder -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>

<!-- Kafka Binder -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
# application.yml
spring:
  cloud:
    stream:
      # 绑定器配置
      binders:
        rabbit-binder:
          type: rabbit
          environment:
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
        kafka-binder:
          type: kafka
          environment:
            spring:
              kafka:
                bootstrap-servers: localhost:9092
      
      # 绑定配置
      bindings:
        # 输出通道
        userOutput:
          destination: user-topic
          content-type: application/json
          binder: rabbit-binder
        
        # 输入通道
        userInput:
          destination: user-topic
          group: user-group
          binder: rabbit-binder

3.2 定义消息通道

import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.*;
import org.springframework.stereotype.Component;

// 消息通道定义
public interface UserChannels {
    
    // 输出通道(生产者)
    String USER_OUTPUT = "userOutput";
    
    @Output(USER_OUTPUT)
    MessageChannel userOutput();
    
    // 输入通道(消费者)
    String USER_INPUT = "userInput";
    
    @Input(USER_INPUT)
    SubscribableChannel userInput();
}

// 多通道定义
public interface OrderChannels {
    
    @Output("orderOutput")
    MessageChannel orderOutput();
    
    @Input("orderInput")
    SubscribableChannel orderInput();
    
    @Output("paymentOutput")
    MessageChannel paymentOutput();
    
    @Input("paymentInput")
    SubscribableChannel paymentInput();
}

3.3 启用消息通道

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;

@SpringBootApplication
@EnableBinding({UserChannels.class, OrderChannels.class})
public class StreamApplication {
    public static void main(String[] args) {
        SpringApplication.run(StreamApplication.class, args);
    }
}

3.4 消息生产者

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

@Service
@EnableBinding(UserChannels.class)
public class UserProducer {
    
    @Autowired
    private UserChannels userChannels;
    
    // 发送简单消息
    public void sendMessage(UserDTO user) {
        userChannels.userOutput().send(
            MessageBuilder.withPayload(user).build()
        );
    }
    
    // 发送带头的消息
    public void sendMessageWithHeaders(UserDTO user, Map<String, Object> headers) {
        Message<UserDTO> message = MessageBuilder
            .withPayload(user)
            .copyHeaders(headers)
            .setHeader("type", "user-created")
            .setHeader("version", "1.0")
            .build();
        
        userChannels.userOutput().send(message);
    }
    
    // 发送延迟消息(RabbitMQ)
    public void sendDelayedMessage(UserDTO user, int delayMs) {
        Message<UserDTO> message = MessageBuilder
            .withPayload(user)
            .setHeader("x-delay", delayMs)
            .build();
        
        userChannels.userOutput().send(message);
    }
    
    // 发送有序消息(Kafka)
    public void sendOrderedMessage(UserDTO user, String key) {
        Message<UserDTO> message = MessageBuilder
            .withPayload(user)
            .setHeader("kafka_messageKey", key)
            .build();
        
        userChannels.userOutput().send(message);
    }
}

3.5 消息消费者

import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;

@Service
public class UserConsumer {
    
    // 基础消费者
    @StreamListener(UserChannels.USER_INPUT)
    public void handleUser(UserDTO user) {
        System.out.println("收到用户消息: " + user);
        // 处理业务逻辑
    }
    
    // 处理带头的消息
    @StreamListener(UserChannels.USER_INPUT)
    public void handleMessage(Message<UserDTO> message) {
        UserDTO user = message.getPayload();
        String type = (String) message.getHeaders().get("type");
        
        System.out.println("收到消息类型: " + type);
        System.out.println("消息内容: " + user);
    }
    
    // 条件消费
    @StreamListener(
        target = UserChannels.USER_INPUT,
        condition = "headers['type'] == 'user-created'"
    )
    public void handleCreatedUser(UserDTO user) {
        System.out.println("处理新用户创建: " + user);
    }
    
    @StreamListener(
        target = UserChannels.USER_INPUT,
        condition = "headers['type'] == 'user-updated'"
    )
    public void handleUpdatedUser(UserDTO user) {
        System.out.println("处理用户更新: " + user);
    }
    
    // 返回结果(请求-响应模式)
    @StreamListener(UserChannels.USER_INPUT)
    @SendTo("responseOutput")
    public UserDTO processAndRespond(UserDTO user) {
        // 处理用户
        user.setStatus("PROCESSED");
        return user;
    }
}

3.6 错误处理

import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;

@Service
public class ErrorHandler {
    
    // 局部错误处理(针对特定输入通道)
    @ServiceActivator(inputChannel = "userInput.errors")
    public void handleError(ErrorMessage errorMessage) {
        System.out.println("处理错误: " + errorMessage.getPayload());
        // 记录日志、发送告警等
    }
    
    // 全局错误处理
    @StreamListener("errorChannel")
    public void handleGlobalError(ErrorMessage errorMessage) {
        System.out.println("全局错误处理: " + errorMessage);
    }
    
    // 消费者重试
    @StreamListener(UserChannels.USER_INPUT)
    public void handleWithRetry(UserDTO user) {
        try {
            processUser(user);
        } catch (Exception e) {
            // 抛出异常会触发重试
            throw new RuntimeException("处理失败,触发重试", e);
        }
    }
}
# 重试配置
spring:
  cloud:
    stream:
      bindings:
        userInput:
          consumer:
            max-attempts: 3          # 最大重试次数
            back-off-initial: 1000   # 初始重试间隔
            back-off-multiplier: 2   # 重试间隔倍数
            back-off-max: 10000      # 最大重试间隔

3.7 消费者组

spring:
  cloud:
    stream:
      bindings:
        userInput:
          destination: user-topic
          group: user-service-group  # 同组内只有一个消费者消费
          consumer:
            concurrency: 3           # 并发消费者数量
            partitioned: false       # 是否分区消费
@Service
public class GroupConsumer {
    
    @StreamListener("userInput")
    public void handle(UserDTO user) {
        // 同一个 group 内只有一个实例会收到消息
        System.out.println("消费消息: " + user);
    }
}

3.8 分区消息

spring:
  cloud:
    stream:
      bindings:
        userOutput:
          destination: user-topic
          producer:
            partition-count: 3           # 分区数
            partition-key-expression: headers['userId']  # 分区键表达式
            
        userInput:
          destination: user-topic
          group: user-group
          consumer:
            partitioned: true
            instance-count: 3            # 实例数量
            instance-index: 0            # 当前实例索引
@Service
public class PartitionProducer {
    
    @Autowired
    private UserChannels userChannels;
    
    public void sendPartitionedMessage(UserDTO user) {
        Message<UserDTO> message = MessageBuilder
            .withPayload(user)
            .setHeader("userId", user.getId())  // 分区键
            .build();
        
        userChannels.userOutput().send(message);
    }
}

3.9 函数式编程模型(Spring Cloud Stream 3.x)

# application.yml
spring:
  cloud:
    function:
      definition: userConsumer;userSupplier;userFunction
    stream:
      bindings:
        userConsumer-in-0:
          destination: user-topic
          group: user-group
        userSupplier-out-0:
          destination: user-topic
        userFunction-in-0:
          destination: input-topic
        userFunction-out-0:
          destination: output-topic
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.function.*;

@Component
public class StreamFunctions {
    
    // 消费者
    @Bean
    public Consumer<UserDTO> userConsumer() {
        return user -> {
            System.out.println("消费用户: " + user);
        };
    }
    
    // 生产者
    @Bean
    public Supplier<UserDTO> userSupplier() {
        return () -> {
            UserDTO user = new UserDTO();
            user.setId(1L);
            user.setName("test");
            return user;
        };
    }
    
    // 处理器(输入 -> 输出)
    @Bean
    public Function<UserDTO, UserDTO> userFunction() {
        return user -> {
            user.setName(user.getName().toUpperCase());
            return user;
        };
    }
    
    // 带状态的处理器
    @Bean
    public Function<Flux<UserDTO>, Flux<UserDTO>> userFluxFunction() {
        return flux -> flux.map(user -> {
            user.setName(user.getName().toUpperCase());
            return user;
        });
    }
}

3.10 RabbitMQ 特定配置

spring:
  cloud:
    stream:
      rabbit:
        bindings:
          userOutput:
            producer:
              exchange-type: direct    # 交换机类型
              routing-key-expression: headers['routingKey']
              delayed-exchange: true   # 延迟交换机
              dead-letter-routing-key: dlq.user
              
          userInput:
            consumer:
              auto-bind-dlq: true      # 自动创建死信队列
              republish-to-dlq: true   # 重发到死信队列
              prefetch: 10             # 预取数量
              max-concurrency: 5       # 最大并发
              acknowledge-mode: MANUAL  # 手动确认

3.11 Kafka 特定配置

spring:
  cloud:
    stream:
      kafka:
        bindings:
          userOutput:
            producer:
              sync: false              # 异步发送
              batch-size: 16384        # 批量大小
              compression-type: gzip   # 压缩类型
              key-serializer: org.apache.kafka.common.serialization.StringSerializer
              
          userInput:
            consumer:
              auto-commit-offset: false  # 手动提交
              start-offset: earliest     # 从最早开始
              enable-dlq: true           # 启用死信队列
              dlq-name: user-dlq
              
        binder:
          configuration:
            key.deserializer: org.apache.kafka.common.serialization.StringDeserializer
            value.deserializer: org.springframework.kafka.support.serializer.JsonDeserializer

四、实战应用场景

4.1 订单处理流程

import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;

@Service
@EnableBinding(OrderChannels.class)
public class OrderProcessor {
    
    @Autowired
    private OrderChannels channels;
    
    // 创建订单
    public void createOrder(OrderDTO order) {
        // 发送订单创建消息
        channels.orderOutput().send(
            MessageBuilder.withPayload(order)
                .setHeader("type", "order-created")
                .build()
        );
    }
    
    // 监听订单创建
    @StreamListener("orderInput")
    public void handleOrderCreated(OrderDTO order) {
        // 1. 库存检查
        checkInventory(order);
        
        // 2. 发送支付消息
        channels.paymentOutput().send(
            MessageBuilder.withPayload(order)
                .setHeader("type", "payment-required")
                .build()
        );
    }
    
    // 监听支付完成
    @StreamListener("paymentInput")
    public void handlePaymentCompleted(PaymentDTO payment) {
        // 更新订单状态
        updateOrderStatus(payment.getOrderId(), "PAID");
        
        // 发送发货消息
        // ...
    }
    
    private void checkInventory(OrderDTO order) { }
    private void updateOrderStatus(Long orderId, String status) { }
}

4.2 广播通知

import org.springframework.cloud.stream.annotation.*;
import org.springframework.stereotype.Service;

// 广播消息(所有实例都收到)
@Service
public class BroadcastService {
    
    @StreamListener("notificationInput")
    public void handleNotification(Notification notification) {
        // 所有实例都会收到并处理
        System.out.println("收到通知: " + notification);
    }
}
spring:
  cloud:
    stream:
      bindings:
        notificationInput:
          destination: notification-topic
          # 不设置 group,每个实例都会收到

4.3 消息重试与死信

import org.springframework.cloud.stream.annotation.*;
import org.springframework.stereotype.Service;

@Service
public class RetryConsumer {
    
    @StreamListener("userInput")
    public void handleWithRetry(UserDTO user) {
        int retryCount = getRetryCount(user);
        
        if (retryCount >= 3) {
            // 超过重试次数,发送到死信队列
            throw new RuntimeException("超过最大重试次数");
        }
        
        try {
            processUser(user);
        } catch (Exception e) {
            // 设置重试次数
            setRetryCount(user, retryCount + 1);
            throw e;
        }
    }
    
    // 死信队列消费者
    @StreamListener("userDlqInput")
    public void handleDeadLetter(UserDTO user) {
        System.out.println("处理死信消息: " + user);
        // 记录日志、发送告警
    }
    
    private int getRetryCount(UserDTO user) { return 0; }
    private void setRetryCount(UserDTO user, int count) { }
    private void processUser(UserDTO user) { }
}

五、总结与最佳实践

消息中间件选择

场景推荐
简单异步RabbitMQ
高吞吐量Kafka
事务消息RocketMQ
延迟消息RabbitMQ/RocketMQ

最佳实践

  1. 幂等设计:消息可能重复消费
  2. 错误处理:配置合理的重试和死信
  3. 监控告警:监控消息积压
  4. 消息追踪:添加链路追踪信息

Spring Cloud Stream 提供了统一的消息驱动编程模型,掌握其使用方式和最佳实践,能够构建出异步、解耦、高可用的微服务系统。