RabbitMQ--消息投递确认与100%投递成功方案

Rabbitmq消息投递确认有四种方案

  • 通过RabbitMQ事务
    @Test
    public void test02() throws IOException, TimeoutException {
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(HOST);
        connectionFactory.setPort(PORT);
        connectionFactory.setUsername(USERNAME);
        connectionFactory.setPassword(PASSWORD);
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        try {
            //开启事务
            channel.txSelect();
            channel.basicPublish(EXCHANGE_NAME, ROUNTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN,
                    ("hello world ").getBytes());
            System.out.println(1 / 0);
            //提交
            channel.txCommit();
        } catch (Exception e) {
            e.printStackTrace();
            //回滚
            channel.txRollback();
        } finally {
            channel.close();
        }
        if (connection != null) {
            connection.close();
        }
    }

开 启 R a b b i t m q 的 事 务 , 然 后 推 送 消 息 , 后 续 如 果 异 常 , 则 回 滚 。 该 种 方 式 对 r a b b i t m q 的 性 能 会 产 生 较 大 影 响 。 \color{#FF0000}{开启Rabbitmq的事务,然后推送消息,后续如果异常,则回滚。该种方式对rabbitmq的性能会产生较大影响。} Rabbitmqrabbitmq

  • 同步Confirm模式
ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(HOST);
        connectionFactory.setPort(PORT);
        connectionFactory.setUsername(USERNAME);
        connectionFactory.setPassword(PASSWORD);
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        try {
            //设置confirm模式
            channel.confirmSelect();
            channel.basicPublish(EXCHANGE_NAME, ROUNTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN,
                    ("hello world ").getBytes());
            if (!channel.waitForConfirms()) {
                System.out.println("发送消息失败");
                //重试
            }else{
                System.out.println("投递成功");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            channel.close();
            connection.close();
        }

该 种 方 式 能 较 事 务 那 种 方 式 性 能 能 提 升 一 点 , 但 是 也 是 一 种 串 行 方 式 , 即 推 送 一 条 信 息 , 然 后 确 认 。 \color{#FF0000}{该种方式能较事务那种方式性能能提升一点,但是也是一种串行方式,即推送一条信息,然后确认。}

  • 同步批量confirm
ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(HOST);
        connectionFactory.setPort(PORT);
        connectionFactory.setUsername(USERNAME);
        connectionFactory.setPassword(PASSWORD);
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        channel.confirmSelect();
        int count = 0;
        int batchPublishNum = 10;
        List<Integer> failList = new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            channel.basicPublish(EXCHANGE_NAME, ROUNTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN,
                    ("hello world " + i).getBytes());
            failList.add(i);
            if (++count >= batchPublishNum) {
                //开始检查一批是否提交成功
                count = 0;
                if (channel.waitForConfirms()) {
                    //投递成功
                    failList.clear();
                } else {
                    //投递失败--重新投递
                    for (int j = 0; j < failList.size(); j++) {
                        channel.basicPublish(EXCHANGE_NAME, ROUNTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN,
                                ("hello world " + j).getBytes());
                    }
                }
            }
        }
        channel.close();
        connection.close();

批 量 c o n f i r m 较 单 条 性 能 能 提 升 不 少 , 减 少 了 交 互 , \color{#FF0000}{批量confirm较单条性能能提升不少,减少了交互,} confirm

  • 异步回调
        //推送消息
        ConnectionFactory connectionFactory = new ConnectionFactory();
        connectionFactory.setHost(HOST);
        connectionFactory.setPort(PORT);
        connectionFactory.setUsername(USERNAME);
        connectionFactory.setPassword(PASSWORD);
        Connection connection = connectionFactory.newConnection();
        Channel channel = connection.createChannel();
        //开启confirm确认
        channel.confirmSelect();
        channel.exchangeDeclare(EXCHANGE_NAME, "direct", true, false, null);
        channel.queueDeclare(QUEUE_NAME, true, false, false, null);
        channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUNTING_KEY);

        //推送消息
        for (int i = 0; i < 100; i++) {
            long seqNo = channel.getNextPublishSeqNo();
            channel.basicPublish(EXCHANGE_NAME, ROUNTING_KEY, MessageProperties.PERSISTENT_TEXT_PLAIN,
                    ("hello world " + i).getBytes());
            seqNoSet.add(seqNo);
        }
        System.out.println("推送完成");

        //监控推送回调
        channel.addConfirmListener(new ConfirmListener() {
            @Override
            public void handleAck(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("ack:" + deliveryTag + " multiple:" + multiple);
                if (multiple) {
                    seqNoSet.headSet(deliveryTag + 1).clear();
                } else {
                    seqNoSet.remove(deliveryTag);
                }
            }

            @Override
            public void handleNack(long deliveryTag, boolean multiple) throws IOException {
                System.out.println("nack:" + deliveryTag + " multiple:" + multiple);
                if (multiple) {
                    seqNoSet.headSet(deliveryTag + 1).clear();
                } else {
                    seqNoSet.remove(deliveryTag);
                }
                //处理投递失败的场景
            }
        });
        Thread.sleep(200000);
        channel.close();
        connection.close();

        while (Thread.activeCount() > 2) {
            System.out.println(seqNoSet.size());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("run over");

异 步 回 调 较 批 量 确 认 性 能 提 升 了 一 点 。 \color{#FF0000}{异步回调较批量确认性能提升了一点。}

100%消息投递成功方案

  • 消息落库打标
    在这里插入图片描述
    该方案的核心的是通过表记录投递成功与否,失败的通过定时任务进行重新推送。

  • 消息延迟投递,做二次确认。
    在这里插入图片描述
    说明:

  • 推送消息会推送两次,第一次是真正的业务消息,如果消费失败,那么监听到消费失败后会修改msg表的标识,第二次只是检查一下是否推送成功,检查msg表如果为失败,则发送消息重新推送业务消息。
    图片摘自某课网。

幂等性
重复提交是在第一次请求已经成功的情况下,人为的进行多次操作,导致不满足幂等要求的服务多次改变状态。而幂等更多使用的情况是第一次请求不知道结果(比如超时)或者失败的异常情况下,发起多次请求,目的是多次确认第一次请求成功,却不会因多次请求而出现多次的状态变化
针对RabbitMQ,消费端收到多条也只消费一次

  • 利用redis的原子性去实现。
    需要解决的问题
    1 实现写redis和写数据库原子性操作。
    可以采用均写redis,定时同步到缓存中。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值