linux上RabbitMQ安装及Springboot使用教程

  • 准备linux安装rabbitMQ

拉取镜像

docker pull rabbitmq:management

安装容器

docker run -d --hostname rabbitmq --name rabbitmq -e RABBITMQ_DEFAULT_USER=lailai -e RABBITMQ_DEFAULT_PASS=lailai -p 5671:5671 -p 15672:15672 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 镜像id

一、导入依赖

        <!--rabbitMQ-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

       <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
        </dependency>

二、配置application.properties/yml

# 应用名
spring.application.name=XXX
# rabbitmq配置信息
# ip
spring.rabbitmq.host=XXXX
# 端口
spring.rabbitmq.port=5672
# 用户名
spring.rabbitmq.username=XXX
# 密码
spring.rabbitmq.password=XXX
# 配置虚拟机
spring.rabbitmq.virtual-host=/
# 开启return机制 (用于处理一些不可路由的消息,在一些特殊的情况下,当前的exchange不存在或者指定的路由key路由不到,这时如果我们需要及时监听这种消息,就需要return机制)
spring.rabbitmq.publisher-returns=true
# 开启消息确认机制 confirm 异步 
spring.rabbitmq.publisher-confirm-type=correlated
# 消息开启手动确认
spring.rabbitmq.listener.simple.acknowledge-mode=manual
#最大重试传递次数
spring.rabbitmq.listener.simple.retry.max-attempts=3
#第一次和第二次尝试传递消息的间隔时间 单位毫秒
spring.rabbitmq.listener.simple.retry.initial-interval=5000ms
#最大重试时间间隔,单位毫秒
spring.rabbitmq.listener.simple.retry.max-interval=300000ms
spring.rabbitmq.listener.simple.retry.multiplier=3
#以上配置的间隔0s  5s  15s  45s

#重试次数超过上面的设置之后是否丢弃(消费者listener抛出异常,是否重回队列,默认true:重回队列, false为不重回队列(结合死信交换机))
spring.rabbitmq.listener.simple.default-requeue-rejected=true
### 模板配置
##设置为 true 后 消费者在消息没有被路由到合适队列情况下会被return监听,而不会自动删除
spring.rabbitmq.template.mandatory=true
#在单个请求中处理的消息个数,他应该大于等于事务数量(unack的最大数量)
spring.rabbitmq.listener.simple.prefetch=2

三、3种常用类型

  • rabbitMQ有fanout广播发布模式,direct路由模式,topic通配符主题订阅模式,下面将逐一详细的讲解三种模式的用法及应用场景。

1.fanout广播发布模式

  • config配置
public  static final String FANOUT_QUEUENAME="queue_fanout";//直连型队列名字

public  static final String EXCHANGE_QUEUENAME="fanout_exchange";//直连类型交换机名字

public  static final String QUEUE_DIRECT="queue-direct";


/*
  1.direct(广播式直连交换机)
 */

/**
 *  ① 声明队列
 * @return
 */
@Bean
public Queue fanoutTest(){
    /**
     * 1.队列名字
     * 2.durable:是否持久化队列
     * 3.是否独享、排外的
     * 4.是否自动删除(临时队列)
     * 5.arguments:队列的其他属性参数(这里不需要)
     */
    return new Queue(FANOUT_QUEUENAME,true,false,false);
}

/**
 * ② 声明交换机
 * @return
 */
@Bean
public FanoutExchange fanoutExchange(){
    return ExchangeBuilder.directExchange(EXCHANGE_QUEUENAME).durable(true).build();
}

/**
 * ③ 将消息队列与直连型交换机绑定
 * @return
 */
@Bean
public Binding bindQueueAndExchange(){
    return BindingBuilder.bind(fanoutTest()).to(fanoutExchange());
}

发送消息

@Autowired
private RabbitTemplate rabbitTemplate;

/**
 * 发送消息
 * @param message 消息内容
 */
