AMQP RabbitMQ消费者互相堵塞?

时间:2014-08-01 03:41:33

标签: c rabbitmq amqp librabbitmq

我编写了一个C(rabbitmq-c)工作者应用程序,该应用程序使用Python脚本(pika)发布的队列。

我有以下奇怪的行为,我似乎无法解决:

  1. 在将消息发布到队列之前启动所有工作程序按预期工作
  2. 在队列发布后启动1个工作符按预期工作
  3. 但是:在工作人员开始从队列中消费后启动其他工作人员意味着这些工作人员看不到队列中的任何消息(消息计数= 0),因此只是等待(尽管有很多意思消息仍在队列中)。杀死第一名工作人员会突然开始向所有其他(等待)消费者发送消息。
  4. 任何想法可能会发生什么?

    我已经尝试确保每个消费者拥有它自己的频道(这是必要的吗?)但仍然是相同的行为......

    这里是消费者(工人)的代码:

    conn = amqp_new_connection();
    sock = (amqp_socket_t *)(uint64_t)amqp_tcp_socket_new(conn);
    amqp_socket_open(sock, "localhost", 5672);
    amqp_login(conn,
               "/",
               0,
               131072,
               0,
               AMQP_SASL_METHOD_PLAIN,
               "guest",
               "guest");
    
    if (amqp_channel_open(conn, chan) == NULL)
        LOG_ERR(" [!] Failed to open amqp channel!\n");
    
    if ((q = amqp_queue_declare(conn,
                                chan,
                                amqp_cstring_bytes("ranges"),
                                0,
                                0,
                                0,
                                0,
                                amqp_empty_table)) == NULL)
        LOG_ERR(" [!] Failed to declare queue!\n");
    
    LOG_INFO(" [x] Queue (message count = %d)\n", q->message_count);
    
    amqp_queue_bind(conn, chan, amqp_cstring_bytes("ranges"), amqp_empty_bytes, amqp_empty_table);
    amqp_basic_consume(conn, chan, amqp_cstring_bytes("ranges"), amqp_empty_bytes, 0, 0, 0, amqp_empty_table);
    
    while(1) {
        amqp_maybe_release_buffers(conn);
        amqp_consume_message(conn, &e, NULL, 0);
    
        {
            int n;
            amqp_frame_t f;
            unsigned char buf[8];
            unsigned char *pbuf = buf;
    
            amqp_simple_wait_frame(conn, &f);       // METHOD frame
            amqp_simple_wait_frame(conn, &f);       // HEADER frame
    
            n = f.payload.properties.body_size;
            if (n != sizeof(range_buf))
                LOG_ERR(" [!] Invalid message size!");
    
            while (n) {
                amqp_simple_wait_frame(conn, &f);   // BODY frame
                memcpy(pbuf,
                       f.payload.body_fragment.bytes,
                       f.payload.body_fragment.len);
                n -= f.payload.body_fragment.len;
                pbuf += f.payload.body_fragment.len;
            }
    
            // do something with buf
    
            LOG_INFO(" [x] Message recevied from queue\n");
        }
    
        amqp_destroy_envelope(&e);
    
        amqp_maybe_release_buffers(conn);
    }
    

2 个答案:

答案 0 :(得分:4)

此处的问题很可能是您的消费者在启动时预先获取所有消息。这是RabbitMQ的默认行为,但您可以减少消费者预先提取的消息数量,以便更好地将工作负载分散到多个工作者。

这仅仅意味着一个或多个消费者将收集所有消息,并且不为新消费者留下任何消息。

如果您将qos应用于您的消费者并将预取限制为10条消息。消费者只会排出10条第一条消息,而新消费者可以收拾残局。

您正在寻找实现此功能的功能称为amqp_basic_qos,此外,您还可以阅读有关使用预取here的更多信息。

答案 1 :(得分:0)

这可能对您有所帮助

消息确认

执行任务可能需要几秒钟。你可能想知道如果其中一个消费者开始一项长期任务并且只是部分完成而死亡会发生什么。使用我们当前的代码,一旦RabbitMQ向客户发送消息,它立即将其从内存中删除。在这种情况下,如果你杀死一个工人,我们将丢失它刚刚处理的消息。我们还会丢失所有发送给这个特定工作者但尚未处理的消息。

但我们不想失去任何任务。如果工人死亡,我们希望将任务交给另一名工人。

为了确保消息永不丢失,RabbitMQ支持消息确认。从消费者那里发回一个ack(nowledgement)告诉RabbitMQ已收到,处理了一条特定的消息,并且RabbitMQ可以自由删除它。

如果消费者在没有发送确认消息的情况下死亡,RabbitMQ将理解消息未被完全处理并将其重新发送给另一个消费者。这样你就可以确保没有消息丢失,即使工人偶尔会死亡。

没有任何消息超时;只有当工作者连接死亡时,RabbitMQ才会重新传递消息。即使处理消息需要非常长的时间,也没关系。

默认情况下,消息确认已开启。