消费者回调:哪个队列为空?

时间:2018-05-09 10:12:15

标签: ibm-mq

我正在使用MQCB函数为我正在阅读的队列添加消息使用者回调函数。我正在尝试从同一连接上的两个队列中读取,并且在接收消息时似乎工作正常:我的回调函数获取接收消息的队列的对象句柄。

但是,当我收到MQRC_NO_MSG_AVAILABLE事件时(因为我在我的消费者身上设置了MQGMO_WAIT),对象句柄是MQHO_NONE,所以我无法分辨事件引用的是哪个队列至。我可以通过将对象句柄放在回调上下文中来解决这个问题,但这是应该完成的吗?或者我错过了一些明显的东西?

我正在Linux上使用C客户端库版本8.0.0.5连接到运行版本8.0.0.2的队列管理器,同样在Linux上。这是我的示例程序的输出,显示对象句柄为0:

Opened queue 'AMQ.5A55ED982D616602                            ' with handle 101
Opened queue 'AMQ.5A55ED982D616603                            ' with handle 102
Completion code MQCC_FAILED, reason MQRC_NO_MSG_AVAILABLE, object handle 0
Completion code MQCC_FAILED, reason MQRC_NO_MSG_AVAILABLE, object handle 0

程序本身:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <cmqc.h>
#include <cmqxc.h>
#include <cmqstrc.h>

void my_message_consumer(MQHCONN, PMQMD, PMQGMO, PMQVOID, PMQCBC);

volatile unsigned events_received = 0;

void
check_fail(const char *action, MQLONG comp_code, MQLONG reason)
{
  if (comp_code != MQCC_OK) {
    fprintf(stderr, "%s failed with %s %s\n",
            action, MQCC_STR(comp_code), MQRC_STR(reason));
    exit(1);
  }
}

int
main()
{
  MQHCONN hconn;
  MQHOBJ hobj1, hobj2;
  MQOD od = {MQOD_DEFAULT};
  char queue_name[MQ_Q_NAME_LENGTH + 1];
  MQLONG c, r;

  MQCONN("", &hconn, &c, &r);
  check_fail("MQCONN", c, r);

  /* Open two dynamic queues */
  strcpy(od.ObjectName, "SYSTEM.DEFAULT.MODEL.QUEUE");
  MQOPEN(hconn, &od, MQOO_INPUT_EXCLUSIVE, &hobj1, &c, &r);
  check_fail("MQOPEN", c, r);

  strncpy(queue_name, od.ObjectName, MQ_Q_NAME_LENGTH);
  queue_name[MQ_Q_NAME_LENGTH] = '\0';
  printf("Opened queue '%48s' with handle %d\n", queue_name, hobj1);

  strcpy(od.ObjectName, "SYSTEM.DEFAULT.MODEL.QUEUE");
  MQOPEN(hconn, &od, MQOO_INPUT_EXCLUSIVE, &hobj2, &c, &r);
  check_fail("MQOPEN", c, r);

  strncpy(queue_name, od.ObjectName, MQ_Q_NAME_LENGTH);
  queue_name[MQ_Q_NAME_LENGTH] = '\0';
  printf("Opened queue '%48s' with handle %d\n", queue_name, hobj2);

  /* Add a callback with zero WaitInterval for both queues */
  MQMD md = {MQMD_DEFAULT};
  MQGMO gmo = {MQGMO_DEFAULT};
  MQCBD cbd = {MQCBD_DEFAULT};
  gmo.Options = MQGMO_NO_SYNCPOINT | MQGMO_WAIT;
  gmo.WaitInterval = 0;
  cbd.CallbackType = MQCBT_MESSAGE_CONSUMER;
  cbd.CallbackFunction = &my_message_consumer;

  MQCB(hconn, MQOP_REGISTER, &cbd, hobj1, &md, &gmo, &c, &r);
  check_fail("MQCB", c, r);
  MQCB(hconn, MQOP_REGISTER, &cbd, hobj2, &md, &gmo, &c, &r);
  check_fail("MQCB", c, r);

  /* Start consuming */
  MQCTLO ctlo = {MQCTLO_DEFAULT};
  MQCTL(hconn, MQOP_START, &ctlo, &c, &r);
  check_fail("MQCTL start", c, r);

  /* Wait until events received */
  while (events_received < 2)
    sleep(1);

  return 0;
}