@Override
public void sendDirect(String message) {
   
    rabbitTemplate.convertAndSend(RabbitmqConfig.EXCHANGE_QUEUENAME, "", "测试广播式直连型模型:" + message);
}

注:

① 使用 convertSendAndReceive 方法时的结果:使用此方法,只有确定消费者接收到消息,才会发送下一条信息,每条消息之间会有间隔时间;使用 convertAndSend 方法时的结果:输出时没有顺序,不需要等待,直接运行 不会阻塞

② 发送消息方法的构造方法参数,如果没有没有路由键(routingKey)也需要传空;

接收消息

//接受队列消息
@RabbitListener(queues = RabbitmqConfig.FANOUT_QUEUENAME)
public void receiveMessage(String msg, Channel channel, Message message, Object object) {
    System.out.println("fanout收到的消息msg:"+msg);
    System.out.println("channel:"+channel);
    System.out.println("message:"+message);
    System.out.println("object:"+object);
}

2.direct路由模式

  • config配置

  • public  static final String QUEUE_NAME="queue_direct";//直连型队列名字
    
    public  static final String EXCHANGE_NAME1="direct_exchange1";//直连类型交换机名字
    
    public  static final String EXCHANGE_NAME2="direct_exchange2";//直连类型交换机名字
    
    public  static final String ROUTING_KEY1="routingKey_a";//路由键1
    public  static final String ROUTING_KEY2="routingKey_b";//路由键2
    
    /*
      1.direct(路由交换机)
     */
    
    /**
     *  ① 声明队列
     * @return
     */
    @Bean
    public Queue directTest(){
        /**
         * 1.队列名字
         * 2.durable:是否持久化队列
         * 3.是否独享、排外的
         * 4.是否自动删除(临时队列)
         * 5.arguments:队列的其他属性参数(这里不需要)
         */
        HashMap<String, Object> map = new HashMap<>();
        map.put("x-message-ttl",3000);//三秒消息过期
        return new Queue(QUEUE_NAME,true,false,false);
    }
    
    /**
     * ② 声明交换机1
     * @return
     */
    @Bean
    public DirectExchange directExchange1(){
        return ExchangeBuilder.directExchange(EXCHANGE_NAME1).durable(true).build();
    }
    /**
     * ③ 将消息队列与直连型交换机1绑定
     * @return
     */
    @Bean
    public Binding bindQueueAndExchange1(){
        return BindingBuilder.bind(directTest()).to(directExchange1()).with(ROUTING_KEY1);
    }
    
    
    /**
     * ② 声明交换机1
     * @return
     */
    @Bean
    public DirectExchange directExchange2(){
        return ExchangeBuilder.directExchange(EXCHANGE_NAME2).durable(true).build();
    }
    /**
     * ③ 将消息队列与直连型交换机2绑定
     * @return
     */
    @Bean
    public Binding bindQueueAndExchange2(){
        return BindingBuilder.bind(directTest()).to(directExchange2()).with(ROUTING_KEY2);
    }
    

发送消息

@Autowired
private RabbitTemplate rabbitTemplate;

/**
 * 路由模式发送消息
 */
@Override
public void sendDirect() {
    for (int i = 1; i <= 20; i++) {
         if(i%2==0){
             MessagePostProcessor messagePostProcessor = message -> {
                 message.getMessageProperties().setExpiration("10000");
                 return message;
             };
             rabbitTemplate.convertAndSend( DirectConfig.EXCHANGE_NAME1,DirectConfig.ROUTING_KEY1,"出现偶数数字了" ,messagePostProcessor); //不手动ack十秒过期
             rabbitTemplate.convertAndSend( DirectConfig.EXCHANGE_NAME1,DirectConfig.ROUTING_KEY1,"出现偶数数字:" + i);
         }else {
             rabbitTemplate.convertAndSend( DirectConfig.EXCHANGE_NAME1,DirectConfig.ROUTING_KEY2,"出现奇数数字了" ); //不会到交换机和队列中。
             rabbitTemplate.convertAndSend( DirectConfig.EXCHANGE_NAME2,DirectConfig.ROUTING_KEY2,"出现奇数数字:" + i);
         }
    }

}

