RabbitMQ笔记

使用vmware15、centos7.9、jdk11、rabbitmq3.7.18

安装

  • 因为RabbitMQ是基于erlang编写,所以在安装之前需要引入erlang运行依赖
    erlang-22.0.7-1.el7.x86_64.rpm
    socat-1.7.3.2-2.el7.x86_64.rpm
  • 引入RabbitMQ安装包
    rabbitmq-server-3.7.18-1.el7.noarch.rpm

配置

  • 创建配置文件
// rabbitmq-server-3.7.18根据安装版本填写
cp /usr/share/doc/rabbitmq-server-3.7.18/rabbitmq.config.example /etc/rabbitmq/rabbitmq.config
  • 开启web管理
// 输入命令
rabbitmq-plugins enable rabbitmq_management
  • 开启guest访问账号(56行去掉注释)
 49    %% Security, Access Control
 50    %% ========================
 51    %%
 52    %% Related doc guide: https://www.rabbitmq.com/access-control.html.
 53 
 54    %% The default "guest" user is only permitted to access the server
 55    %% via a loopback interface (e.g. localhost).
 56    {loopback_users, []}
 57    %%
 58    %% Uncomment the following line if you want to allow access to the
 59    %% guest user from anywhere on the network.
 60    %% {loopback_users, []},
  • 浏览器访问 http://主机IP:15672,账号密码默认为guest

队列模式

//封装的连接工具类
public class RabbitMQUtils {
    private static ConnectionFactory connectionFactory;

    static {
        connectionFactory = new ConnectionFactory();
        //设置rabbitmq主机
        connectionFactory.setHost("192.168.227.202");
        //设置端口号
        connectionFactory.setPort(5672);
        //设置连接的虚拟主机
        connectionFactory.setVirtualHost("/ems");
        //设置管理员
        connectionFactory.setUsername("ems");
        connectionFactory.setPassword("000");
    }

    public static Connection createConnection() {
        Connection connection = null;
        try {
            connection = connectionFactory.newConnection();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }

        return connection;
    }

    public static void closeConnectionAndChannel(Connection connection, Channel channel) {
        try {
            channel.close();
            connection.close();
        } catch (IOException | TimeoutException e) {
            e.printStackTrace();
        }
    }
}
hello world
  • 生产者绑定通道后,将消息直接发送到指定虚拟机的队列中
  • 单个消费者直接从队列中获取消息
//生产者
public class Producer {

    @Test
    public void sendMessage() throws IOException, TimeoutException {
        Connection connection = RabbitMQUtils.createConnection();

        //通过连接获取通道
        Channel channel = connection.createChannel();
        //通道绑定队列
        //参数:队列名称、是否持久化、是否独占队列、是否消费完成后自动删除队列、附加参数
        //当队列不存在时自动创建
        channel.queueDeclare("ems", true, false, false, null);
        //发布消息
        //参数:交换机名称、路由关键字(队列)、额外设置、消息内容
        channel.basicPublish("", "ems", MessageProperties.PERSISTENT_TEXT_PLAIN, "hello".getBytes());

        RabbitMQUtils.closeConnectionAndChannel(connection, channel);
    }
}
/消费者
public class Consumer {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();

        channel.queueDeclare("ems", true, false, false, null);
        channel.basicConsume("ems", true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
            }
        });
    }
}
work queue
  • 生产者绑定通道后,将消息发送到队列中
  • 队列消息被消费时,如果开启自动确认,队列中的消息将均分给每个消费者
//生产者
public class Producer {
    @Test
    public void sendMessage() throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        //参数详解queue, durable, exclusive, autoDelete, arguments
        //参数1:声明队列的名称,不存在则创建
        //参数2:是否设置队列持久化,服务器宕机或重启后,队列不删除
        //参数3:是否设置独占队列,只允许当前connection下的通道访问
        //参数4:是否设置自动删除,当队列为空,最后一个消费者断开连接时,自动删除队列
        //参数5:其他配置
        channel.queueDeclare("work", false, false, false, null);
        for (int i = 1; i <= 20; i++) {
            //参数exchange, routingKey, props, body
            channel.basicPublish("", "work", null, ("message body " + i).getBytes());
        }
        RabbitMQUtils.closeConnectionAndChannel(connection, channel);
    }
}
//消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("work", false, false, false, null);
        channel.basicConsume("work", true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
            }
        });
    }
}
  • 配置“多劳多得”模式
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        channel.queueDeclare("work", false, false, false, null);
        //设置每次只取回一个队列消息
        channel.basicQos(1);
        //参数2:关闭自动确认,自动确认会将队列中的消息全部取回
        channel.basicConsume("work", false, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
                //手动确认消息被消费完成,可处理下一条消息
                channel.basicAck(envelope.getDeliveryTag(), true);
            }
        });
    }
}
subscribe 订阅(广播)
  • 生产者绑定通道,将消息发送给交换机
  • 每个消费者通过临时队列从交换机获取消息
  • 消费者之间获取的消息完全一致
