本文主要解决以下几个问题
1、rabbtiMq的消息队列topic,交换器exchange 和 路由key routKey的概念及关系
2、rabbitMq 消息的生产和消费的规则逻
3、springboot集成rabbitMq
4、通过代码自动创建 topic 、exchange、以及指定routKey绑定 topic 和 exchange
一、rabbtiMq的消息队列topic,交换器exchange 和 路由key routKey的概念及关系
1、消息队列topic: 本质就是一个queue, 用来存储消息,具体可分为 消息队列,延迟队列,和死信队列。创建一个queue, 如下操作即可
public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete,
@Nullable Map<String, Object> arguments)
Params:
name – the name of the queue - must not be null; set to "" to have the broker generate the name.
durable – true if we are declaring a durable queue (the queue will survive a server restart)
exclusive – true if we are declaring an exclusive queue (the queue will only be used by the declarer's connection)
autoDelete – true if the server should delete the queue when it is no longer in use
arguments – the arguments used to declare the queue
创建延迟队列,则设置arguments 参数,设置 ttl的延迟时间即可,时间单位为毫秒
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 6000);
2、交换器exchange
exchange的类型主要分为direct、topic和 fanout
direct类型-直连交换机:
最直接的理解,就是queue 和exchange 通过routKey 一对一绑定。
例如:queueA、exchange_direct 通过routKey 绑定,发消息时,指定exchange为exchange_direct, 指定路由键为 routKey, 则消息会被路由到队列queueA上,供消费者监听队列queueA并消费对应的消息
topic类型-主题交换机:
topic类型的交换机,对绑定queue的routKey,支持通配符匹配。
routKey为#: topicExchange和queue绑定,发消息时,不指定routKey, 消费者会接收所有和#绑定的消息队列,此时功能和 fanout类型的exchange一致
routKey为routKey.#, queueA,queueB 和 topicExchange 分别通过 routKey.# 进行绑定,发消息时,指定routKey为, routKey.a 或者 routKey.b, 对于消费者监听 queueA 和queueB 都能同时接收到消息
routKey为 *.routKey.* : queueA 通过 a.routKey.b 和topicExchange 绑定, queueB 通过*.routKey.* 和 topicExchange绑定, 发消息时,指定 routKey 为 a.routKey.b。 则消费者监听消息时, queueA 和queueB 都能接收到消息
routKey为 routKey时, queueA 通过routKey 和 topicExchange 绑定,这个功能和 directExchange功能一致
二、rabbitMq 消息的生产和消费的规则逻