接收消息


//接受队列消息
@RabbitListener(queues = DirectConfig.QUEUE_NAME)
public void receiveMessage(String msg, Channel channel, Message message, Object object) {
    System.out.println("direct收到的消息msg:"+msg);
    System.out.println("channel:"+channel);
    System.out.println("message:"+message);
    System.out.println("object:"+object);
}

3.topic通配符主题订阅模式

  • config配置

    public  static final String QUEUE_NAME="queue_direct";//通配符队列名字
    
    public  static final String EXCHANGE_NAME1="direct_exchange1";//通配符类型交换机名字
    
    public  static final String EXCHANGE_NAME2="direct_exchange2";//通配符类型交换机名字
    
    public  static final String ROUTING_KEY1="log.key.#";//路由键1
    public  static final String ROUTING_KEY2="error.key.#";//路由键2
    
    @Bean
    public Queue topQueue(){
        return new Queue(QUEUE_NAME,true,false,false);
    }
    
    @Bean
    public TopicExchange topicExchange1(){
        return ExchangeBuilder.directExchange(EXCHANGE_NAME1).durable(true).build();
    }
    
    @Bean
    public TopicExchange topicExchange2(){
        return ExchangeBuilder.directExchange(EXCHANGE_NAME2).durable(true).build();
    }
    
    @Bean
    public Binding bind1(){
        return BindingBuilder.bind(topQueue()).to(topicExchange1()).with(ROUTING_KEY1);
    }
    
    @Bean
    public Binding bind2(){
        return BindingBuilder.bind(topQueue()).to(topicExchange2()).with(ROUTING_KEY2);
    }
    

