RabbitMQ中间件部署及使用

 

1. RabbitMQ的部署

1.1 CentOS7.x下的直接部署
  • 下载RabbitMQ安装包[rabbitmq-server-3.8.14-1.el7.noarch.rpm]:https://wwe.lanzous.com/ibMV4n0uj8f
  • 下载Erlang安装包[erlang-solutions-2.0-1.noarch.rpm]:https://wwe.lanzous.com/iZRumn0uj9g
  • 将安装包上传到CentOS中
  • 安装
# 1. 安装erlang环境
rpm -Uvh erlang-solutions-2.0-1.noarch.rpm
yum install -y erlang
# 2. 安装socat插件
yum install -y socat
# 3. 安装rabbitMQ
rpm -Uvh rabbitmq-server-3.8.14-1.el7.noarch.rpm
yum install -y rabbitmq-server
  • 启动与关闭
# 启动rabbitMQ
systemctl start rabbitmq-server
# 查看rabbitMQ状态
systemctl status rabbitmq-server
# 停止rabbitMQ
systemctl stop rabbitmq-server
# 开机启动rabbitMQ
systemctl enable rabbitmq-server
  • 打开web管理
# 开启web管理插件
rabbitmq-plugins enable rabbitmq_management
# 重启rabbitMQ服务
systemctl restart rabbitmq-server
  • 用户添加与设置
# 添加用户(admin admin为用户账号和密码)
rabbitmqctl add_user admin admin
# 设置用户为管理员
rabbitmqctl set_user_tags admin administrator
# 为用户添加资源权限
rabbitmqctl set_permissions -p / admin ".*" ".*" ".*"

1.2 CentOS7.x下通过Docker的安装部署
  • 安装Docker
# 1. 将yum包更新到最新
yum update
# 2. 安装依赖
yum install -y yum-utils device-mapper-persistent-data lvm2
# 3. 设置yum源为阿里云
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# 4. 安装docker
yum install -y docker-ce
# 5. 安装加速镜像
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
 "registry-mirrors":["https://hub-mirror.c.163.com/"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker
  • Docker的启动和关闭
# 启动docker
systemctl start docker
# 关闭docker
systemctl stop docker
  • 通过Docker安装rabbitMQ(包含用户与web管理)
docker run -di --name rabbitmq -e RABBITMQ_DEFAULT_USER=admin -e RABBITMQ_DEFAULT_PASS=admin -p 5672:5672 -p 15672:15672 -p 25672:25672 -p 61613:61613 -p 1883:1883 rabbitmq:management

1.3 RabbitMQ的web管理访问
  • 阿里云、腾讯云等的服务器需通过安全组打开15672、5672等端口
  • 其他服务器需通过防火墙打开15672、5672等端口
# 开放15672和5672端口
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --zone=public --add-port=5672/tcp --permanent
# 配置立即生效
firewall-cmd --reload

# 其他相关命令
# 关闭15672和5672端口
firewall-cmd --zone=public --remove-port=15672/tcp --permanent
firewall-cmd --zone=public --remove-port=5672/tcp --permanent
# 查看防火墙所有开放的端口
firewall-cmd --zone=public --list-ports
  • 输入http://ip:15672/访问web管理

 


 

2. RabbitMQ的使用

2.1 SpringBoot整合RabbitMQ
  • 新建Spring项目
  • pom.xml依赖
<dependencies>

    <!--RabbitMQ启动依赖-->
    <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>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.amqp</groupId>
        <artifactId>spring-rabbit-test</artifactId>
        <scope>test</scope>
    </dependency>

</dependencies>
  • application.yml配置
# 服务器端口
server:
  port: 8080

# RabbitMQ服务配置
spring:
  rabbitmq:
    username: admin
    password: admin
    virtual-host: /
    host: 192.168.150.128
    port: 5672
  • RabbitMQConfiguration消息队列配置类
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;

/**
 * RabbitMQ配置类
 */
@Configuration
public class RabbitMQConfiguration {

    /**
     * 声明交换机(类型有DirectExchange、FanoutExchange、TopicExchange、HeadersExchange)
     */
    @Bean
    public DirectExchange directExchange() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("direct_exchange_01", true, false);
    }

    /**
     * 声明队列1
     */
    @Bean
    public Queue queue1() {
        /*
            @param1 队列的名称
            @param2 队列是否持久化
         */
        return new Queue("01.queue", true);
    }

    /**
     * 声明队列2
     */
    @Bean
    public Queue queue2() {
        return new Queue("02.queue", true);
    }

    /**
     * 声明队列3
     */
    @Bean
    public Queue queue3() {
        return new Queue("03.queue", true);
    }

    /**
     * 队列1与交换机绑定
     */
    @Bean
    public Binding queue1Binding() {
        return BindingBuilder
                .bind(queue1())         // 要进行绑定的队列
                .to(directExchange())   // 队列绑定的交换机
                .with("1");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

    /**
     * 队列2与交换机绑定
     */
    @Bean
    public Binding queue2Binding() {
        return BindingBuilder
                .bind(queue2())
                .to(directExchange())
                .with("2");
    }

    /**
     * 队列3与交换机绑定
     */
    @Bean
    public Binding queue3Binding() {
        return BindingBuilder
                .bind(queue3())
                .to(directExchange())
                .with("3");
    }

}

2.2 简单使用案例
  • 生产者
  • Service接口
public interface ProducerService {
    void sendMessage(String routingKey, String message);
}
  • Service实现类
import com.mo.rabbitmqproducer.service.ProducerService;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class ProducerServiceImpl implements ProducerService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Override
    public void sendMessage(String routingKey, String message) {
        // 交换机名称
        String exchangeName = "direct_exchange_01";
        // 发给消息队列
        rabbitTemplate.convertAndSend(exchangeName, routingKey, message);
        System.out.println("发送消息成功");
    }

}
  • 消费者
  • Service接口
