• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
Y-wee
博客园    首页    新随笔    联系   管理     

rabbitmq发布确认

rabbitmq发布确认

发布确认原理

生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都将会被指派一个唯一的 ID(从 1 开始),一旦消息被投递到所有匹配的队列之后,broker 就会发送一个确认给生产者(包含消息的唯一 ID),这就使得生产者知道消息已经正确到达目的队列了

如果消息和队列是可持久化的,那么确认消息会在将消息写入磁盘之后发出,broker 回传给生产者的确认消息中 delivery-tag 域包含了确认消息的序列号,此外 broker 也可以设置basic.ack 的multiple 域,表示到这个序列号之前的所有消息都已经得到了处理。

1、单个发布确认

这是一种简单的确认方式,它是一种同步确认发布的方式,也就是发布一个消息之后只有它被确认发布,后续的消息才能继续发布,waitForConfirmsOrDie(long)这个方法只有在消息被确认的时候才返回,如果在指定时间范围内这个消息没有被确认那么它将抛出异常

这种确认方式有一个最大的缺点就是:发布速度特别的慢,因为如果没有确认发布的消息就会阻塞所有后续消息的发布,这种方式最多提供每秒不超过数百条发布消息的吞吐量。当然对于某些应用程序来说这可能已经足够了

2、批量发布确认

与单个等待确认消息相比,先发布一批消息然后一起确认可以极大地提高吞吐量,当然这种方式的缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布消息。当然这种方案仍然是同步的,也一样阻塞消息的发布

代码实现

package com.yl.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.yl.util.RabbitConstant;
import com.yl.util.RabbitUtils;

import java.io.IOException;

/**
 * 发布确认
 * 注意:这里的确认指的是确认发布,即确认消息被发送到队列中了,而不是确认消息被消费成功
 * 单个发布确认:发布一个消息之后确认消息是否发布成功(发布失败,后续代码依然可以执行)
 * 批量发布确认:先发布一批消息然后一起确认消息是否发布成功
 *
 * @author Y-wee
 */
public class ProviderSingle {

    public static void main(String[] args) throws IOException, InterruptedException {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        // 开启发布确认
        channel.confirmSelect();

        channel.queueDeclare(RabbitConstant.QUEUE_CONFIRM, false, false, false, null);

        for (int i = 0; i < 100; i++) {
            String message=i+"";
            channel.basicPublish("",RabbitConstant.QUEUE_CONFIRM,null,message.getBytes());
            // 确认消息是否发布成功,若(若要批量确认则判断发布多条消息后再执行该方法)
            channel.waitForConfirms();

            System.out.println("第"+i+"条消息发送成功");
        }

    }

}

RabbitUtils是笔者自定义的一个工具类,用来获取连接

RabbitConstant是笔者自定义的一个常量类,里面存储了队列名和交换机名

3、异步发布确认

异步确认虽然编程逻辑比上两个要复杂,但是性价比最高,无论是可靠性还是效率都没得说,他是利用回调函数来达到消息可靠性传递的

代码实现

package com.yl.confirm;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmCallback;
import com.rabbitmq.client.Connection;
import com.yl.util.RabbitConstant;
import com.yl.util.RabbitUtils;

import java.io.IOException;
import java.util.concurrent.ConcurrentNavigableMap;
import java.util.concurrent.ConcurrentSkipListMap;

/**
 * 异步发布确认
 *
 * @author Y-wee
 */
public class ProviderAsync {

    public static void main(String[] args) throws IOException {
        Connection connection = RabbitUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.confirmSelect();
        channel.queueDeclare(RabbitConstant.QUEUE_CONFIRM, false, false, false, null);

        /**
         * 线程安全有序的一个哈希表,适用于高并发
         * 存储消息(Long-消息标记,String-消息),方便发布确认失败回调函数获取发布失败的消息并处理
         */
        ConcurrentSkipListMap<Long,String> map=new ConcurrentSkipListMap();

        /**
         * 发布确认成功回调函数
         * deliveryTag:消息标记,如果是批量的则该标记是最后一条消息的
         * multiple:是否批量确认
         */
        ConfirmCallback ackCallback=(deliveryTag,multiple)->{
            // 获取发布确认成功的消息并从map中删除
            if (multiple){
                // 获取key<deliveryTag的值,deliveryTag+1即获取所有发布确认成功的消息
                ConcurrentNavigableMap<Long,String> confirmMap=map.headMap(deliveryTag+1);
                confirmMap.clear();
            }else {
                map.remove(deliveryTag);
            }

            System.out.println(deliveryTag+"发布成功");
        };

        // 发布确认失败回调函数
        ConfirmCallback nackCallback=(deliveryTag,multiple)->{
            // 获取确认发布失败的消息
            String message=map.get(deliveryTag);
            System.out.println(message+"发布失败");
        };

        // 添加发布确认监听器
        channel.addConfirmListener(ackCallback,nackCallback);

        for (int i = 0; i < 100; i++) {
            String message=i+"";
            channel.basicPublish("",RabbitConstant.QUEUE_CONFIRM,null,message.getBytes());
            /**
             * 将发送的消息存入map
             * channel.getNextPublishSeqNo():要发布的下一条消息的序列号
             */
            map.put(channel.getNextPublishSeqNo(),message);
        }

        System.out.println("消息发布完成");

    }

}
记得快乐
posted @ 2021-12-26 20:35  Y-wee  阅读(52)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3