四、ack机制

            /**第一个参数:当前消息的标记
             * 第二个参数:是否批量进行应答
             * 下面是签收
             */
            Channel.basicAck(envelope.getDeliveryTag(),false);
            //下面也可以拒绝签收
            /**
             * 第三个参数:表示决绝签收之后这个消息是否要重回队列?
             */
            channel.basicNack(envelope.getDeliveryTag(),false,true);
  System.out.println("=====================异常了========================");
  if (message.getMessageProperties().getRedelivered()) {
    System.out.println("================消息已重复处理失败,拒绝再次接收======================" + content);
    // 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列
    channel.basicReject(message.getMessageProperties().getDeliveryTag(), false);
  } else {
    System.out.println("====================消息即将再次返回队列处理=========================" + content);
    // requeue为是否重新回到队列,true重新入队
    channel.basicNack(message.getMessageProperties().getDeliveryTag(), false, true);

五、confirm 和 return

  • 1、confirm机制,消息的确认,是指生产者投递消息之后,如果Broker收到消息,则会给生产者一个应答,生产者能接收应答,用来确定这条消息是否正常的发送到Broker,这种机制是消息可靠性投递的核心保障。confirm机制是只保证消息到达exchange,并不保证消息可以路由到正确的queue。

        RabbitTemplate.ReturnsCallback returnsCallback =new RabbitTemplate.ReturnsCallback(){
            /**
             *  ReturnedMessage 参数(Message message, int replyCode, String replyText, String exchange, String routingKey)
             *  returnedMessage
             *  message消息对象
             *  replyCode 错误码
             *  replyText 错误信息
             *  exchange 交换机名称
             *  routingKey 路由key
             */
            @Override
            public void returnedMessage(ReturnedMessage returnedMessage) {
    
            }
        };
    
  • 2、return机制,用于处理一些不可路由的消息,在一些特殊的情况下,当前的exchange不存在或者指定的路由key路由不到,这时如果我们需要及时监听这种消息,就需要return机制。

        RabbitTemplate.ConfirmCallback callback = new  RabbitTemplate.ConfirmCallback(){
            /**
             *
             * @param correlationData 相关配置信息
             * @param ack 交换机是否成功收到消息
             * @param cause 错误信息
             */
            @Override
            public void confirm(CorrelationData correlationData, boolean ack, String cause) {
                correlationData.getId();
            }
        };
    

六、死信队列

  • 死信队列过程

    生产者 → 消息 → 交换机 → (死信)队列 → 变成死信 → DLX交换机 →队列 → 消费者


    /**
     * 死信交换机
     * @return
     */
    @Bean
    public TopicExchange deadLetterExchange() {
        return new TopicExchange("DLX_EXCHANGE_NAME");
    }

    /**
     * 正常交换机
     * @return
     */
    @Bean
    public TopicExchange demoExchange() {
        return new TopicExchange("EXCHANGE_NAME");
    }

    /**
     * 声明队列
     * @return
     */
    @Bean
    public Queue deadLetterQueue() {
        Map<String, Object> args = new HashMap<>(2);
//       x-dead-letter-exchange    声明  死信交换机
        args.put("x-dead-letter-exchange", "DLX_EXCHANGE_NAME");
//       十秒过期变成死信
        args.put("x-message-ttl",10000);
//       x-dead-letter-routing-key    声明 死信路由键(这里没有声明,后面绑定的)
//        args.put("x-dead-letter-routing-key", "dlx.#");
        return QueueBuilder.durable("QUEUE_NAME").withArguments(args).build();
    }

    /**
     * 死信队列绑定交换机
     * @param deadLetterQueue
     * @param demoExchange
     * @return
     */
    @Bean
    public Binding bindingExchange1(Queue deadLetterQueue,TopicExchange demoExchange){
        return BindingBuilder.bind(deadLetterQueue).to(demoExchange).with("dlx.#");
    }


    /**
     * 替补队列
     * @return
     */
    @Bean
    public Queue redirectQueue() {
        return new Queue("REDIRECT_QUEUE_NAME");
    }

    /**
     * 死信交换机和替补队列绑定
     * @param redirectQueue
     * @param deadLetterExchange
     * @return
     */
    @Bean
    public Binding bindingExchange2(Queue redirectQueue,TopicExchange deadLetterExchange){
        return BindingBuilder.bind(redirectQueue).to(deadLetterExchange).with("#");
    }

附:

/**
 * Construct a new queue, given a name, durability flag, and auto-delete flag, and arguments.
 * @param name the name of the queue - must not be null; set to "" to have the broker generate the name.
 * @param durable true if we are declaring a durable queue (the queue will survive a server restart)
 * @param exclusive true if we are declaring an exclusive queue (the queue will only be used by the declarer's
 * connection)
 * @param autoDelete true if the server should delete the queue when it is no longer in use
 * @param arguments the arguments used to declare the queue
 */
public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete, Map<String, Object> arguments) {
   Assert.notNull(name, "'name' cannot be null");
   this.name = name;
   this.actualName = StringUtils.hasText(name) ? name
         : (Base64UrlNamingStrategy.DEFAULT.generateName() + "_awaiting_declaration");
   this.durable = durable;
   this.exclusive = exclusive;
   this.autoDelete = autoDelete;
   this.arguments = arguments != null ? arguments : new HashMap<>();
}

