RabbitMQ学习笔记

一、一些概念

1. 消息队列的作用

  • 解耦
  • 异步
  • 削峰

2.使用了什么协议

  • 常见的消息中间件协议:OpenWire、AMQP、MQTT、Kafka、OpenMessage协议
  • 基于TCP/IP协议之上的协议
  • 为什么不用HTTP协议:
    • 因为http的请求报文头和响应报文头是比较复杂的,包含了cookie、数据加解密、状态码、响应码等附加功能,对于一个消息而言并不需要这么复杂,它就只是负责数据的传递、存储、分发,追求的是高性能,应尽量简洁和快速
    • 大部分情况下http都是短链接,在实际的交互过程重,一个请求到响应的过程很有可能中断,中断以后就不会进行持久化,就会造成请求的丢失,这就不利于消息中间件的应用场景,因为消息中间件肯是一个长期的获取消息的过程,出现故障和问题要对消息进行持久化等,目的是为了消息和数据的高可靠和稳健运行

3. 消息持久化

ActiveMQRabbitMQKafkaRockMQ
文件存储支持支持支持支持
数据库支持///

4. 分发策略

在这里插入图片描述

  • 发布订阅:有订阅的都能收到消息
  • 轮询分发:平均分发
  • 公平分发:能者多劳,谁先干完先给谁发
  • 重发:消息无应答后重发给其他消费者

二、RabbitMQ入门及安装

1. rpm安装

  • 下载安装erlang环境
  • yum install -y socat
  • 下载安装RabbitMQ
  • rabbitmq-plugins enable management 开启前端管理页面默认端口15672,账号密码 guest guest 只能在本机访问
    • 新增用户命令: rabbitmqctl add_user admin admin
    • 分配权限:rabbitmqctl set_user_tags admin administrator
      • administrator:最高级权限可以登录控制台,查看所有信息,管理RabbitMQ
      • monitoring:监控者,可以登录查看所有信息
      • policymaker:策略制定者,登录控制台配置策略
      • management:普通管理员,登录控制台
    • 资源权限授权:rabbitmqctl set_permission -p admin “." ".” “.*”

2. Docker安装

docker run -di --name myrabbit -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=user123 -p 15672:15672 -p 5672:5672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management

在这里插入图片描述

3. Demo

  • 创建一个Maven项目
  • 引入依赖
    <dependencies>
        <!-- https://mvnrepository.com/artifact/com.rabbitmq/amqp-client -->
        <dependency>
            <groupId>com.rabbitmq</groupId>
            <artifactId>amqp-client</artifactId>
            <version>5.10.0</version>
        </dependency>

    </dependencies>
  • Producer示例代码
package com.ambition.rabbitmq.simple;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;



