RabbitMQ消息中间件

在这里插入图片描述
首先要先部署好RabbitMQ,可以利用docker命令进行部署

docker run \
 -e RABBITMQ_DEFAULT_USER=itcast \
 -e RABBITMQ_DEFAULT_PASS=123321 \
 -v mq-plugins:/plugins \
 --name mq \
 --hostname mq1 \
 -p 15672:15672 \
 -p 5672:5672 \
 -d \
 rabbitmq:3.8-management

在这里插入图片描述
在spring中使用RabbitMQ使用的时AMQP统一规范,需要引入依赖

<!--AMQP依赖,包含RabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

一、基本使用
推送端yml配置,virtual-host是配置rabbitmq的虚拟主机,类似于不同的服务器,这样当rabbitmq部署多个通道队列的时候,可以有效的进行隔离

spring:
  rabbitmq:
    host: 121.***.***.***
    port: 5672
    virtual-host: /
    username: test
    password: 123456

消费端yml配置,注意这里配置listener.simple.prefetch=1,意味着消费端每次只预取一个消息,消费过之后才能再次拉取消息

spring:
  rabbitmq:
    host: 121.***.***.***
    port: 5672
    virtual-host: /
    username: test
    password: 123456
    listener:
      simple:
        prefetch: 1

(一)、消息队列方式一
在这里插入图片描述
1.推送端
推送端主要使用的是RabbitTemplate

	@Autowired
    private RabbitTemplate rabbitTemplate;

    @RequestMapping("/sendMsg")
    public String sendMsg(){
        String queueName = "simple.queue";
        String message = "hello world";
        rabbitTemplate.convertAndSend(queueName,message);
        return "send success";
    }

2.消费端

@Component
public class MyRabbitListener {

    @RabbitListener(queues = "simple.queue")
    public void listeneQueue1(String msg) throws InterruptedException {
        System.out.println("收到msg:"+msg);
    }
}

(二)、消息队列方式二
在这里插入图片描述
1.推送端

	@Autowired
    private RabbitTemplate rabbitTemplate;

    @RequestMapping("/sendMsg")
    public String sendMsg(){
        String queueName = "simple.queue";
        String message = "hello world";
        rabbitTemplate.convertAndSend(queueName,message);
        return "send success";
    }

2.消费端

@Component
public class MyRabbitListener {

    @RabbitListener(queues = "simple.queue")
    public void listeneQueue1(String msg) throws InterruptedException {
        System.out.println("Listener111收到msg:"+msg);
        Thread.sleep(1000);
    }

    @RabbitListener(queues = "simple.queue")
    public void listeneQueue21(String msg) throws InterruptedException {
        System.out.println("Listener222收到msg:"+msg);
        Thread.sleep(20);
    }
}

(三)、消息队列方式三(采用fanoutexchange交换机)
在这里插入图片描述

1.推送端
首先需要配置交换机并绑定队列

@Configuration
public class FanoutConfig {

    @Bean
    public FanoutExchange fanoutExchange(){
        return new FanoutExchange("itcast.fanout");
    }

    @Bean
    public Queue fanoutQueue1(){
        return new Queue("fanout.queue1");
    }
	
    @Bean
    public Binding fanoutBinding(Queue fanoutQueue1,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue1).to(fanoutExchange);
    }
	//这里是绑定第二个队列,和上一个重复
    @Bean
    public Queue fanoutQueue2(){
        return new Queue("fanout.queue2");
    }

    @Bean
    public Binding fanoutBinding2(Queue fanoutQueue2,FanoutExchange fanoutExchange){
        return BindingBuilder.bind(fanoutQueue2).to(fanoutExchange);
    }
}

之后再编写消息推送代码

	@RequestMapping("/sendMsg2")
    public String sendMsg2(){
        String fanoutExchange = "itcast.fanout";
        String message = "fanout test";
        rabbitTemplate.convertAndSend(fanoutExchange,"aaa",message);
        return "send success";
    }

2消费端

@Component
public class MyRabbitListener {
    @RabbitListener(queues = "fanout.queue1")
    public void listeneQueue3(String msg) throws InterruptedException {
        System.out.println("Listener333收到msg:"+msg);
    }

    @RabbitListener(queues = "fanout.queue2")
    public void listeneQueue4(String msg) throws InterruptedException {
        System.out.println("Listener444收到msg:"+msg);
    }
}

(四)、消息队列方式四(采用directexchange交换机)
其实就是在上一个基础之上加入了bingkey,使得在推送数据的时候,交换机会根据bingkey添加到还有相同key的消息队列
在这里插入图片描述
1.推送端

	@Autowired
    private RabbitTemplate rabbitTemplate;
    @RequestMapping("/sendMsg3")
    public String sendMsg2(){
        String directExchange = "itcast.direct";
        String message = "direct test";
        Map<String,String> map = new HashMap<>();
        map.put("女生","柳岩");
        map.put("男生","蔡徐坤222");
        rabbitTemplate.convertAndSend(directExchange,"red",map);
        return "send success";
    }