//生产者
public class Producer {
    @Test
    public void sendMessage() throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        //声明交换机,设置为fanout类型
        channel.exchangeDeclare("notice", "fanout");
        //向交换机发送消息
        channel.basicPublish("notice", "", null, "message body".getBytes());
        RabbitMQUtils.closeConnectionAndChannel(connection, channel);
    }
}
//消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        //防止先运行消费者报错,可省略
        channel.exchangeDeclare("notice", "fanout");
        //声明临时队列
        String queue = channel.queueDeclare().getQueue();
        //队列绑定交换机
        channel.queueBind(queue, "notice", "");
        channel.basicConsume(queue, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println("消费者1 " + new String(body));
            }
        });
    }
}
routing
  • 生产者创建通道,向交换机发送指定routingKey的消息
  • 消费者根据交换机中routingKey的值,选择性的获取消息
//生产者
public class Producer {
    @Test
    public void sendMessage() throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        //设置交换机为direct类型
        channel.exchangeDeclare("logs", "direct");
        String routingKey = "warning";
        channel.basicPublish("logs", routingKey, null, ("logs type is " + routingKey).getBytes());

        RabbitMQUtils.closeConnectionAndChannel(connection, channel);
    }
}
//消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();

        channel.exchangeDeclare("logs", "direct");
        String queueName = channel.queueDeclare().getQueue();
        //String queue, String exchange, String routingKey, Map arguments
        //设置只从交换机中获取routingKey为warning的消息
        channel.queueBind(queueName, "logs", "warning", null);
        //channel.queueBind(queueName, "logs", "error", null);
        channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
            }
        });
    }
}
topic
  • topic模式是在routing模式的基础上增加了routingKey的通配符*,#
  • 多个key之间使用.(点)分隔
  • * 匹配单个key
  • # 匹配一个或多个key
//生产者
public class Producer {
    @Test
    public void sendMessage() throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        //设置交换机为topic类型
        channel.exchangeDeclare("order", "topic");
        String routingKey = "order.new.add";
        channel.basicPublish("logs", routingKey, null, ("order message: " + routingKey).getBytes());

        RabbitMQUtils.closeConnectionAndChannel(connection, channel);
    }
}
//消费者
public class Consumer1 {
    public static void main(String[] args) throws IOException {
        Connection connection = RabbitMQUtils.createConnection();
        Channel channel = connection.createChannel();
        
        channel.exchangeDeclare("order", "topic");
        String queueName = channel.queueDeclare().getQueue();
        //下列两种写法都可以获取routingKey为order.new.add的消息
        channel.queueBind(queueName, "order", "order.new.*", null);
        //channel.queueBind(queueName, "order", "order.#", null);
        channel.basicConsume(queueName, true, new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
                System.out.println(new String(body));
            }
        });
    }
}

SpringBoot2整合RabbitMQ

  • pom文件导入amqp场景

    org.springframework.boot
    spring-boot-starter-amqp

  • application.yml配置RabbitMQ连接参数
spring:
  application:
    name: springboot_rabbitmq
  rabbitmq:
    host: 192.168.227.201
    port: 5672
    username: ems
    password: "000" #密码试了试不加双引号会报错
    virtual-host: /ems
  • 生产者(单元测试)
@SpringBootTest(classes = SpringRabbitmqApplication.class)
@RunWith(SpringRunner.class)
public class SpringRabbitmqApplicationTests {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Test
    public void sendMessage() {
        rabbitTemplate.convertAndSend("hello", "hello rabbit");
    }

    @Test
    public void sendMessageByWork() {
        for (int i = 0; i < 10; i++) {
            rabbitTemplate.convertAndSend("work", "message " + i);
        }
    }

    @Test
    public void sendMessageByFanout() {
        rabbitTemplate.convertAndSend("notice", "", "notice message");
    }

    @Test
    public void sendMessageByDirect() {
        rabbitTemplate.convertAndSend("logs", "error", "warning message");
    }

    @Test
    public void sendMessageByTopic() {
        rabbitTemplate.convertAndSend("order", "order.history.show", "order message");
    }
}
  • 消费者(以topic模式为例)
@Component
public class TopicConsumer {
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue,
            exchange = @Exchange(value = "order", type = "topic"),
            key = {"order.new.*"}
    ))
    public void consumer1(String msg) {
        System.out.println("consume new order message");
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue,
            exchange = @Exchange(value = "order", type = "topic"),
            key = {"order.#"}
    ))
    public void consumer2(String msg) {
        System.out.println("consume all order message");
    }
}
  • 在springboot中,交换机和队列的创建以消费者为依据,当消费者不存在时,生产者无法发布消息到队列

