一个小demo:基于 RabbitMQ 的消息发布与订阅

pom.xml

		<dependency>
			<groupId>com.rabbitmq</groupId>
			<artifactId>amqp-client</artifactId>
			<version>4.1.0</version>
		</dependency>

RabbitMQ 连接配置工具类

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

public class ChannelUtils {

    public static Channel getChannelInstance(String connectionDescription) {
        try {
            ConnectionFactory connectionFactory = getConnectionFactory();
            // 创建连接
            Connection connection = connectionFactory.newConnection(connectionDescription);
            return connection.createChannel();
        } catch (Exception e) {
            throw new RuntimeException("获取Channel连接失败");
        }
    }

    /**
     * 功能描述:
     * <连接配置>
     *
     *
     * @return com.rabbitmq.client.ConnectionFactory
     * @author zhoulipu
     * @date   2019/7/31 18:02
     */
    private static ConnectionFactory getConnectionFactory() {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        // 配置连接信息
        connectionFactory.setHost("127.0.0.1");
        connectionFactory.setPort(5672);
        connectionFactory.setVirtualHost("/");
        connectionFactory.setUsername("guest");
        connectionFactory.setPassword("guest");
        // 失败重连模式:网络异常自动重新连接
        connectionFactory.setAutomaticRecoveryEnabled(true);
        // 失败重连模式:每10秒重试连接
        connectionFactory.setNetworkRecoveryInterval(10000);
        // 失败重连模式:重新声明交换器,队列等信息
        connectionFactory.setTopologyRecoveryEnabled(true);
        return connectionFactory;
    }

}

生产者发布消息

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Scanner;

public class Producer {

    /**
     * 交换机名称(需与消费者保持一致)
     */
    private static final String pbxName = "PBX";

    /**
     * 路由密匙(需与消费者保持一致)
     */
    private static final String routingKey = "routingKey";

    public static void main(String[] args) throws IOException {
        // 获取连接
        Channel channel = ChannelUtils.getChannelInstance("队列消息生产者");
        // 生产者配置
        AMQP.BasicProperties basicProperties = producerConfig(channel, pbxName);
        while (true) {
            System.out.print("请输入即将发布的消息:");
            Scanner str = new Scanner(System.in);
            // 发布消息
            publish(channel, basicProperties, pbxName, routingKey, str.next());
            System.out.println("消息已发布,请查看订阅者状态");
        }
    }

    /**
     * 功能描述:
     * <生产者发布消息>
     *
     * @param channel         1
     * @param basicProperties 2
     * @param pbxName         3
     * @param routingKey      4
     * @param msg             5
     * @return void
     * @author zhoulipu
     * @date 2019/7/31 17:52
     */
    public static void publish(Channel channel, AMQP.BasicProperties basicProperties, String pbxName, String routingKey, String msg) throws IOException {
        // 发布消息(交换机名, 路由关键字, 是否为强制性, 消息属性, 消息体);
        channel.basicPublish(pbxName, routingKey, false, basicProperties, msg.getBytes());
    }

    /**
     * 功能描述:
     * <生产者配置>
     *
     * @param channel 1
     * @param pbxName 2
     * @return com.rabbitmq.client.AMQP.BasicProperties
     * @author zhoulipu
     * @date 2019/7/31 17:52
     */
    public static AMQP.BasicProperties producerConfig(Channel channel, String pbxName) throws IOException {
        // 声明交换机 (交换机名称, 交换机类型, 是否持久化, 是否自动删除, 是否是内部交换机(客户机直接发送消息), 队列中的消息自动删除配置);
        channel.exchangeDeclare(pbxName, BuiltinExchangeType.DIRECT, true, false, false, new HashMap<>());
        // 消息属性
        AMQP.BasicProperties basicProperties = new AMQP.BasicProperties().builder()
                .deliveryMode(2) // 设置消息是否持久化,1: 非持久化 2:持久化
                .contentType("UTF-8")
                .contentEncoding("UTF-8")
                .headers(new HashMap<>())
                .build();
        return basicProperties;
    }

}

消费者订阅消息

