zoukankan      html  css  js  c++  java
  • rabbitmq 公平分发和消息接收确认(转载)

    原文地址:http://www.jianshu.com/p/f63820fe2638

    当生产者投递消息到broker,rabbitmq把消息分发到消费者。 如果设置了autoAck=true 消费者会自动确认收到信息。这时broker会立即将消息删除,这种情况下如果消费者出现异常(连接中断)该消息就会丢失。为了保证消息能够被正确的消费,rabbitmq支持消息确认。

     

    String basicConsume(String queue, boolean autoAck, Consumer callback) throws IOException;

     

    sender:

    public class Send {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {

    ConnectionFactory factory = new ConnectionFactory();

    factory.setHost("localhost");

    Connection connection = factory.newConnection();

    Channel channel = connection.createChannel();

    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    String message = "Hello World!kkkkkkkkkkkkkk";

    channel.basicPublish("", QUEUE_NAME, null, message.getBytes("UTF-8"));

    System.out.println(" [x] Sent '" + message + "'");

    channel.close();

    connection.close();

    }

    }

     

    receive:

    public class Receive {

    private final static String QUEUE_NAME = "hello";

    public static void main(String[] argv) throws Exception {

    ConnectionFactory factory = new ConnectionFactory();

    factory.setHost("localhost");

    Connection connection = factory.newConnection();

    Channel channel = connection.createChannel();

    channel.queueDeclare(QUEUE_NAME, false, false, false, null);

    System.out.println(" [*] Waiting for messages. To exit press CTRL+C");

    // channel.basicQos(1);//使得每个Consumer在同一个时间点最多处理一个Message。在接收到该Consumer的ack前,不会将新的Message分发给它

    Consumer consumer = new DefaultConsumer(channel) {

    @Override

    public void handleDelivery(String consumerTag, Envelope envelope,AMQP.BasicProperties properties,byte[] body) throws IOException {

    try {

    Thread.sleep(10000);

    } catch (InterruptedException e) {

    e.printStackTrace();

    }

    String message = new String(body, "UTF-8");

    System.out.println(" [x] Received '" + message + "'");

    // channel.basicAck(envelope.getDeliveryTag(), false);

    }

    };

    channel.basicConsume(QUEUE_NAME, false, consumer);

    }

    }

     

    启动sender 发送3消息:


    rabbitmq 中有了两条消息记录:


    启动消费者:


    channel.basicConsume(QUEUE_NAME, false, consumer);

    消费者没有确认消息被消费,消息一直留在队列中,只有当从有新的消费者加入时,消息被分发到新的消费者。如果由于连接中断,消费者退出时,那么消息会被轮训分发到其余的消费者。

    公平分发

    rabbitmq 是以轮训的方式进行分发消息,将N个消息发到n个消费者,这样有可能出现某些消费者要处理一些耗时的消息堆积在那里,而有些消费者处理很简单的消息,无事可做。为了我解决这个问题我们可以使用:

    channel.basicQos(1)

    关于消息确认的疑问

    "ACK机制可以保证消费者如果拿了队列的消息,处理出错了,那么队列中还有这个消息,仍然可以给下个机子来跑。但是,个人觉得一般处理消息出错都是因为代码逻辑或者出bug,即使 队列中后来仍然保留该消息,然后再给某一个消费者消费,不还是报错吗?
    Ps:当然,如果一个机子宕掉,消息还有,还可以给另外的机子用,这种情景下 ACK 是很有用的。但是个人觉得这种应该是少数情况吧。"
    官方介绍:
    https://www.rabbitmq.com/confirms.html
    consumer 做了一个ACK是为了告诉broker该条消息已经被消费,broker如果没有收到acknowledgment 会一直保存该信息,不会分发给其他的consumer,指导当前的consumer 发生异常断开连接 broker 才会将该条消息发送给其他的consumer.所以你的 consumer 代码必须能够处理各种异常,确保只要收到一条消息,最终一定能够执行一条 ACK / NACK。

    它使得每个Consumer在同一个时间点最多处理一个Message。在接收到该Consumer的ack前,不会将新的Message分发给它。这样就以保证等消费者处理完数据之后才会发送改消息给该消费者。但是这样也可能会使得所有的消息积压在rabbitmq中。

    关于上面这个疑问的解释:

     

    首先你弄错了 acknowledgment(翻译:承认,承认书,感谢;)的目的。acknowledgment 是 consumer 告诉 broker 当前消息是否成功 consume,至于 broker 如何处理 NACK,取决于 consumer 是否设置了 requeue:如果 requeue=False,那么 NACK 后 broker 是会删除消息的。看看 RabbitMQ 官方的解释。Consumer 做一个 ACK,是为了告诉 Broker 这条消息已经被成功处理了(transaction committed)。只要没收到 consumer 的 acknowledgment,broker 就会一直保存着这条消息(但不会 requeue,更不会分配给其他 consumer,直到当前 consumer 发生断开连接之类的异常)。RabbitMQ 之所以是 guaranteed delivery,这是一个关键。换言之,你的 consumer 代码必须能够处理各种异常,确保只要收到一条消息,最终一定能够执行一条 ACK / NACK(当然也没人阻止你设置 no_ack=True,干脆不用 acknowledgment 机制,这个视业务需求而定)。

     

    个人理解:也就是说,并非当前consumer没有ask,broker就会把消息发送给下一个consumer,而是当前consumer故障无应答才会发送给下一个consumer,如果只是没有接收到ask,那么broker就会一直保存着这个消息等待应答,同时不再发送消息给这个consumer。这样才是正确的逻辑,我的理解,应该要避免autoAck,在处理完这个消息之后再ask,之后才会接收下一个消息进行处理,否则处理还没有完成就接收下一个消息,会造成消息在应用服务器挤压,而不是在消息队列挤压。所以正确的接收方法是,配置多个consumer,然后每个consumer处理完消息之后再ask。



    作者:roye9
    链接:http://www.jianshu.com/p/f63820fe2638
    來源:简书
    著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

  • 相关阅读:
    操作系统 第二章 进程管理
    操作系统 第一章 概述(补充)
    第六次博客作业——团队总结
    专题(十三)watch
    专题(十二)find 查找
    JVM 排查工具介绍(二)Memory Analyzer 堆内存分析工具
    Linux 学习笔记之(二)curl命令
    centos openjdk 11 安装软件包获取方式
    软件工程课程总结
    小黄衫!又一次?
  • 原文地址:https://www.cnblogs.com/xiaolang8762400/p/7470262.html
Copyright © 2011-2022 走看看