1.概览
Spring WebFlux整合reactor-rabbitmq。有发送和接受。并且我是走延时插件x-delay的延时消息,所以在这里记录下。
jdk17、springboot2.6.7
1. 让运维先配置队列
Routing Key: xxxname
Queue: xxxname
Exchange: xxxname
持久化,不自动删除
2. 配置类
pom依赖
java
<!-- rabbitmq -->
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor.rabbitmq</groupId>
<artifactId>reactor-rabbitmq</artifactId>
</dependency>
先说整体的配置类
java
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import javax.annotation.PreDestroy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;
import reactor.rabbitmq.RabbitFlux;
import reactor.rabbitmq.Receiver;
import reactor.rabbitmq.ReceiverOptions;
import reactor.rabbitmq.Sender;
import reactor.rabbitmq.SenderOptions;
/**
* rabbitMq 配置
*
*/
@Slf4j
@Configuration
@RequiredArgsConstructor
public class RabbitMqConfig {
Mono<Connection> connectionMono;
@Bean
public Mono<Connection> connectionMono(@Value("${rabbit.host}") String host,
@Value("${rabbit.port}") int port,
@Value("${rabbit.username}") String username,
@Value("${rabbit.password}") String password) {
var connectionFactory = new ConnectionFactory();
connectionFactory.useNio();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost("/");
connectionMono = Mono.fromCallable(() -> connectionFactory.newConnection("reactor-rabbit")).cache();
return connectionMono;
}
@Bean
public Receiver rabbitReceiver(Mono<Connection> connectionMono) {
return RabbitFlux.createReceiver(new ReceiverOptions().connectionMono(connectionMono));
}
@Bean
public Sender rabbitSender(Mono<Connection> connectionMono) {
return RabbitFlux.createSender(new SenderOptions().connectionMono(connectionMono));
}
@PreDestroy
public void close() throws Exception {
log.info("[RabbitMqConfig] close connection");
Connection connection = connectionMono.block();
if (connection != null) {
connection.close();
}
}
}
3. 监听者
java
import javax.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import reactor.rabbitmq.Receiver;
@Component
@Slf4j
@RequiredArgsConstructor
public class RabbitMqListener {
private static final String QUEUE = "delayed.queue";
private final Receiver rabbitReceiver;
private static final String ACK = "ACK";
/**
* 初始化监听
*/
public @PostConstruct void initQueue() {
rabbitReceiver.consumeManualAck(QUEUE)
.flatMap(delivery -> Mono.defer(() -> {
String message = new String(delivery.getBody());
// todo something
return Mono.fromRunnable(delivery::ack)
.thenReturn(ACK);
})
// 这里两行代码解决问题 :因为错误导致整个流结束
.onErrorResume(throwable -> {
log.warn("[rabbitReceiver] RabbitMq 异常 直接确认 消息:{}", new String(delivery.getBody()), throwable);
return Mono.fromRunnable(delivery::ack)
.thenReturn(ACK);
})
)
.subscribe();
}
}
4. 发送者
发送的是延时消息,借助gitlab的issue里大佬提供的代码
java
import java.util.HashMap;
import java.util.Map;
import org.jetbrains.annotations.NotNull;
import reactor.rabbitmq.ExchangeSpecification;
/**
* @date 2025/12/19 15:57
*/
public class DelayedExchangeSpecification extends ExchangeSpecification {
private static final String DELAYED_TYPE = "x-delayed-message";
private static final String DELAYED_ARGUMENT_KEY = "x-delayed-type";
public DelayedExchangeSpecification() {
type(super.getType());
}
public static ExchangeSpecification exchange() {
return new DelayedExchangeSpecification();
}
public static @NotNull ExchangeSpecification exchange(@NotNull String name) {
return new DelayedExchangeSpecification().name(name);
}
@Override
public @NotNull DelayedExchangeSpecification type(@NotNull String type) {
Map<String, Object> args = getArguments();
if (args == null) {
args = new HashMap<>();
}
args.put(DELAYED_ARGUMENT_KEY, type);
arguments(args);
return this;
}
@Override
public @NotNull String getType() {
return DELAYED_TYPE;
}
}
延时消息类
java
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.AMQP.BasicProperties;
import java.util.HashMap;
import java.util.Map;
import reactor.rabbitmq.OutboundMessage;
import reactor.util.annotation.Nullable;
/**
* @date 2025/12/19 15:58
*/
public class DelayedOutboundMessage extends OutboundMessage {
public static final String DELAYED_HEADER_KEY = "x-delay";
/**
* @param delayMillis Delay time in milliseconds 注意时间单位,可以用Duration改造这里统一接受,你来负责统一转换
*/
public DelayedOutboundMessage(String exchange, String routingKey, long delayMillis, byte[] body) {
super(exchange, routingKey, delayedProperties(null, delayMillis), body);
}
/**
* @param delayMillis Delay time in milliseconds 注意时间单位,可以用Duration改造这里统一接受,你来负责统一转换
*/
public DelayedOutboundMessage(String exchange, String routingKey, long delayMillis, BasicProperties properties, byte[] body) {
super(exchange, routingKey, delayedProperties(properties, delayMillis), body);
}
private static BasicProperties delayedProperties(@Nullable AMQP.BasicProperties source, long delayMillis) {
AMQP.BasicProperties.Builder builder;
if (source != null) {
builder = source.builder();
} else {
builder = new BasicProperties.Builder();
}
Map<String, Object> headers;
if (source != null) {
headers = source.getHeaders();
} else {
headers = new HashMap<>();
}
headers.put(DELAYED_HEADER_KEY, delayMillis);
return builder.headers(headers).build();
}
}
具体的发送方法
java
import cn.hutool.json.JSONUtil;
import java.nio.charset.StandardCharsets;
import java.time.Duration;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.rabbitmq.BindingSpecification;
import reactor.rabbitmq.Sender;
/**
* 发送消息工具
*
*/
@Component
@Slf4j
@RequiredArgsConstructor
public class RabbitMqSender {
private static final String EXCHANGE = "delayed.exchange";
private static final String ROUTING_KEY = "delayed.routing-key";
private static final String QUEUE = "delayed.queue";
private final Sender sender;
public void sendDelayMessage(String method, String content, Duration duration) {
log.debug("[RabbitMqSender] sendDelayMessage method: {} content: {} duration: {}", method, content, duration);
sender.declare(DelayedExchangeSpecification.exchange(EXCHANGE)
.durable(true)// 持久化
.autoDelete(false)// 不自动删除
.type("x-delayed-message") // 类型
)
.then(sender.bind(BindingSpecification.binding(EXCHANGE, ROUTING_KEY, QUEUE)))
.then(sender.send(Flux.just(new DelayedOutboundMessage(
EXCHANGE,
ROUTING_KEY,
duration.toMillis(),
content.getBytes(StandardCharsets.UTF_8)
))))
.doOnError(throwable -> log.warn("[RabbitMqSender] sendDelayMessage method: {} content: {} duration: {}", method, content, duration, throwable))
.subscribe();
}
}