public interface ConsumerService {
    void receiveMessage(String message);
}
  • Service实现类
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"01.queue"})
public class Consumer01ServiceImpl implements ConsumerService {

    @Override
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("消费者01接收到消息:" + message);
    }

}
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"02.queue"})
public class Consumer02ServiceImpl implements ConsumerService {

    @Override
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("消费者02接收到消息:" + message);
    }

}
import com.mo.rabbitmqconsumer.service.ConsumerService;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;

@Service
@RabbitListener(queues = {"03.queue"})
public class Consumer03ServiceImpl implements ConsumerService {

    @Override
    @RabbitHandler
    public void receiveMessage(String message) {
        System.out.println("消费者03接收到消息:" + message);
    }

}
  • 测试
import com.mo.rabbitmqproducer.service.ProducerService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class RabbitmqProducerApplicationTests {

    @Autowired
    private ProducerService producerService;

    @Test
    void contextLoads() {
        producerService.sendMessage("1", "Hello 01!");
        producerService.sendMessage("2", "Hello 02!");
        producerService.sendMessage("3", "Hello 03!");
    }

}

2.3 TTL消息过期
  • 可设置队列的过期时间让队列中的消息过期后删除或转移
  • RabbitMQConfiguration消息队列配置类
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;

import java.util.HashMap;
import java.util.Map;

/**
 * RabbitMQ配置类
 */
@Configuration
public class RabbitMQConfiguration2 {

    /**
     * 声明交换机
     */
    @Bean
    public DirectExchange ttlDirectExchange() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("ttl_direct_exchange", true, false);
    }

    /**
     * 声明过期队列
     */
    @Bean
    public Queue ttlQueue() {
        // 设置过期时间
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 5000);    // 过期时间为5000毫秒
        /*
            @param1 队列的名称
            @param2 队列是否持久化
            @param3 队列的排他性
            @param4 队列是否自动删除
            @param5 队列属性
         */
        return new Queue("ttl.direct.queue", true, false, false, args);
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding ttlQueueBinding() {
        return BindingBuilder
                .bind(ttlQueue())         // 要进行绑定的队列
                .to(ttlDirectExchange())   // 队列绑定的交换机
                .with("ttl");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

}
  • 可设置消息的过期时间让消息到队列中过期后删除
  • 设置消息过期的方法
public void ttlMessage(String routingKey, String message) {
    // 交换机名称
    String exchangeName = "direct_exchange_01";
    // 设置消息过期时间
    MessagePostProcessor messagePostProcessor = msg -> {
        msg.getMessageProperties().setExpiration("5000");   // 设置过期时间为5000毫秒
        return null;
    };
    // 发给消息队列
    rabbitTemplate.convertAndSend(exchangeName, routingKey, message, messagePostProcessor);
    System.out.println("发送消息成功");
}
  • 同时设置了队列的过期时间和消息的过期时间时,以最短时间为准

2.4 死信队列
  • 死信队列用于消息被拒绝、消息过期、队列已达最大长度时消息的接收
  • RabbitMQConfiguration消息队列配置类
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;

import java.util.HashMap;
import java.util.Map;

/**
 * RabbitMQ配置类
 */
@Configuration
public class RabbitMQConfiguration3 {