三、springboot集成rabbitMq
基础连接信息
public abstract class BaseMQConfig {
private static final int CHANNEL_CACHE_SIZE = 180_000;
private static final int CONNECTION_CACHE_SIZE = 1024;
private String host = "";
private Integer port = 5672;
private String user = "root";
private String password = "root";
protected CachingConnectionFactory createConnectionFactory(String virtualHost) {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
connectionFactory.setCacheMode(CachingConnectionFactory.CacheMode.CONNECTION);
connectionFactory.setChannelCacheSize(CHANNEL_CACHE_SIZE);
connectionFactory.setConnectionCacheSize(CONNECTION_CACHE_SIZE);
connectionFactory.setUsername(user);
connectionFactory.setPassword(password);
connectionFactory.setPublisherReturns(false);
connectionFactory.setPublisherConfirms(false);
if (StringUtils.isNotBlank(virtualHost)) {
connectionFactory.setVirtualHost(virtualHost);
}
return connectionFactory;
}
protected Binding createBinding(AmqpAdmin amqpAdmin, Queue queue, Exchange exchange) {
Binding binding;
if (exchange instanceof FanoutExchange) {
binding = BindingBuilder.bind(queue)
.to((FanoutExchange) exchange);
} else if (exchange instanceof DirectExchange) {
binding = BindingBuilder.bind(queue)
.to((DirectExchange) exchange)
.with(queue.getName());
} else {
return null;
}
amqpAdmin.declareBinding(binding);
return binding;
}
protected RabbitTemplate createTemplate(ConnectionFactory connectionFactory, String exchange) {
RabbitTemplate template = techRabbitBuilder.createRabbitTemplate(connectionFactory);
template.setExchange(exchange);
return template;
}
public abstract ConnectionFactory connectionFactory();
public abstract AmqpAdmin amqpAdmin(ConnectionFactory connectionFactory);
public abstract Exchange exchange();
public abstract RabbitTemplate template(ConnectionFactory connectionFactory);
}
基于不同的消息队列,创建不同的admin template 和绑定关系,确保 bean的别名不同即可
@Configuration
public class CalculateMQConfig extends BaseMQConfig {
public static final String CALBenefit_QUEUE_NAME = "queue_demo";
public static final String CALBenefit_ROUTING_KEY = "routing.key_demo";
public static final String CALBenefit_MEMBER_EXCHANGE = "exchange_demo";
private static final String CALBenefit_VIRTUAL_HOST = "demo_vhost";
public static final String NOTIFYUSER_QUEUE_NAME = "notify_user";
public static final String NOTIFYUSER_ROUTING_KEY = "notify_user.key";
@Bean(name = "calBenefitConnection")
public ConnectionFactory connectionFactory() {
return createConnectionFactory(CALBenefit_VIRTUAL_HOST);
}
@Bean(name = "calBenefitAmqpAdmin")
public AmqpAdmin amqpAdmin(@Qualifier("calBenefitConnection") ConnectionFactory calBenefitConnection) {
return new RabbitAdmin(calBenefitConnection);
}
@Bean(name = "calBenefitExchange")
public DirectExchange exchange() {
return new DirectExchange(CALBenefit_MEMBER_EXCHANGE);
}
@Bean(name = "calBenefitRabbitTemplate")
public RabbitTemplate template(@Qualifier("calBenefitConnection") ConnectionFactory calBenefitConnection) {
return createTemplate(calBenefitConnection, CALBenefit_MEMBER_EXCHANGE);
}
@Bean(name = "calBenefitQueue")
public Queue contractQueue() {
return new Queue(CALBenefit_QUEUE_NAME);
}
@Bean(name = "calBenefitBinding")
public Binding contractBinding(@Qualifier("calBenefitAmqpAdmin") AmqpAdmin blackHoleAmqpAdmin,
@Qualifier("calBenefitQueue") Queue contractQueue,
@Qualifier("calBenefitExchange") DirectExchange contractExchange) {
Binding binding = BindingBuilder.bind(contractQueue)
.to(contractExchange)
.with(CALBenefit_ROUTING_KEY);
blackHoleAmqpAdmin.declareBinding(binding);
return binding;
}
//若为多个不同host的消费者的配置,该bean 可在消费者监听时指定该bean,见下面的消费者配置
@Bean(name="rabbitListenerContainerFactory")//和kafka的配置仓库类一莫一样很牛逼的
public RabbitListenerContainerFactory<SimpleMessageListenerContainer> tt(){
//ConcurrentKafkaListenerContainerFactory
//消息的统一过滤器
//MessageConverter messageConverter = new ObjConsert();
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConcurrentConsumers(5);//允许同时消费数量为5
factory.setMaxConcurrentConsumers(10);//允许同时最大消费数量为10
factory.setReceiveTimeout(10000L);//10秒
//factory.setMessageConverter(messageConverter);//具体的逻辑要自己在ObjConsert里面写
factory.setAcknowledgeMode(AcknowledgeMode.AUTO);//设置手动提交
factory.setConnectionFactory(connectionFactory());
return factory;
}
}
生产者发消息
@Autowired
@Qualifier("calBenefitRabbitTemplate_consumer")
private AmqpTemplate rabbitTemplateConsumer;
@GetMapping("/test")
public void test() {
String msg = "这是个消息";
this.rabbitTemplateConsumer.convertAndSend(CalculateMQConfig.CALBenefit_MEMBER_EXCHANGE, CalculateMQConfig.CALBenefit_ROUTING_KEY, msg);
}
消费者
containerFactory 可以指定消费者对应的连接,若生产者和消费者属于同一host, 则可以不配置,若同一个项目,既有生产者,又有消费者,且生产者和消费者属于不同的连接,则可以指定containerFactory确定是走那个连接进行消费
@Slf4j
@Component
@RabbitListener(containerFactory="rabbitListenerContainerFactory",bindings = {@QueueBinding(value = @Queue(value = CalculateMQConfig.CALBenefit_QUEUE_NAME, durable = "true"),
exchange = @Exchange(value = CalculateMQConfig.CALBenefit_MEMBER_EXCHANGE, durable = "true"), key =
CalculateMQConfig.CALBenefit_ROUTING_KEY)})
public class CalculateReceiver {
@RabbitHandler
public void process(String message) {
log.info("事件 接受消息,message = {}", message);
}
}
四、通过代码自动创建 topic 、exchange、以及指定routKey绑定 topic 和 exchange
通过amqbAdmint 创建和通过channel 创建
@Configuration
public class RabbitMqQueueExchangeCreate {
public static String exchangeStr = "exchange_cutity";
public static String queueStr = "queue_topic_cutiyu";
public static String routKeyStr = "routKey.cutity";
@Autowired
private AmqpAdmin amqpAdmin;
@Autowired
@Qualifier("demoConnectionFactory")
ConnectionFactory connectionFactory;
@PostConstruct
public void init(){
Exchange exchange = new TopicExchange(exchangeStr);
Queue queue = new Queue(queueStr);
Binding noargs = BindingBuilder.bind(queue).to(exchange).with(routKeyStr).noargs();
amqpAdmin.declareExchange(exchange);
amqpAdmin.declareQueue(queue);
amqpAdmin.declareBinding(noargs);
System.out.println("===========");
Connection connection = connectionFactory.createConnection();
Channel channel = connection.createChannel(false);
try {
channel.exchangeDeclare(RabbitMqCnst.EXCHANGE_TOPIC_DEMO, BuiltinExchangeType.TOPIC,true);
channel.exchangeDeclare(RabbitMqCnst.EXCHANGE_FANOUT_DEMO, BuiltinExchangeType.FANOUT,true);
channel.queueDeclare(RabbitMqCnst.QUEUE_TOPIC_DEMO, false, false, false, null);
channel.queueDeclare(RabbitMqCnst.QUEUE_TOPIC_DEMO_A_B, false, false, false, null);
channel.queueDeclare(RabbitMqCnst.QUEUE_TOPIC_DEMO_C_D, false, false, false, null);
channel.queueDeclare(RabbitMqCnst.E_QUEUE_TOPIC_DEMO_F, false, false, false, null);
channel.queueDeclare(RabbitMqCnst.G_QUEUE_TOPIC_DEMO_H, false, false, false, null);
channel.queueBind(RabbitMqCnst.QUEUE_TOPIC_DEMO, RabbitMqCnst.EXCHANGE_TOPIC_DEMO, RabbitMqCnst.ROUTKEY_DEMO);
channel.queueBind(RabbitMqCnst.QUEUE_TOPIC_DEMO_A_B, RabbitMqCnst.EXCHANGE_TOPIC_DEMO, RabbitMqCnst.ROUTKEY_DEMO_A);
channel.queueBind(RabbitMqCnst.QUEUE_TOPIC_DEMO_C_D, RabbitMqCnst.EXCHANGE_TOPIC_DEMO, RabbitMqCnst.ROUTKEY_DEMO_X);
channel.queueBind(RabbitMqCnst.E_QUEUE_TOPIC_DEMO_F, RabbitMqCnst.EXCHANGE_TOPIC_DEMO, RabbitMqCnst.E_ROUTKEY_DEMO_F);
channel.queueBind(RabbitMqCnst.G_QUEUE_TOPIC_DEMO_H, RabbitMqCnst.EXCHANGE_TOPIC_DEMO, RabbitMqCnst.X_ROUTKEY_DEMO_X);
} catch (Exception e) {
e.printStackTrace();
}
}
}
本文详细介绍了RabbitMQ中消息队列topic、交换器exchange和路由键routKey的概念及它们之间的关系。讲解了direct和topic类型的exchange工作原理,并通过代码示例展示了如何在SpringBoot中集成RabbitMQ,创建和绑定topic、exchange。此外,还提供了自动创建队列和交换器的代码实现。
51万+

被折叠的 条评论
为什么被折叠?