public class Producer {
    public static void main(String[] args) {
//        1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("你的IP");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("你设置的用户");
        connectionFactory.setPassword("你设置的密码");
        Connection connection = null;
        Channel channel = null;
        try {
//            2. 创建连接
            connection = connectionFactory.newConnection("生产者");
//            3. 通过连接获取channel
            channel = connection.createChannel();
//            4. 声明队列
            String queueName = "queue3";
            /*
            * @params1 队列名称
            * @params2 是否持久化,随着服务器重启,队列是否还存在,非持久化队列里的消息依旧会存盘,但是随着服务重启消息也会丢失
            * @params3 排他性,是否独占队列
            * @params4 是否自动删除,当队列里最后一个消息被消费后是否删除队列
            * @params5 携带参数
            * */
            channel.queueDeclare(queueName,false,false,false,null);
//            5. 准备消息
            String msg = "Hello Wrold!";
//            6. 发送消息
            channel.basicPublish("", queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
//            7. 关闭通道
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

  • Consumer示例代码
package com.ambition.rabbitmq.simple;

import com.rabbitmq.client.*;

import java.io.IOException;
import java.util.concurrent.TimeoutException;



public class Consumer {
    public static void main(String[] args) {
//        1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost("你的IP");
        connectionFactory.setPort(5672);
        connectionFactory.setUsername("你设置的用户");
        connectionFactory.setPassword("你设置的密码");
        Connection connection = null;
        Channel channel = null;
        try {
//            2. 创建连接
            connection = connectionFactory.newConnection("消费者");
//            3. 通过连接获取channel
            channel = connection.createChannel();
//            4. 声明队列
            String queueName = "queue3";
            channel.basicConsume(queueName, true, new DeliverCallback() {
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("收到的消息是:" + new String(delivery.getBody(), "UTF-8"));
                }
            }, new CancelCallback() {
                public void handle(String s) throws IOException {
                    System.out.println("接收消息失败了……");
                }
            });
            System.out.println("开始接收消息");
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
//            7. 关闭通道
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

  • 消息一定是先到exchange(交换机),exchange再根据路由键投递给对应的queue,而不是直接塞到queue,queue没有指定exchange就会绑定到默认的那一个Default exchange

4. RabbitMQ 支持消息的模式

在这里插入图片描述
在这里插入图片描述

  • Publish/Subscribe:消息发送绑定本exchange的所有queue
    在这里插入图片描述
package com.ambition.rabbitmq.routing;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) {
//        1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
//        2. 设置连接属性
        connectionFactory.setHost("你的IP");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("你设置的用户");
        connectionFactory.setPassword("你设置的密码");
        Connection connection = null;
        Channel channel = null;
        try {
//            3. 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
//            4. 通过连接获取channel
            channel = connection.createChannel();
//            5. 准备消息
            String msg = "Hello Wrold!";
//            6. 准备交换机
            String exchangeName = "fanout-exchange";
//            7. 定义routing key
            String routingKey = "";
//            8. 指定交换机类型
            String type = "fanout";

//            6. 发送消息
            channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
//            7. 关闭通道
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

  • Routing:消息发送绑定本exchange的并且指定的RoutingKey的queue
    在这里插入图片描述
package com.ambition.rabbitmq.routing;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.MessageProperties;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

public class Producer {
    public static void main(String[] args) {
//        1.创建连接工厂
        ConnectionFactory connectionFactory = new ConnectionFactory();
//        2. 设置连接属性
        connectionFactory.setHost("你的IP");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("你设置的用户");
        connectionFactory.setPassword("你设置的密码");
        Connection connection = null;
        Channel channel = null;
        try {
//            3. 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
//            4. 通过连接获取channel
            channel = connection.createChannel();
//            5. 准备消息
            String msg = "Hello Direct!";
//            6. 准备交换机
            String exchangeName = "direct-exchange";
//            7. 定义routing key
            String routingKey = "email";
//            8. 指定交换机类型
            String type = "direct";

//            6. 发送消息
            channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TimeoutException e) {
            e.printStackTrace();
        } finally {
//            7. 关闭通道
            if (null != channel && channel.isOpen()) {
                try {
                    channel.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (TimeoutException e) {
                    e.printStackTrace();
                }
            }
            if (null != connection && connection.isOpen()) {
                try {
                    connection.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
    }
}

  • Topics:模糊匹配
    • #.:可以没有也可以有一个或多个
    • *.:只能有且必须有一个
      在这里插入图片描述
//            3. 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
//            4. 通过连接获取channel
            channel = connection.createChannel();
//            5. 准备消息
            String msg = "Hello Topic!";
//            6. 准备交换机
            String exchangeName = "topics-exchange";
//            7. 定义routing key
            String routingKey = "com.course.order";
//            8. 指定交换机类型
            String type = "topic";

//            6. 发送消息
            channel.basicPublish(exchangeName, routingKey, null, msg.getBytes());
  • 代码创建交换机、队列,并绑定关系
//            3. 从连接工厂中获取连接
            connection = connectionFactory.newConnection("生产者");
//            4. 通过连接获取channel
            channel = connection.createChannel();
//            5. 准备消息
            String msg = "Hello direct!";
//            6. 准备交换机
            String exchangeName = "direct_message_exchange";
            String exchangeType = "direct";
//            交换机可以在管理页面进行创建和绑定,也可以通过代码创建
//            param3是否持久化,指的是交换机会不会随着服务器重启而丢失
            channel.exchangeDeclare(exchangeName,exchangeType,true);

//            7. 声明队列
            channel.queueDeclare("queue5",true,false,false,null);
            channel.queueDeclare("queue6",true,false,false,null);
            channel.queueDeclare("queue7",true,false,false,null);
//            8. 绑定交换机和队列
            channel.queueBind("queue5",exchangeName,"order");
            channel.queueBind("queue6",exchangeName,"order");
            channel.queueBind("queue7",exchangeName,"course");

//            9. 发送消息
            channel.basicPublish(exchangeName, "order", null, msg.getBytes());
  • Work queues
    • 默认轮询:例如代码Producer向一个队列发送20条消息,同时有Work1和Work2两个消费者,默认情况下,MQ会平均地向Work1和Work2推送消息,Work1发一条->Work2发一条 如此轮询
    • 公平分发:在代码设置手动应答的情况下,假如设置Qos=1,那么就是MQ推送给Work1一条消息,Work1消费完应答后立马获取下一条消息,Work2同理,但是当Work1处理消息速递比Work2快时,此时就不再是轮询地把1357给Work1而2468给Work2,而是Work1处理完立马推送下一条消息就可能是134567
//生产者发送20条消息
//            5. 准备消息
            for (int i = 0; i < 20; i++) {
                String msg = "Hello ," + i;
//            6. 发送消息
                channel.basicPublish("", queueName, MessageProperties.PERSISTENT_TEXT_PLAIN, msg.getBytes());
            }
// 默认情况下就是轮询分发
//            4. 声明队列
            String queueName = "queue";
//            Qos ,每次取多少条消息
            finalChannel.basicQos(1);
//            param2 是否自动应答
            finalChannel.basicConsume(queueName, false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("Work1收到的消息是:" + new String(delivery.getBody(), "UTF-8"));
                    try {
                        Thread.sleep(200);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                }
开始接收消息
Worker1收到的消息是:Hello ,0
Worker1收到的消息是:Hello ,2
Worker1收到的消息是:Hello ,4
Worker1收到的消息是:Hello ,6
Worker1收到的消息是:Hello ,8
Worker1收到的消息是:Hello ,10
Worker1收到的消息是:Hello ,12
Worker1收到的消息是:Hello ,14
Worker1收到的消息是:Hello ,16
Worker1收到的消息是:Hello ,18

开始接收消息
Worker2收到的消息是:Hello ,1
Worker2收到的消息是:Hello ,3
Worker2收到的消息是:Hello ,5
Worker2收到的消息是:Hello ,7
Worker2收到的消息是:Hello ,9
Worker2收到的消息是:Hello ,11
Worker2收到的消息是:Hello ,13
Worker2收到的消息是:Hello ,15
Worker2收到的消息是:Hello ,17
Worker2收到的消息是:Hello ,19

// 使用公平调度,手动应答,设置basicQos mq每次分配消息的个数,可以根据服务器性能情况等因素灵活设置
//            4. 声明队列
            String queueName = "queue";
            finalChannel.basicQos(1);
//            param2 是否自动应答
            finalChannel.basicConsume(queueName, false, new DeliverCallback() {
                @Override
                public void handle(String s, Delivery delivery) throws IOException {
                    System.out.println("Work1收到的消息是:" + new String(delivery.getBody(), "UTF-8"));
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    finalChannel.basicAck(delivery.getEnvelope().getDeliveryTag(),false);
                }
开始接收消息
Work1收到的消息是:Hello ,0
Work1收到的消息是:Hello ,6
Work1收到的消息是:Hello ,11
Work1收到的消息是:Hello ,17

开始接收消息
Work2收到的消息是:Hello ,1
Work2收到的消息是:Hello ,2
Work2收到的消息是:Hello ,3
Work2收到的消息是:Hello ,4
Work2收到的消息是:Hello ,5
Work2收到的消息是:Hello ,7
Work2收到的消息是:Hello ,8
Work2收到的消息是:Hello ,9
Work2收到的消息是:Hello ,10
Work2收到的消息是:Hello ,12
Work2收到的消息是:Hello ,13
Work2收到的消息是:Hello ,14
Work2收到的消息是:Hello ,15
Work2收到的消息是:Hello ,16
Work2收到的消息是:Hello ,18
Work2收到的消息是:Hello ,19

三、Springboot 整合RabbitMQ

1. 创建一个spring boot工程,包依赖引入

	<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>

2. 配置rabbitmq连接信息

server:
  port: 8081
spring:
  rabbitmq:
    username: 你的用户名
    password: 你的密码
    host: 你的服务器地址
    port: 5672
    virtual-host: /

3. fanout类型

(1)配置交换机、队列、绑定关系

package cn.ambition.rabbbitmq.producer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.FanoutExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RabbitMQConfiguration {
    /**
     * 创建交换机,通过使用对应的类 指定交换机类型
     * @return
     */
    @Bean
    public FanoutExchange fanoutExchange() {
        return new FanoutExchange("fanout_order_exchange",true,false);
    }

    /**
     * 创建队列
     * @return
     */
    @Bean
    public Queue smsQueue() {
        return new Queue("sms.fanout.queue",true);
    }

    @Bean
    public Queue emailQueue() {
        return new Queue("email.fanout.queue",true);
    }

    @Bean
    public Queue wechatQueue() {
        return new Queue("wechat.fanout.queue",true);
    }

    /**
     * 创建绑定关系
     * @param fanoutExchange
     * @param smsQueue
     * @return
     */
    @Bean
    public Binding smsBinding(FanoutExchange fanoutExchange, Queue smsQueue) {
        return BindingBuilder.bind(smsQueue).to(fanoutExchange);
    }

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

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

(2)发送消息

package cn.ambition.rabbbitmq.producer.service;

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.UUID;

@Service
public class OrderService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void makeOrder(String userId, String productId, Integer num) {
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功:" + orderId);

        String exchangeName = "fanout_order_exchange";
        String routingKey = "";
        rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId);

    }
}

(3)消费消息

  • 使用@RabbitListener(queues = {""})指定要消费的队列
  • 使用@RabbitHandler注解指定处理消息的方法
package cn.ambition.rabbbitmq.consumer.service.fanout;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = {"email.fanout.queue"})
public class FanoutEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String msg) {
        System.out.println("FanoutEmailConsumer 收到消息:" + msg);
    }
}

3. direct类型

(1)配置交换机、队列、绑定关系

package cn.ambition.rabbbitmq.producer.config;

import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;


@Configuration
public class DirectRabbitMQConfiguration {
    /**
     * 创建交换机,通过使用对应的类 指定交换机类型
     * @return
     */
    @Bean
    public DirectExchange directExchange() {
        return new DirectExchange("direct_order_exchange",true,false);
    }

    /**
     * 创建队列
     * @return
     */
    @Bean
    public Queue directSmsQueue() {
        return new Queue("sms.direct.queue",true);
    }

    @Bean
    public Queue directEmailQueue() {
        return new Queue("email.direct.queue",true);
    }

    @Bean
    public Queue directWechatQueue() {
        return new Queue("wechat.direct.queue",true);
    }

    /**
     * 创建绑定关系
     * @param directExchange
     * @param directSmsQueue
     * @return
     */
    @Bean
    public Binding directSmsBinding(DirectExchange directExchange, Queue directSmsQueue) {
        return BindingBuilder.bind(directSmsQueue).to(directExchange).with("sms");
    }

    @Bean
    public Binding directEmailBinding(DirectExchange directExchange, Queue directEmailQueue) {
        return BindingBuilder.bind(directEmailQueue).to(directExchange).with("email");
    }

    @Bean
    public Binding directWechatBinding(DirectExchange directExchange, Queue directWechatQueue) {
        return BindingBuilder.bind(directWechatQueue).to(directExchange).with("wechat");
    }
}

(2)发送消息

    public void makeOrderDirect(String userId, String productId, Integer num) {
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功:" + orderId);

        String exchangeName = "direct_order_exchange";
        String routingKey = "";
        rabbitTemplate.convertAndSend(exchangeName,"sms",orderId);
        rabbitTemplate.convertAndSend(exchangeName,"wechat",orderId);

    }

(3)消费消息

package cn.ambition.rabbbitmq.consumer.service.direct;

import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(queues = {"email.direct.queue"})
public class DirectEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String msg) {
        System.out.println("DirectEmailConsumer 收到消息:" + msg);
    }
}

4. topic类型,使用注解创建绑定交换机队列

package cn.ambition.rabbbitmq.consumer.topic;

import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;

@Component
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = "email.topic.queue",durable = "true",autoDelete = "false"),
        exchange = @Exchange(value = "topic_order_exchange",type = ExchangeTypes.TOPIC),
        key = "#.email.#"))
public class TopicEmailConsumer {
    @RabbitHandler
    public void receiveMessage(String msg) {
        System.out.println("TopicEmailConsumer 收到消息:" + msg);
    }
}

四、RabbitMQ高级

1. 设置消息过期时间

(1)在queue的参数列表中设置,针对所有进队列的消息

在这里插入图片描述

    @Bean
    public Queue ttlQueue() {
        Map<String,Object> params = new HashMap<>();
        params.put("x-message-ttl",5000);//单位是毫秒
        return new Queue("ttl.direct.queue",true,false,false,params);
    }

(2)在消息发送时设置过期时间

    public void makeTtlMsg(String userId, String productId, Integer num) {
        String orderId = UUID.randomUUID().toString();
        System.out.println("订单生成成功:" + orderId);

        String exchangeName = "ttl_direct_exchange";
        String routingKey = "ttl-msg";
        MessagePostProcessor messagePostProcessor = new MessagePostProcessor() {
            @Override
            public Message postProcessMessage(Message message) throws AmqpException {
                message.getMessageProperties().setExpiration("5000");
                message.getMessageProperties().setContentEncoding("UTF-8");
                return message;
            }
        };
        rabbitTemplate.convertAndSend(exchangeName,routingKey,orderId,messagePostProcessor);

    }

当上述两种方法同时存在时,即同时设置了队列的过期时间又设置了消息的过期时间,此时以最短的时间为准

2. 死信队列

DLX,全程dead-letter-exchange,可以称之为死信交换机,当消息在一个队列中变成死信之后,他能被重新发送到另一个交换机中,这个交换机就是DLX,绑定DLX的队列就是死信队列。
消息变成死信,可能有一下几个原因:

  • 消息被拒绝
  • 消息过期
  • 队列达到最大长度
  • 创建一个死信队列
package cn.ambition.rabbbitmq.producer.config;

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DeadRabbitMqConfiguration {

    @Bean
    public DirectExchange deadExchange() {
        return new DirectExchange("dead_direct_exchange",true,false);
    }

    @Bean
    public Queue deadQueue() {
        return new Queue("dead.direct.queue",true);
    }

    @Bean
    public Binding deadBinding(DirectExchange deadExchange, Queue deadQueue) {
        return BindingBuilder.bind(deadQueue).to(deadExchange).with("dead");
    }
}

  • 常规队列绑定死信交换机
    @Bean
    public Queue ttlQueue() {
        Map<String,Object> params = new HashMap<>();
        params.put("x-message-ttl",5000);
        // 绑定死信交换机
        params.put("x-dead-letter-exchange","dead_direct_exchange");
        //fanout不用配
        params.put("x-dead-letter-routing-key","dead");
        return new Queue("ttl.direct.queue",true,false,false,params);
    }

发现消息进入队列 ttl.direct.queue 5s后消息过期,被重新发送到了 dead.direct.queue
在这里插入图片描述

3. RabbitMQ的内存控制

当内存使用率达到一定的阈值时报警,并且不再接收新的消息

3.1 内存预警(二者选其一,命令的方式:重启后失效,配置文件:一直有效)

  • 设置相对值:默认值是 0.4,建议一般配置为0.6,可以0.4-0.7之间
rabbitmqctl set_vm_memory_high_watermark <fraction>
  • 设置绝对值
rabbitmqctl set_vm_memory_high_watermark absolute 2GB

3.2 磁盘预警:当服务器磁盘空间小于指定数值时磁盘预警

rabbitmqctl set_disk_free_limit 50MB

在这里插入图片描述

3.3 内存换页

在某个broker节点内存阻塞生产者之前,他会尝试将内存中的消息换页到磁盘中以释放内存空间,持久化和非持久化的消息都会写入磁盘中,其中持久化的消息在磁盘中就有一个副本,所以在转移的过程中持久化的消息会优先从内存中清除掉。

例如:服务器内存1000MB,当内存使用了400MB达到了预警线,但是因为配置了内存换页 0.5,这时会在达到400MB极限之前,把内存中的200MB内容转移到磁盘中,从而达到稳健运行的目的

vm_memory_high_watermark_paging_ratio = 0.5 # 设置值肯定小于1

五、RabbitMQ集群

1. Docker搭建RabbitMQ集群

默认模式下主节点挂掉,所有节点都会强制下线无法使用

  • 创建节点rabbit-1,因为在一台服务器启动了两个节点,因此使用--net mynet加入自定义的docker网络
docker run -d --name rabbit-1 -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=user123 -e RABBITMQ_ERLANG_COOKIE='rabbit_mq' -p 15672:15672 -p 5672:5672 --net mynet rabbitmq:management
  • 创建节点rabbit-2
docker run -d --name rabbit-2 -e RABBITMQ_DEFAULT_USER=user -e RABBITMQ_DEFAULT_PASS=user123 -e RABBITMQ_ERLANG_COOKIE='rabbit_mq' -p 15673:15672 -p 5673:5672 --net mynet rabbitmq:management
  • 进入rabbit-1
docker exec -it rabbit-1 /bin/bash
# 停止服务
rabbitmqctl stop_app
# 清除历史数据,如果不清楚可能会无法加入集群
rabbitmqctl reset
# 重新启动
rabbitmqctl start_app
  • 进入rabbit-2
docker exec -it rabbit-2 /bin/bash
# 停止服务
rabbitmqctl stop_app
# 清除历史数据,如果不清楚可能会无法加入集群
rabbitmqctl reset
# 将rabbit-2 加入 rabbit-1(主节点)的集群中,-n 指定节点 节点名称@主机名称
rabbitmqctl -n rabbit@3d7211e507ee join_cluster rabbit@4907b0882674
# 启动服务
rabbitmqctl start_app
# 查看集群节点信息
rabbitmqctl cluster_status -n rabbit

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值