2.消费端
由于上一个方式的绑定需要写非常多的bean,所以这里可以采用注解的方式进行交换机和队列的绑定

@Component
public class MyRabbitListener {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue1"),
            exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key = {"red","blue"}
    ))
    public void listeneQueue1(String msg) throws InterruptedException {
        System.out.println("directListener111收到msg:"+msg);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "direct.queue2"),
            exchange = @Exchange(name = "itcast.direct",type = ExchangeTypes.DIRECT),
            key = {"red","yellow"}
    ))
    public void listeneQueue2(Map msg) throws InterruptedException {
        System.out.println("directListener222收到msg:"+msg);
    }
}

(五)、消息队列方式五(采用topicexchange交换机)
在这里插入图片描述
通配符用#表示,比如china.#或#.news

二、消息转换器
当我们使用AMQP发送Object对象时,默认会采取序列化的方法把对象转化为字节数据,这样一是数据庞大,二是不便于查阅,所以我们一般采用json的方式进行序列化
解决方法:
在这里插入图片描述

RabbitMQ高级
一、生产者确认机制
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
ReturnCallback应配置为一个单例的bean
在这里插入图片描述
ConfirmCallback应该放到每一个发送消息的方法中

二、消息持久化
实际上RabbitMQ会默认进行交换机、队列和消息的持久化,当然我们也可以进行更改。

三、消费者确认机制
消费者确认机制可以设置为auto模式
在这里插入图片描述
这种方式,如果消费端监听出现异常没有返回nack,则RabbitMQ会保留数据并再次向消费端推送,从而就会进入死循环,由于速度非常快,所以对RabbitMQ的消耗是非常大的,这种方式并不常用。
我们可以在消费端配置失败重试,在消费端进行失败重新循环,就可以环节RabbitMQ的压力
在这里插入图片描述
在这里插入图片描述

如果达到指定次数仍然失败,会自动返回nack给RabbitMQ,而RabbitMQ会再次向消费端推送,同样会进入死循环,只不过循环比较慢,对RabbitMQ开销比较小,我们可以更改为达到失败次数之后,直接向另一个交换机发送失败信息,由其他服务或者人工进行处理
在这里插入图片描述

四、死信交换机
在这里插入图片描述
这里与之前消费失败转发至error交换机有所不同,这里并不是在消费端转发至error交换机,而是消费端声明reject、nack或者超时未消费或者消息堆满了,由自身队列将信息打给dl队列
这里的basic.reject和basic.nack可以通过监听函数中的Channel实现

关于Channel的使用如下图
在这里插入图片描述

(一)设置延时TTL
方式一:在队列中设置延时时间和死信交换机
在这里插入图片描述
也可以在监听消息中绑定,这样的好处是在监听队列的同时就可以绑定死信队列,不会出现@bean声明绑定队列和@RabbitListener声明绑定队列重复报错的问题了

@RabbitListener(bindings = @QueueBinding(
            value = @Queue(name = "ttl.queue2",durable = "true",arguments = {
                    @Argument(name = "x-dead-letter-exchange", value = "dl.direct"),
                    @Argument(name = "x-dead-letter-routing-key", value = "dl"),
                    @Argument(name = "x-message-ttl", value = "10000")
            }),
            exchange = @Exchange(name = "ttl.direct"),
            key = {"ttl"}
    ))
    public void listeneTtlqueue(String msg, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) throws InterruptedException, IOException {
        System.out.println("ttl:"+msg);
        channel.basicReject(deliveryTag, false);
    }

方式二:在发送消息的时候设置延时时间,但仍然需要在声明队列的时候先添加死信交换机
在这里插入图片描述
当自身消息队列不设置消费者监听时,则成了真正的延时队列,当然还有更加简便的方式实现延时队列,将在接下来简介。

(二)延时队列
1.下载插件
RabbitMQ有一个官方的插件社区,地址为:https://www.rabbitmq.com/community-plugins.html
在这里插入图片描述
2.上传插件

docker volume inspect mq-plugins

通过上述命令获取插件目录
在这里插入图片描述
将插件上传至对应目录

3.进入容器

docker exec -it mq bash

4.安装

rabbitmq-plugins enable rabbitmq_delayed_message_exchange

5.使用
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
五、消息堆积和惰性队列
有时候消息来不及消费的话,就会产生堆积,本来消息是在内存中,但消息益处时,就将一部分消息转存在硬盘上,由于内存和硬盘读取的速度不一样,就会产生性能波动,对于消息较多的队列,我们可以直接设置为存储到硬盘上,也就是惰性队列
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值