参数介绍:
1、name: 队列的名称;
2、actualName: 队列的真实名称,默认用name参数,如果name为空,则根据规则生成一个;
3、durable: 是否持久化;
4、exclusive: 是否独享、排外的;
5、autoDelete: 是否自动删除;
6、arguments:队列的其他属性参数,有如下可选项,可参看图2的arguments:
(1)x-message-ttl:消息的过期时间,单位:毫秒;
(2)x-expires:队列过期时间,队列在多长时间未被访问将被删除,单位:毫秒;
(3)x-max-length:队列最大长度,超过该最大值,则将从队列头部开始删除消息;
(4)x-max-length-bytes:队列消息内容占用最大空间,受限于内存大小,超过该阈值则从队列头部开始删除消息;
(5)x-overflow:设置队列溢出行为。这决定了当达到队列的最大长度时消息会发生什么。有效值是drop-head、reject-publish或reject-publish-dlx。仲裁队列类型仅支持drop-head;
(6)x-dead-letter-exchange:死信交换器名称,过期或被删除(因队列长度超长或因空间超出阈值)的消息可指定发送到该交换器中;
(7)x-dead-letter-routing-key:死信消息路由键,在消息发送到死信交换器时会使用该路由键,如果不设置,则使用消息的原来的路由键值
(8)x-single-active-consumer:表示队列是否是单一活动消费者,true时,注册的消费组内只有一个消费者消费消息,其他被忽略,false时消息循环分发给所有消费者(默认false)
(9)x-max-priority:队列要支持的最大优先级数;如果未设置,队列将不支持消息优先级;
(10)x-queue-mode(Lazy mode):将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;如果未设置,队列将保留内存缓存以尽可能快地传递消息;
(11)x-queue-master-locator:在集群模式下设置镜像队列的主节点信息。

过期或被删除(因队列长度超长或因空间超出阈值)的消息可指定发送到该交换器中;
(7)x-dead-letter-routing-key:死信消息路由键,在消息发送到死信交换器时会使用该路由键,如果不设置,则使用消息的原来的路由键值
(8)x-single-active-consumer:表示队列是否是单一活动消费者,true时,注册的消费组内只有一个消费者消费消息,其他被忽略,false时消息循环分发给所有消费者(默认false)
(9)x-max-priority:队列要支持的最大优先级数;如果未设置,队列将不支持消息优先级;
(10)x-queue-mode(Lazy mode):将队列设置为延迟模式,在磁盘上保留尽可能多的消息,以减少RAM的使用;如果未设置,队列将保留内存缓存以尽可能快地传递消息;
(11)x-queue-master-locator:在集群模式下设置镜像队列的主节点信息。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring Boot集成RabbitMQ可以通过以下步骤完成: 1. 添加Maven依赖:在pom.xml文件中添加RabbitMQ的Spring Boot Starter依赖。 ```xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` 2. 配置RabbitMQ连接信息:在application.properties(或application.yml)文件中配置RabbitMQ的连接信息。 ```properties spring.rabbitmq.host=your_rabbitmq_host spring.rabbitmq.port=your_rabbitmq_port spring.rabbitmq.username=your_rabbitmq_username spring.rabbitmq.password=your_rabbitmq_password ``` 3. 创建RabbitMQ发送者:创建一个发送消息的类,使用`RabbitTemplate`发送消息到指定的交换机和队列。 ```java import org.springframework.amqp.core.RabbitTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class RabbitMQSender { @Autowired private RabbitTemplate rabbitTemplate; public void sendMessage(String exchange, String routingKey, Object message) { rabbitTemplate.convertAndSend(exchange, routingKey, message); } } ``` 4. 创建RabbitMQ接收者:创建一个接收消息的类,使用`@RabbitListener`注解监听指定的队列,处理接收到的消息。 ```java import org.springframework.amqp.rabbit.annotation.RabbitListener; import org.springframework.stereotype.Component; @Component public class RabbitMQReceiver { @RabbitListener(queues = "your_queue_name") public void receiveMessage(Object message) { // 处理接收到的消息 System.out.println("Received message: " + message.toString()); } } ``` 5. 发送和接收消息:在需要发送或接收消息的地方调用对应的方法。 ```java @Autowired private RabbitMQSender rabbitMQSender; public void sendMessage() { rabbitMQSender.sendMessage("your_exchange_name", "your_routing_key", "Hello, RabbitMQ!"); } ``` 以上是基本的使用方式,你可以根据实际需求进行扩展和配置。注意,你还需要安装并启动RabbitMQ服务。 希望对你有所帮助!如果有任何疑问,请随时提问。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值