void
my_message_consumer(MQHCONN hconn, PMQMD md, PMQGMO gmo,
                    PMQVOID buffer, PMQCBC context)
{
  printf("Completion code %s, reason %s, object handle %d\n",
     MQCC_STR(context->CompCode), MQRC_STR(context->Reason),
     context->Hobj);
  events_received++;
}

用以下内容编译:

gcc -o mq-no-msg mq-no-msg.c -g -Wall -I/opt/mqm/inc -L/opt/mqm/lib64 -lmqic_r -Wl,-rpath=/opt/mqm/lib64

并在运行之前设置MQSERVER环境变量。

2 个答案:

答案 0 :(得分:1)

我从几个来源找到了关于这个主题的信息,这些信息总结在一起描绘了整个画面(不幸的是,IBM的MQ KC并没有很好地记录这一点,至少可以说)。

  1. 在Capitalware的MQ技术大会v2.0.1.3上,Morag Hughson做了一个演讲WebSphere MQ V7 Enhanced Application Programming,其中有一些有用的信息。

    第六页说明:

      
        
    • 也可以通过将CallType设置为MQCBCT_EVENT_CALL来调用您的消息使用者(这也是事件处理程序可以的唯一方式)   所谓的)。消息使用者将获得相关的事件   例如,MQRC_GET_INHIBITED消耗的队列   而事件处理程序获取连接范围的事件。
    •   
  2. 在IBM MQ v8 KC页面MQCBC - Callback context > Fields for MQCBC > Hobj (MQHOBJ)中,它指出:

      

    对于事件处理程序,此值为MQHO_NONE

  3. IBM提供的示例amqscbf0.c还演示了检查pContext->CallType中的MessageConsumer,如果是MQCBCT_EVENT_CALL,则会打印出原因,如果是类型的话MQCBCT_MSG_REMOVED它打印消息。

  4. 根据上述信息,您看到的行为似乎是预期的行为。

    建议的解决方法是使用可用于确定事件所引用的队列的唯一值来设置每个队列的CallbackArea字段MQCBD enter image description here

    在Morag的演讲和第34页的第4页上,WebSphere MQ V7增强型应用程序编程&#34;它陈述如下:

      
        
    • MQGMO_WAIT与MQGMO.WaitInterval = 0一样操作   当MQGET使用时,MQGMO_NO_WAIT,但是在   异步消费者,我们希望避免消费者在一个   在这种情况下繁忙的循环,所以它更像是一个止回器标记   显示何时到达一批消息的结尾。
    •   

    在同一页面的表格中,它在MQGMO_WAIT with MQGMO.WaitInterval = 0的异步消耗列下显示:

      

    如果刚刚启动或有一个,只能使用MQRC_NO_MSGS_AVAILABLE调用   自2033年以来的消息

    您的消费者不会不断收到通知它没有消息在队列中的事件。仅当首次启动回叫时和/或每次从队列中读取(GET)所有消息之后没有消息时,才会生成事件。从本质上讲,它可以让您知道在读取至少一条消息后,当前没有更多消息可用。如果您希望批量消息并希望在从队列中读取批处理中的所有消息后执行某些操作,这可能很有用。

      
        
    • 请注意,MQGMO_NO_WAIT和MQGMO_WAIT的WaitInterval为   传递给MQGET时,MQWI_UNLIMITED有很大的不同   MQCB调用它们的行为是一样的。消费者只会   传递消息和事件,它永远不会传递原因代码   表示没有消息。有效地,MQGMO_NO_WAIT将被视为   无限期的等待。这是为了防止消费者无休止地   使用无消息原因代码调用。
    •   

    如果您确实不需要MQRC_NO_MSG_AVAILABLE个事件消息,那么MQGMO_NO_WAIT可能就是您的选择。

答案 1 :(得分:0)

如果您将MQRC_NO_MSG_AVAILABLE作为事件,则表示在您注册的任何队列中没有消息(符合您的条件,如果您指定了任何条件)。因此,在这种情况下,不需要为任何特定的HObj提供回调。