    /**
     * 声明死信交换机
     */
    @Bean
    public DirectExchange deadDirectExchange() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("dead_direct_exchange", true, false);
    }

    /**
     * 声明死信队列
     */
    @Bean
    public Queue deadQueue() {
        return new Queue("ttl.direct.queue", true);
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding deadQueueBinding() {
        return BindingBuilder
                .bind(deadQueue())         // 要进行绑定的队列
                .to(deadDirectExchange())   // 队列绑定的交换机
                .with("dead");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

    /**
     * 声明过期交换机
     */
    @Bean
    public DirectExchange ttlDirectExchange2() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("ttl_direct_exchange2", true, false);
    }

    /**
     * 声明过期队列
     */
    @Bean
    public Queue ttlQueue2() {
        // 设置过期时间
        Map<String, Object> args = new HashMap<>();
        args.put("x-message-ttl", 5000);    // 过期时间为5000毫秒
        args.put("x-max-length", 10);   // 消息队列的最大长度
        args.put("x-dead-letter-exchange", "dead_direct_exchange"); // 设置死信交换机
        args.put("x-dead-letter-routing-key", "dead");  //  设置路由Key(Fanout模式交换机不需要设置)
        /*
            @param1 队列的名称
            @param2 队列是否持久化
            @param3 队列的排他性
            @param4 队列是否自动删除
            @param5 队列属性
         */
        return new Queue("ttl2.direct.queue", true, false, false, args);
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding ttlQueueBinding2() {
        return BindingBuilder
                .bind(ttlQueue2())         // 要进行绑定的队列
                .to(ttlDirectExchange2())   // 队列绑定的交换机
                .with("ttl2");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

}

2.5 内存磁盘的预警管理控制
  • 内存的预警管理控制
  • 命令行的方式(重启后失效)
# 相对大小,默认方式,默认值为0.4,建议设置范围为0.4~0.7
rabbitmqctl set_vm_memory_high_watermark relative 0.5
# 绝对大小
rabbitmqctl set_vm_memory_high_watermark absolute 100MB
  • 配置文件方式(重启后不失效)
vim /etc/rabbitmq/rabbitmq.conf
# 修改对应值
# 相对大小,默认方式,默认值为0.4,建议设置范围为0.4~0.7
vm_memory_high_watermark.relative = 0.5
# 绝对大小
vm_memory_high_watermark.absolute = 100MB
# 内存换页,当内存达到一个界限就将内存置换到磁盘,默认值为0.5,设置值应该小于1
vm_memory_high_watermark_paging_ratio.relative = 0.5
  • 磁盘的预警管理控制
  • 命令行的方式(重启后失效)
# 相对内存大小,建议设置范围为1.0~2.0
rabbitmqctl set_disk_free_limit memory_limit 1.5
# 绝对大小,默认方式,默认值为50MB
rabbitmqctl set_disk_free_limit 100MB
  • 配置文件方式(重启后不失效)
vim /etc/rabbitmq/rabbitmq.conf
# 修改对应值
# 相对内存大小,建议设置范围为1.0~2.0
disk_free_limit.relative = 1.5
# 绝对大小,默认方式,默认值为50MB
disk_free_limit.absolute = 100MB

2.6 集群
  • 单机集群的搭建(节点1为主节点,节点2为从节点)
# 1. 启动第一个节点
sudo RABBITMQ_NODE_PORT=5672 RABBITMQ_NODENAME=rabbit-1 rabbit-server start &
# 2. 启动第二个节点
sudo RABBITMQ_NODE_PORT=5673 RABBITMQ_SERVER_ARGS="-rabbitmq_management listener [{port,15673}]" RABBITMQ_NODENAME=rabbit-2 rabbit-server start &
# 3. 停止节点1并清除节点的历史数据,再启动
sudo rabbitmqctl -n rabbit-1 stop_app
sudo rabbitmqctl -n rabbit-1 reset
sudo rabbitmqctl -n rabbit-1 start_app
# 4. 停止节点2并清除节点的历史数据,绑定节点1再启动(Server-node为主机名或IP地址)
sudo rabbitmqctl -n rabbit-2 stop_app
sudo rabbitmqctl -n rabbit-2 reset
sudo rabbitmqctl -n rabbit-2 join_cluster rabbit-1@'Server-node'
sudo rabbitmqctl -n rabbit-2 start_app
  • 节点用户添加
# 为第一、第二个节点添加用户
rabbitmqctl -n rabbit-1 add_user admin admin
rabbitmqctl -n rabbit-2 add_user admin admin
# 设置用户为管理员
rabbitmqctl -n rabbit-1 set_user_tags admin administrator
rabbitmqctl -n rabbit-2 set_user_tags admin administrator
# 为用户添加资源权限
rabbitmqctl -n rabbit-1 set_permissions -p / admin ".*" ".*" ".*"
rabbitmqctl -n rabbit-2 set_permissions -p / admin ".*" ".*" ".*"

2.7 分布式事务
  • application配置
# 服务器端口
server:
  port: 8081

# RabbitMQ服务配置
spring:
  rabbitmq:
    username: admin
    password: admin
    virtual-host: /
    host: 192.168.150.128
    port: 5672
    #addresses: 192.168.150.128:5672	  # 集群IP及端口连接方式
    publisher-confirm-type: correlated  # 消息确认机制
    publisher-returns: true # 开启发送失败退回
    
    template:
      mandatory: true # 保证监听有效
      
    listener:
      simple:
        acknowledge-mode: manual  # 手动ACK机制
        concurrency: 1  # 消费者最小数
        max-concurrency: 10 # 消费者最大处
        retry:
          enabled: true # 支持重试/重发
  • RabbitMQConfiguration消息队列配置类
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;

import java.util.HashMap;
import java.util.Map;

/**
 * RabbitMQ配置类
 */
@Configuration
public class RabbitMQConfiguration4 {

    /**
     * 声明死信交换机
     */
    @Bean
    public DirectExchange deadDirectExchange2() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("dead_direct_exchange2", true, false);
    }

    /**
     * 声明死信队列
     */
    @Bean
    public Queue deadQueue2() {
        return new Queue("dead2.direct.queue", true);
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding deadQueueBinding2() {
        return BindingBuilder
                .bind(deadQueue2())         // 要进行绑定的队列
                .to(deadDirectExchange2())   // 队列绑定的交换机
                .with("dead2");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

    /**
     * 声明交换机
     */
    @Bean
    public DirectExchange directExchange2() {
        /*
            @param1 交换机的名称
            @param2 交换机是否持久化
            @param3 交换机是否自动删除
         */
        return new DirectExchange("direct_exchange2", true, false);
    }

    /**
     * 声明队列
     */
    @Bean
    public Queue directQueue2() {
        Map<String, Object> args = new HashMap<>();
        args.put("x-dead-letter-exchange", "dead_direct_exchange2"); // 设置死信交换机
        args.put("x-dead-letter-routing-key", "dead2");  //  设置路由Key(Fanout模式交换机不需要设置)
        /*
            @param1 队列的名称
            @param2 队列是否持久化
            @param3 队列的排他性
            @param4 队列是否自动删除
            @param5 队列属性
         */
        return new Queue("direct2.queue", true, false, false, args);
    }

    /**
     * 队列与交换机绑定
     */
    @Bean
    public Binding directQueueBinding2() {
        return BindingBuilder
                .bind(directQueue2())         // 要进行绑定的队列
                .to(directExchange2())   // 队列绑定的交换机
                .with("direct2");   // 队列的路由Key(Fanout类型的交换机不需要)
    }

}
  • 生产者
  • Service接口
public interface ProducerTXService {
    void sendTXMessage(String message);
}
  • Service实现类
import com.mo.rabbitmqproducer.service.ProducerTXService;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

@Service
public class ProducerTXServiceImpl implements ProducerTXService {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @PostConstruct
    public void regCallback() {
        // 添加一个消息确认的回调方法
        rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> {
            System.out.println("cause:" + cause);

            if (!ack) {
                // 应答失败操作
                System.out.println("MQ消息应答失败");
                return;
            }

            try {
                System.out.println("更改消息投递状态");
                System.out.println("消息投递成功");
            } catch (Exception e) {
                System.out.println("消息状态修改失败");
                System.out.println("异常操作");
            }
        });
    }

    @Override
    public void sendTXMessage(String message) {
        // 交换机名称
        String exchangeName = "direct_exchange2";
        // 路由Key
        String routingKey = "direct2";
        // 发给消息队列
        rabbitTemplate.convertAndSend(exchangeName, routingKey, message, new CorrelationData(message));
    }

}
  • 消费者
  • Service接口
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.connection.CorrelationData;

public interface ConsumerTXService {
    void receiveTXMessage(String message, Channel channel, CorrelationData correlationData, long tag);
}
  • Service实现类
import com.mo.rabbitmqconsumer.service.ConsumerTXService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.support.AmqpHeaders;
import org.springframework.messaging.handler.annotation.Header;
import org.springframework.stereotype.Service;

import java.io.IOException;

@Service
public class ConsumerTXServiceImpl implements ConsumerTXService {

    @Override
    @RabbitListener(queues = {"direct2.queue"})
    public void receiveTXMessage(String message, Channel channel,
                                 CorrelationData correlationData,
                                 @Header(AmqpHeaders.DELIVERY_TAG) long tag){
        try {
            // 获取消息队列的信息
            System.out.println("收到的信息为:" + message);
            int a = 1/0;
            // 自动应答
            channel.basicAck(tag, false);
        } catch (Exception e) {
            try {
                // 手动应答
                channel.basicNack(tag, false, false);
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }

}
  • 测试
import com.mo.rabbitmqproducer.service.ProducerTXService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class RabbitmqProducerApplicationTests {
    
    @Autowired
    private ProducerTXService producerTXService;
    
    @Test
    void contextLoads() {
        producerTXService.sendTXMessage("hello");
    }

}