Spring Cloud Stream RabbitMQ动态路由Key
前言
这里有个业务是这样的,我需要在不同的操作后给用户发送不同的邮件,由于比较耗时所以引入消息中间件,不同的邮件对应的消息类型是不一样的,所以需要生产者往队列里发送数据时绑定好路由key,例如:
图里表示交换机根据路由key绑定了不同的队列。
要达到这种效果,首先消费者肯定是可以根据路由key来决定消息是不是发送给自己的,对于生产者则需要用到routingKeyExpression
来决定往哪个路由key发送数据(大概是这个意思)。
然后就是stream中的group其实对应到rabbitMQ中就是队列的概念,所以我们这里设置两个不同的group来对应到不同的队列,区分开业务;
例子
这里我定义了两个服务对应消费者和生产者。
生产者
spring:
application:
name: producer
cloud:
stream:
binders: # 绑定MQ服务信息(此处我们是RabbitMQ)
etpmsRabbitMQ: # 给Binder定义的名称,⽤于后⾯的关联
type: rabbit # MQ类型,如果是Kafka的话,此处配置kafka
environment: # MQ环境配置(⽤户名、密码等)
spring:
rabbitmq:
host: localhost
port: 5672
username: admin
password: xxxxxx
bindings: # 关联整合通道和binder对象
output: # output是我们定义的通道名称,此处不能乱改
destination: testExchange # 要使⽤的Exchange名称(消息队列主题名称)
content-type: text/plain # application/json # 消息类型设置,⽐如json
binder: etpmsRabbitMQ # 关联MQ服务
rabbit:
bindings:
output:
producer:
# 生产者配置RabbitMq的动态路由键
routingKeyExpression: headers.type
package top.chenyt.producer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
/**
* @author yantao.chen
*/
@Service
public class ProviderService {
/**
* 将MessageChannel的封装对象Source注⼊到这⾥使⽤
*/
@Autowired
private Source source;
public void sendMessage(String content, String type) {
// 向mq中发送消息(并不是直接操作mq,应该操作的是spring cloud stream)
// 使⽤通道向外发出消息(指的是Source⾥⾯的output通道)
source.output().send(MessageBuilder.withPayload(content).setHeader("type",type).build());
}
}
package top.chenyt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.cloud.stream.messaging.Source;
/**
* @ClassName etpms-parent
* @Author Jinondo
* @Date 2022/1/31 12:42
*/
@SpringBootApplication
@Slf4j
@EnableBinding({Source.class})
public class ProducerApplication {
public static void main(String[] args) {
SpringApplication.run(ProducerApplication.class, args);
}
}
主要是yml配置添加:routingKeyExpression: headers.type
发送消息的时候setHeader一下
消费者
spring:
application:
name: consumer
cloud:
stream:
binders: # 绑定MQ服务信息(此处我们是RabbitMQ)
etpmsRabbitMQ: # 给Binder定义的名称,⽤于后⾯的关联
type: rabbit # MQ类型,如果是Kafka的话,此处配置kafka
environment: # MQ环境配置(⽤户名、密码等)
spring:
rabbitmq:
host: localhost
port: 5672
username: admin
password: xxxxx
bindings: # 关联整合通道和binder对象
input: # input是我们定义的通道名称,此处不能乱改
destination: testExchange # 要使⽤的Exchange名称(消息队列主题名称)
content-type: text/plain # application/json # 消息类型设置,⽐如json,自动将对象转为json
binder: etpmsRabbitMQ # 关联MQ服务
group: register
my-input:
destination: testExchange # 要使⽤的Exchange名称(消息队列主题名称)
content-type: text/plain # application/json # 消息类型设置,⽐如json,自动将对象转为json
binder: etpmsRabbitMQ # 关联MQ服务
group: task
rabbit:
bindings:
my-input:
consumer:
bindingRoutingKey: task
input:
consumer:
bindingRoutingKey: register
这里我就定义了两个通道,一个是默认的input,一个是自定的
package top.chenyt.consumer;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
public interface MySink {
String MY_INPUT = "my-input";
@Input(MY_INPUT)
SubscribableChannel myinput();
}
package top.chenyt.consumer;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
/**
* @ClassName etpms-parent
* @Author Jinondo
* @Date 2022/1/31 12:42
*/
@Service
public class ConsumerMsg {
@StreamListener(Sink.INPUT)
public void receiveMessages(Message<String> message) {
System.out.println("========= input接收到的消息:" + message.getPayload());
}
@StreamListener(MySink.MY_INPUT)
public void receiveMessages02(Message<String> message) {
System.out.println("========= myinput接收到的消息:" + message.getPayload());
}
}
package top.chenyt;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Sink;
import top.chenyt.consumer.MySink;
/**
* @ClassName etpms-parent
* @Author Jinondo
* @Date 2022/1/31 12:42
*/
@SpringBootApplication
@Slf4j
@EnableBinding({Sink.class, MySink.class})
public class ConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
这样就能实现根据不同的消息类型对应到不同的队列且不同的路由key去了