Spring Cloud Stream 3.1.x版本,弃用@StreamListener而采用函数式编程实现RocketMQ的接入

本文介绍了从Spring Cloud Stream 3.1.0版本开始弃用@StreamListener,转向函数式编程的原因和优势。详细讲述了如何通过函数式编程方式集成并使用RocketMQ,包括集成步骤、添加依赖、配置和编写代码(生产及消费消息)。这种方式简化了代码,增强了代码的独立性。

一、背景描述

自Spring Cloud 2020版本开始,Spring Cloud Stream的版本升级至3.1.0以上版本,目前最新版本为3.1.3。
自此版本开始@StreamListener上面就增加@Deprecated注解,不赞成使用,有可能接下来的版本会删除掉。下面就介绍下以函数式编程的方式代替StreamListener的方法

二、再叨叨几句为什么?

来自Spring的博客文章(https://spring.io/blog/2019/10/17/spring-cloud-stream-functional-and-reactive)上面写着a functional programming model in Spring Cloud Stream (SCSt). It’s less code, less configuration. Most importantly, though, your code is completely decoupled and independent from the internals of SCSt。这有利于使用Project Reactor提供的事件流抽象(如Flux和Mono)(https://projectreactor.io/). 命令函数在每个单独的事件上触发,而reactive函数只触发一次。

三、使用方法

3.1 如何集成

官方文档目前只集成了RabbitMQ和Kafka,查看Spring Cloud Alibaba的官方示例,没有提供集成方法。所以就按照RabbitMQ的方式,尝试在RocketMQ上测试验证,发现完全可用,说明rocketmq已经实现了Stream的函数式编辑的相关方法。这样就好办,直接开搞。

3.2 添加依赖

 <dependency>
     <groupId>com.alibaba.cloud</groupId>
     <artifactId>spring-cloud-starter-stream-rocketmq</artifactId>
 </dependency>

没有带版本号,需要先依赖

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>vip.mate</groupId>
            <artifactId>mate-starter-dependencies</artifactId>
            <version>3.3.8</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

3.3 写配置

spring:
  cloud:
    stream:
      bindings:
        sms-out-0:
          destination: sms-topic
          content-type: application/json
        sms-in-0:
          destination: sms-topic
          content-type: text/plain
          group: sms-group

注意:
消费者和生产者的使用方式与之前有变化:采用名称-out-数字的方式,用于生产者,名称-in-数字的方式用于消费者。

参考:https://docs.spring.io/spring-cloud-stream-binder-rabbit/docs/3.1.3/reference/html/spring-cloud-stream-binder-rabbit.html

以下摘录rabbitmq的官方示例:

	@Autowired
	private StreamBridge bridge;

	@Bean
	Consumer<List<String>> input() {
		return list -> {
			List<MyCorrelationData> results = new ArrayList<>();
			list.forEach(str -> {
				log.info("Received: " + str);
				MyCorrelationData corr = new MyCorrelationData(UUID.randomUUID().toString(), str);
				results.add(corr);
				this.bridge.send("output-out-0", MessageBuilder.withPayload(str.toUpperCase())
						.setHeader(AmqpHeaders.PUBLISH_CONFIRM_CORRELATION, corr)
						.build());
			});
			results.forEach(correlation -> {
				try {
					Confirm confirm = correlation.getFuture().get(10, TimeUnit.SECONDS);
					log.info(confirm + " for " + correlation.getPayload());
					if (correlation.getReturnedMessage() != null) {
						log.error("Message for " + correlation.getPayload() + " was returned ");

						// throw some exception to invoke binder retry/error handling

					}
				}
				catch (InterruptedException e) {
					Thread.currentThread().interrupt();
					throw new IllegalStateException(e);
				}
				catch (ExecutionException | TimeoutException e) {
					throw new IllegalStateException(e);
				}
		};
	}

3.3 写代码

3.3.1 生产消息
package vip.mate.message.service.impl;

import lombok.AllArgsConstructor;
import org.springframework.cloud.stream.function.StreamBridge;
import org.springframework.stereotype.Service;
import vip.mate.core.rocketmq.constant.MessageConstant;
import vip.mate.message.service.ISmsService;

/**
 * 发送短信实现类
 *
 * @author xuzhanfu
 */
@Service
@AllArgsConstructor
public class SmsServiceImpl implements ISmsService {

	private final StreamBridge streamBridge;

	/**
	 * 采用StreamBridge的发送方式
	 *
	 * @param message  短消息
	 * @link https://docs.spring.io/spring-cloud-stream/docs/3.1.0/reference/html/spring-cloud-stream.html#_binding_and_binding_names
	 */
	@Override
	public void sendSms(String message) {
		streamBridge.send(MessageConstant.SMS_MESSAGE_OUTPUT, message);
	}
}

3.3.2 消费消息
package vip.mate.message.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.function.Consumer;

/**
 * 短信消费者业务
 *
 * @author xuzhanfu
 */
@Slf4j
@Service
public class SmsConsumerService {

	/**
	 * 函数式编辑接收消息
	 *
	 * @return
	 */
	@Bean
	public Consumer<String> sms() {
		return message -> {
			log.info("接收的普通消息为:{}", message);
		};
	}
}

这就是函数式编程的方式,其中方法名,要与通道名的名称一致。

四、完整源码

项目GITHUB码云
MateCloud后端源码https://github.com/matevip/matecloudhttps://gitee.com/matevip/matecloud
Artemis前端源码https://github.com/matevip/artemishttps://gitee.com/matevip/artemis
Spring Cloud Stream 4.x 中使用 RabbitMQ 实现函数式编程风格并支持手动消息确认、延迟队列和死信队列,需要结合 RabbitMQ 的 TTL(Time To Live)机制与死信交换器(Dead Letter Exchange, DLX)功能,并通过函数式编程模型进行配置。以下为实现的关键步骤和配置说明。 ### 函数式编程风格的配置 从 Spring Cloud Stream 3.1 开始引入了函数式编程模型,允许开发者通过 `Supplier`、`Consumer` 和 `Function` 接口定义消息处理逻辑。在 4.x 版本中该模型更加成熟且推荐使用。 ```java @Bean public Consumer<Message<String>> delayedMessageConsumer() { return message -> { try { // 处理业务逻辑 System.out.println("Received: " + message.getPayload()); // 手动确认 Channel channel = (Channel) message.getHeaders().get(AmqpHeaders.CHANNEL); Long deliveryTag = (Long) message.getHeaders().get(AmqpHeaders.DELIVERY_TAG); if (channel != null && deliveryTag != null) { channel.basicAck(deliveryTag, false); } } catch (Exception e) { // 消息拒绝或重新入队 throw new RuntimeException("Error processing message", e); } }; } ``` ### 启用手动确认模式 在 `application.yml` 或 `application.properties` 中启用手动确认: ```yaml spring: cloud: stream: rabbit: bindings: delayedMessageConsumer-in-0: consumer: acknowledge-mode: manual ``` ### 配置延迟队列与死信队列 RabbitMQ 原生不支持延迟队列,但可以通过设置消息的 TTL 并绑定死信交换器来实现类似效果。以下是相关配置示例: ```yaml spring: cloud: stream: bindings: delayedMessageProducer-out-0: destination: delayed-topic delayedMessageConsumer-in-0: destination: delayed-topic group: delayed-group rabbit: bindings: delayedMessageConsumer-in-0: consumer: auto-bind-dlq: true dlq-ttl: 5000 # 设置延迟时间为5秒 post-prefetch-count: 1 ``` 上述配置中,`dlq-ttl` 表示消息在进入死信队列前的有效时间(单位:毫秒),即实现了延迟效果[^3]。 ### 死信队列处理逻辑 为了处理被拒绝的消息或者超时未确认的消息,可以单独定义一个用于消费死信队列的消费者: ```java @Bean public Consumer<Message<String>> deadLetterMessageConsumer() { return message -> { System.err.println("Dead letter received: " + message.getPayload()); // 记录日志或执行补偿逻辑 }; } ``` 并在配置文件中指定死信队列的绑定关系: ```yaml spring: cloud: stream: bindings: deadLetterMessageConsumer-in-0: destination: delayed-topic.dlq group: dlq-group ``` ### 总结 通过以上配置和代码,可以在 Spring Cloud Stream 4.x 中实现基于 RabbitMQ 的函数式编程风格,并支持手动确认、延迟队列和死信队列功能。这种方式不仅保持了系统的可维护性和扩展性,同时也避免了对 RabbitMQ 插件的依赖[^1]。 ---
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值