【问题标题】:Consumer callback: which queue is empty?消费者回调:哪个队列是空的?
【发布时间】:2018-05-12 06:46:29
【问题描述】:

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

但是,当我收到一个MQRC_NO_MSG_AVAILABLE 事件时(因为我在我的消费者上设置了MQGMO_WAIT),对象句柄是MQHO_NONE,所以我不知道该事件指的是哪个队列。我可以通过将对象句柄放在回调上下文中来解决这个问题,但它应该这样做吗?还是我在这里遗漏了一些明显的东西?

我正在使用 C 客户端库版本 8.0.0.5 连接到在 Linux 上运行版本 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 环境变量。

【问题讨论】:

  • 是的,这让我很困惑。我将CallbackType 指定为“消息使用者”而不是“事件处理程序”,并且该页面显示“为影响整个消息使用者环境的条件调用事件处理程序”(而不是单个队列为空) - 那为什么在这种情况下,我的消息使用者是否充当事件处理程序?...
  • 是的,我想我真的不需要“无消息”事件 - 如果我需要它,答案似乎是接受它的工作方式,并且将我需要的内容存储在 CallbackArea 中。谢谢!
  • 将我的 cmets 卷入答案中。
  • 我用更多细节更新了我的答案,并添加了一个用例,您可能希望在您的消费者中接收 MQRC_NO_MSG_AVAILABLE 事件消息,但您发现默认情况下,如果您注册了多个消费者,使用 CallbackArea 可能是解决此问题的一种方法。
  • 如果您有权打开 PMR,可能值得向 IBM 澄清。 KC 关于Hobj 值、For an event handler, this value is MQHO_NONE 的状态,并且正如您正确指出的那样,您没有注册事件处理程序。我也没有在 KC 中找到将事件消息发送给消费者的任何直接引用,但是我引用的发送给消费者的状态事件的 IBM 演示只是那些与它正在消费的队列相关的事件。在我看来,MQCBC 应该包含关联的 Hobj 而不是 MQHO_NONE,这似乎是合理的。

标签: ibm-mq


【解决方案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 还演示了检查MessageConsumer 中的pContext-&gt;CallType,如果它是MQCBCT_EVENT_CALL 类型,它会打印原因,如果它是MQCBCT_MSG_REMOVED 类型,它会打印消息。


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


建议的解决方法是为每个队列的MQCBDCallbackArea 字段设置一个唯一值,您可以使用该值来确定事件所指的队列。


在 Morag 的演示文稿“WebSphere MQ V7 增强应用程序编程”的第四页上,它陈述了以下内容:

  • MQGMO_WAIT 与 MQGMO.WaitInterval = 0 的操作就像 MQGMO_NO_WAIT 在 MQGET 上使用时,但在 我们希望避免消费者轮询异步消费者 在这种情况下是繁忙的循环,所以它更像是一个支持标记 显示一批消息何时结束。

在同一页的表中,它在 MQGMO_WAIT with MQGMO.WaitInterval = 0 的异步消费列下指出:

仅在刚刚启动或已使用 MQRC_NO_MSGS_AVAILABLE 时调用 自去年 2033 年以来的消息

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

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

如果您真的不需要MQRC_NO_MSG_AVAILABLE 事件消息,那么MQGMO_NO_WAIT 可能是要走的路。

【讨论】:

    【解决方案2】:

    当您收到 MQRC_NO_MSG_AVAILABLE 作为事件时,这意味着在您注册的 任何 个队列上没有消息(匹配您的条件,如果您指定了任何条件)。因此,在这种情况下,无需为回调提供任何特定的 HObj。

    【讨论】:

    • 因此,如果您获得MQRC_NO_MSG_AVAILABLE 返回码或类似MQRC_GET_INHIBITED 之类的其他内容,应用程序如何知道它与哪个队列相关,以便在需要时采取适当的措施? OP 有一个有效的观点,即这似乎与预期的行为不符。
    • no-msg-available 与您注册的所有队列有关,而不仅仅是一个。关于get-inhibited,这涉及到一个队列。
    • 您能否确认发送给带有MQRC_GET_INHIBITED 的消费者的MQCBCT_EVENT_CALL 消息已将Hobj 设置为一个值,但是当发送给带有MQRC_NO_MSG_AVAILABLE 的消费者的MQCBCT_EVENT_CALL 消息时, Hobj 设置为 MQHO_NONE?您是否观察到了这种行为?根据 IBM 的介绍,与特定队列无关的 MQCBCT_EVENT_CALL 消息将发送到已注册的事件处理程序,在这种情况下,OP 没有事件处理程序,但当队列为空时,每个注册的消费者都会收到 @987654331 @.
    • 根据 IBM 的介绍,如果 MQRC_NO_MSG_AVAILABLE 不是特定于每个队列,则不应将其发送给已注册的使用者。我也相信但尚未测试过,如果消费者收到MQRC_NO_MSG_AVAILABLE,它也会将Hobj 设置为MQHO_NONE
    • 更改 amqscbf0.c 以在 MessageConsumer 函数顶部转储 pContext->Hobj,您将看到被禁止 (rc=2016) 和重新启用 (rc=2494) 的 hobj你输入 ALTER QL() GET(DISABLED) 或 GET(ENABLED)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-04-17
    • 1970-01-01
    相关资源
    最近更新 更多