提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- RabbitMq问题记录
- 1、如果memory大小超过了high watermark的大小,会阻塞生产者发送过来的消息
- 2、RabbitListener注解下的方法用的private修饰,消费者启动以后,代码没有消费到消息。
- 3、明明是direct交换器绑定的队列,但是没有指定routing key,也能正常发送消息
- 4、添加好mq的包,写好configuration类以后,启动springboot发现web上没有我配置的交换机、队列
- 5、新建了一个commons的模块,在commons下面,新建了一个rabbit-config模块,然后把commons添加到了product服务模块,在product模块里启动以后,在web界面没有队列和交换器出现
- 6、如何配置消费消息手动确认
- 7、Mq管理界面中queues菜单下的features字段为空,其他的为D
- 8、消息发送开启comfirm机制,以为只有发送失败才会走回调方法,成功也会走的
- 9、死信队列的使用
- 10、如何路由多个死信队列
- 11、消息被手动确认的时候,deliveryTag有这么个东东,但是网上又说它是重复的
RabbitMq问题记录
1、如果memory大小超过了high watermark的大小,会阻塞生产者发送过来的消息
学rabbitmq的过程中,因为买的轻量级服务器,内存比较小,所以手动给mq设置了内存大小,也是百度以后稀里糊涂地设置,然后写了一个product,登录mq的web界面找不到消息,然后发现
Overview下面的Memory 是红色,百度再百度以后,说是RABBITMQ_MEMORY_HIGH_WATERMARK=0.4这个参数默认是0.4…反正就是已经超过high watermark的阈值了,这个参数就是用来设置这个阈值的,要么你把这个阈值调大,要么你运行的内存够大。如果是通过docker启动的容器,可以这样设置
docker run -e RABBITMQ_MEMORY_HIGH_WATERMARK=0.8 -m 512m -d --restart=always -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:3.8-management
另外一种方式就是通过写一个mq的config文件,听说这玩意要手动写,懒得搭理了,因为我是学习过程,所以直接docker rm 再docker run了
2、RabbitListener注解下的方法用的private修饰,消费者启动以后,代码没有消费到消息。
(1)首先我看了一下component注解所在位置,发现springboot扫描到,因为它默认的就是扫描当前包,以及当前包下的子包,然后通过这个验证了一下。很明显是扫描到了
ConfigurableListableBeanFactory beanFactory = SpringApplication.run(Demo1Application.class, args).getBeanFactory();
System.out.println(beanFactory.getBean(Consumer.class));;
(2)然后我又看了一下注解里的队列名,发现没有写错
(3)终极大法,无奈大法,因为我脑子马上想到的是它这里头的方法是从哪到哪,我要是能一层一层的看下去,那我应该就能找到问题所在,但是菜鸡一枚,我的大招是核对我的学习文档,我哪里写的和他不一样,发现RabbitListener这个注解的方法,我用的是private,改成了public以后就ok了,具体为什么我也没整明白
3、明明是direct交换器绑定的队列,但是没有指定routing key,也能正常发送消息
交换器的type:direct 、fanout、topic,讲道理direct交换器是需要指定对应的routing key,才能把消息发送到指定的队列上,我理解的是在创建交换器或者队列的时候,需要手动指定routing key
@Bean
protected Queue queue(){
Queue queue = new Queue("myQueue");
return queue;
}
这是我创建的队列,但是没有指定routing key,而且它绑定的是默认的direct类型的交换器。这种情况,会有一个默认的routing key:队列名。所以也能正常路由
总结:mq的基本配置
队列的创建:
@Bean
protected Queue queue(){
Queue queue = new Queue("myQueue");
return queue;
}
交换器的创建
@Bean
public TopicExchange topicExchange()
{
return new TopicExchange("topicEx");
}
@Bean
public FanoutExchange fanoutExchange()
{
return new FanoutExchange("fanoutEx");
}
交换器的绑定
@Bean
public Binding topicBinding(Queue queue,TopicExchange TopicExchange ){
return BindingBuilder.bind(queue).to(TopicExchange).with("test.");
}
4、添加好mq的包,写好configuration类以后,启动springboot发现web上没有我配置的交换机、队列
然后加了这个玩意以后就出来了
@Autowired
private RabbitTemplate rabbitTemplate;
@RabbitListener(queues = "dead_queue")
public void test(Message message, Channel channel) throws IOException {
System.out.println("消息:"+new String(message.getBody()));
long tag = message.getMessageProperties().getDeliveryTag();
channel.basicAck(tag,false);
}
5、新建了一个commons的模块,在commons下面,新建了一个rabbit-config模块,然后把commons添加到了product服务模块,在product模块里启动以后,在web界面没有队列和交换器出现
然后在product模块里尝试声明一下rabbit-config,结果没有代码提示,我本以为我把commons添加到product的依赖里就行了,最后又单独把rabbit-config添加到了product的pom的依赖里
6、如何配置消费消息手动确认
在yml文件里添加
rabbitmq:
host: ip
port: 5672
username: guest
password: guest
listener:
simple:
acknowledge-mode: manual
在消费者代码里,需要添加,如果不确认就会存在多次消费的情形
channel.basicAck(tag,false);
7、Mq管理界面中queues菜单下的features字段为空,其他的为D
表示它不是持久化的(durable)
8、消息发送开启comfirm机制,以为只有发送失败才会走回调方法,成功也会走的
rabbitTemplate.setConfirmCallback(new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack) {
// 消息成功发送到交换器
System.out.println("消息发送成功: " + correlationData);
} else {
// 消息发送失败,比如交换器不存在等
System.out.println("消息发送失败: " + cause);
}
}
});
9、死信队列的使用
在创建队列的时候,绑定私信交换机就可以了
1、可以给消息指定过期时间,但是我在写的时候,出现了
#method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg ‘x-dead-letter-exchange’ for queue ‘testQueue’ in vhost ‘/’: received the value ‘dead_exchange’ of type ‘longstr’ but current is none, class-id=50, method-id=10)
我前几天是这样写的:
@Bean
public Binding bindDeadExchange(Queue deadQueue,FanoutExchange deadExchange){
return BindingBuilder.bind(deadQueue).to(deadExchange);
}
我今天又是这样写的:
@Bean
public Queue testQueue(){
return QueueBuilder.nonDurable(RabbitMqConstant.TEST_QUEUE).deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE).build();
}
已经存在的和将要去创建的存在不一致的地方,然后我删了之前的队列重新写了一个
消息发送:
rabbitTemplate.convertAndSend("testExchange_fanout","", "消息", message -> {
message.getMessageProperties().setExpiration("10000");
return message;
});
2、或者给队列设置时间
@Bean
public Queue orderQueue(){
return QueueBuilder.nonDurable("orderQueue").deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE).ttl(10000).build();
}
发送消息的时候:
rabbitTemplate.convertAndSend("testExchange_fanout","", "消息");
3、添加mq插件,使用延迟交换机
没有尝试
1、2的缺陷就是时间是固定的,mq的队列是FIFO原则,如果你给不同消息设置不同的时间,就会影响进入死信队列的消息,导致它们会一致卡在原来的队列里
10、如何路由多个死信队列
业务:消息先进入 orderqueue,过期以后进入deadQueue1和deadQueue2这两个队列。这两个死信队列关联一个交换机就行了,注意到的点就是,发送到orderQueue的时候,routing key也是路由到死信队列的routing key
@Configuration
public class RabbitMqConfig {
@Bean
public Queue orderQueue(){
return QueueBuilder.nonDurable("orderQueue").deadLetterExchange(RabbitMqConstant.DEAD_EXCHANGE_DIRECT).ttl(10000).build();
}
@Bean
public DirectExchange deadDirectExchange(){
return ExchangeBuilder.directExchange(RabbitMqConstant.DEAD_EXCHANGE_DIRECT).build();
}
@Bean
public FanoutExchange fanoutExchange(){
return ExchangeBuilder.fanoutExchange(RabbitMqConstant.TEST_FANOUT_EXCHANGE).build();
}
@Bean
public Queue deadQueue1(){
return QueueBuilder.durable(RabbitMqConstant.DEAD_QUEUE).build();
}
@Bean
public Queue deadQueue2(){
return QueueBuilder.durable(RabbitMqConstant.DEAD_QUEUE2).build();
}
@Bean
public Binding bindDeadExchange(Queue deadQueue1,Exchange deadDirectExchange){
return BindingBuilder.bind(deadQueue1).to(deadDirectExchange).with("a").noargs();
}
@Bean
public Binding bindDeadExchange2(Queue deadQueue2,Exchange deadDirectExchange){
return BindingBuilder.bind(deadQueue2).to(deadDirectExchange).with("b").noargs();
}
@Bean
public Binding bindOrderQueueToEx(Queue orderQueue,FanoutExchange fanoutExchange){
return BindingBuilder.bind(orderQueue).to(fanoutExchange);
}
}
发送消息观察现象:
rabbitTemplate.convertAndSend("testExchange_fanout","a", "消息");
rabbitTemplate.convertAndSend("testExchange_fanout","b", "消息");
11、消息被手动确认的时候,deliveryTag有这么个东东,但是网上又说它是重复的
不同的消费者之间这个值确实存在重复的,但是消费者从队列里消费到消息,是基于channel,而每一个经过channel的message的deliveryTag是不会重复的。相当于队列里的消费分组以后id唯一