集群(vmware + centos7)

主备模式
  • 主备集群模式特点
  1. 队列源数据只存放在主服务中
  2. 可以通过从服务器间接获取队列中的消息
  3. 当主服务停止,从服务器无法提供队列消息
  4. 向从服务发送队列消息,会存到到主服务中
  5. 为了防止数据丢失,请设置队列数据持久化
  • 使用vmware创建三台虚拟机
  • 配置主机名分别为centos7-1(主服务器)、centos7-2(从)、centos7-3(从)
vim /etc/hostname
  • 配置主机IP分别为 x.x.x.201、x.x.x.202、x.x.x.203
vim /etc/sysconfig/network-scripts/ifcfg-xxx
  • 每个虚拟机都修改hosts文件,主机名与IP对应
vim /etc/hosts
//格式如下
x.x.x.201 centos7-1
x.x.x.202 centos7-2
x.x.x.203 centos7-3
  • 安装RabbitMQ,开启web管理及用户访问
  • 开启RabbitMQ服务,浏览器打开web管理页,检查是否成功
  • 关闭服务,将centos7-1中的.erlang.cookie文件同步给其他两台服务器
scp /var/lib/rabbitmq/.erlang.cookie root@centos7-2:/var/lib/rabbitmq/
scp /var/lib/rabbitmq/.erlang.cookie root@centos7-3:/var/lib/rabbitmq/
  • 检查三台服务器.erlang.cookie文件内容是否一致
cat /var/lib/rabbitmq/.erlang.cookie
  • 静默开启RabbitMQ服务(三台)
rabbitmq-server -detached
  • 查看各自集群状态
rabbitmqctl cluster_status  
  • 关闭备用虚拟机RabbitMQ服务(从)
rabbitmqctl stop_app
  • 从服务加入主服务集群(从)
rabbitmqctl join_cluster rabbit@centos7-1
  • 开启RabbitMQ服务(从)
rabbitmqctl start_app
  • 查看集群状态
rabbitmqctl cluster_status
//集群状态
Cluster status of node rabbit@centos7-1 ...
[{nodes,[{disc,['rabbit@centos7-1','rabbit@centos7-2','rabbit@centos7-3']}]},
 {running_nodes,['rabbit@centos7-2','rabbit@centos7-3','rabbit@centos7-1']},
 {cluster_name,<<"rabbit@centos7-1">>},
 {partitions,[]},
 {alarms,[{'rabbit@centos7-2',[]},
          {'rabbit@centos7-3',[]},
          {'rabbit@centos7-1',[]}]}]
镜像集群模式
  • 个人理解为根据某种访问策略,将队列消息以镜像的形式共享给主从服务器使用
  • 设置策略,在任一主备集群服务器上执行都有效
  • 主服务器宕机,从服务器同样可以获取并消费消息
  • 虚拟主机操作命令
rabbitmqctl add_vhost 
rabbitmqctl delete_vhost 
rabbitmqctl list_vhosts [ ...]
  • 镜像策略操作命令
//设置策略,[]中的内容可选配
rabbitmqctl set_policy [-p ] [--priority ] [--apply-to ]    

//清除
rabbitmqctl clear_policy [-p ] 

//查看
rabbitmqctl list_policies [-p ]
  • 设置策略参数说明
参数 说明
-p 可选参数,针对指定 vhost 下的exchange或 queue
--priority 可选参数,policy 的优先级
--apply-to 可选参数,策略适用的对象类型,其值可为 “queues”, “exchanges” 或 “all”.默认是”all”
name policy 的名称
pattern 匹配模式(正则表达式)
definition 镜像定义,json 格式,包括三部分(ha-mode,ha-params,ha-sync-mode)
  • definition 镜像定义参数说明
参数 说明
ha-mode 镜像队列模式
all:表示在集群所有节点上进行镜像;
exactly:表示在指定个数的节点上镜像,节点个数由 ha-params 指定;
nodes:表示在指定节点上进行镜像,节点名称通过ha-params 指定
ha-params ha-mode 模式需要用到的参数
1. exactly 模式下为数字表述镜像节点数;
2. nodes 模式下为节点列表表示需要镜像的节点
ha-sync-mode 队列消息的同步方式,其值可为”automatic”(自动)或”manually”(手动)
  • 示例
//为/ems虚拟主机设置名称为ha的镜像策略,匹配规则“^”为全部
rabbitmqctl set_policy -p /ems ha '^' '{"ha-mode":"all","ha-sync-mode":"automatic"}'

你可能感兴趣的:(RabbitMQ笔记)