import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class Consumer {

    /**
     * 交换机名称(需与消费者保持一致)
     */
    private static final String pbxName = "PBX";

    /**
     * 路由密匙(需与消费者保持一致)
     */
    private static final String routingKey = "routingKey";


    public static void main(String[] args) throws IOException {
        // 队列名称(多个消费者需使用不同队列名称)
        String queueName = "queueName";
        // 获取连接
        Channel channel = ChannelUtils.getChannelInstance("队列消息消费者");
        // 消费者配置(交换机、路由、队列)
        AMQP.Queue.DeclareOk declareOk = consumerConfig(channel, pbxName, routingKey, queueName);
        // 订阅消息
        subscribe(channel, declareOk, "消费者名称");
    }

    /**
     * 功能描述:
     * <消费者订阅消息>
     *
     * @param channel     1
     * @param declareOk   2
     * @param consumerTag 3
     * @return void
     * @author zhoulipu
     * @date 2019/7/31 17:51
     */
    public static void subscribe(Channel channel, AMQP.Queue.DeclareOk declareOk, String consumerTag) throws IOException {
        // 消费者订阅消息 监听队列 (队列名, 是否自动应答(与消息可靠有关 后续会介绍), 消费者标签, 消费者)
        DefaultConsumer defaultConsumer = new DefaultConsumer(channel) {
            @Override
            public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) {
                System.out.println("consumerTag:" + consumerTag);
                System.out.println("envelope:" + envelope.toString());
                System.out.println("properties:" + properties.toString());
                System.out.println("消息内容:" + new String(body));
            }
        };
        // 回调(队列名,是否自动确认消息,消费者)
        channel.basicConsume(declareOk.getQueue(), true, consumerTag, defaultConsumer);
    }

    /**
     * 功能描述:
     * <消费者配置>
     *
     * @param channel    1
     * @param pbxName    2
     * @param routingKey 3
     * @param queueName  4
     * @return com.rabbitmq.client.AMQP.Queue.DeclareOk
     * @author zhoulipu
     * @date 2019/7/31 17:51
     */
    public static AMQP.Queue.DeclareOk consumerConfig(Channel channel, String pbxName, String routingKey, String queueName) throws IOException {
        // 消息过期配置(文末对参数有简述)
        Map<String, Object> arguments = new HashMap<String, Object>();
        // 声明队列 (队列名, 是否持久化, 是否排他, 是否自动删除, 队列中的消息自动删除配置);
        AMQP.Queue.DeclareOk declareOk = channel.queueDeclare(queueName, true, false, false, arguments);
        // 将队列Binding到交换机上 (队列名, 交换机名, 路由关键字);
        channel.queueBind(declareOk.getQueue(), pbxName, routingKey);
        return declareOk;
    }
    
}

部分参数说明

basicProperties:

属性解释
String contentType上下文类型
String contentEncoding编码集
Map<String,Object> headers消息头
Integer deliveryMode消息的投递模式
Integer priority优先级
String correlationId自定义关联ID
String replyTo回复对象
String expiration过期时间
String messageId消息编号
Date timestamp发送消息时的时间戳
String type消息类型

arguments:

key解释
x-message-ttl队列中的所有消息的过期时间
x-expires超过设定时间没有消费者来访问队列,就删除队列的时间(毫秒)
x-max-length队列的最新的消息数量,如果超过设定数量,前面的消息将从队列中移除掉
x-max-length-bytes队列的内容的最大空间,超过该阈值就删除之前的消息
x-dead-letter-exchange如果队列中的消息被拒绝或过期,则可以根据设置的交换机对消息进行转发,重新发布
x-dead-letter-routing-key将删除的消息推送到指定的交换机对应的路由键(路由密匙)
x-max-priority队列支持的最大优先级数;如果未设置,队列将不支持消息优先级,优先级大的优先被消费
x-queue-mode将队列设置为延迟模式,队列中的消息保存在磁盘中,但不会主动持久化,RabbitMQ重启后消息还是会丢失
x-queue-master-locator将队列设置为主位置模式,确定在节点集群上声明时队列主机所在的规则
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要保证RabbitMQ消息持久化,需要在以下几个方面进行配置: 1. Exchange和Queue的持久化:在RabbitMQ中,Exchange和Queue默认情况下是非持久化的,也就是说它们只存在于内存中,当RabbitMQ服务器重启时,这些非持久化的Exchange和Queue将被删除。为了实现Exchange和Queue的持久化,需要在创建Exchange和Queue时将其标记为持久化。 2. 消息的投递模式:消息持久化需要将消息先存储在磁盘上,然后再进行投递。因此,需要将消息的投递模式设置为"持久化"。 3. 消息的确认模式:为了确保消息被正确地投递并存储在磁盘上,需要启用消息的确认模式。消息确认模式有两种:Publisher Confirmation和Consumer Acknowledgement。 下面是保证RabbitMQ消息持久化的具体方法: 1. Exchange和Queue的持久化: ```java // 创建Exchange和Queue时设置为持久化 channel.exchangeDeclare("exchange_name", "direct", true); channel.queueDeclare("queue_name", true, false, false, null); // 将Queue绑定到Exchange上 channel.queueBind("queue_name", "exchange_name", "routing_key"); ``` 2. 消息的投递模式: ```java // 将消息设置为持久化 byte[] messageBytes = "Hello, RabbitMQ!".getBytes(); BasicProperties properties = new BasicProperties().builder() .deliveryMode(2) // 持久化消息 .build(); // 发送消息到持久化的Queue channel.basicPublish("exchange_name", "queue_name", properties, messageBytes); ``` 3. 消息的确认模式: ```java // 启用Publisher Confirmation模式 channel.confirmSelect(); // 发送消息到持久化的Queue,并等待确认 channel.basicPublish("exchange_name", "queue_name", properties, messageBytes); if (!channel.waitForConfirms()) { // 消息发送失败 System.out.println("Message send failed!"); } // 启用Consumer Acknowledgement模式 channel.basicConsume("queue_name", false, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { // 处理消息 // 发送确认消息 channel.basicAck(envelope.getDeliveryTag(), false); } }); ``` 需要注意的是,以上三个方面都需要进行配置才能保证RabbitMQ消息的持久化,并且在消费者处理消息时,需要发送确认消息来确认消息已经被消费。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值