【问题标题】:Access BOTHRESH value of IBM MQ using JMS API使用 JMS API 访问 IBM MQ 的 BOTHRESH 值
【发布时间】:2021-03-26 21:51:37
【问题描述】:

我正在使用带有 mq-jms-spring-boot-starter 的 Spring Boot 创建一个 JMS 侦听器应用程序,该应用程序从队列中读取消息,对其进行处理并将消息转发到另一个队列。

如果出现有毒消息情况,我会尝试生成警报。但是,为了不对同一消息生成多个警报,我正在考虑将 JMSXDeliveryCountBOTHRESH 值进行比较,并在发送到 BOQ 之前的最后一次重新交付中生成警报。

为源队列配置BOTHRESHBOQNAME

    @JmsListener(destination = "${sourceQueue}")
    public void processMessages(Message message) {
        TextMessage msg = (TextMessage) message;
        int boThresh;
        int redeliveryCount;
        try {
            boThresh = message.getIntProperty("<WHAT COMES HERE>");
            redeliveryCount = message.getIntProperty("JMSXDeliveryCount");
            String processedMessage = this.processMessage(message);
            this.forwardMessage("destinationQueue", processedMessage);
        } catch (Exception e) {
            if (redeliveryCount >= boThresh) {
               //generate alert here
            }
        }
    }

我应该如何在这里获得BOTHRESH 的值?有可能吗?我尝试使用getPropertyNames() 方法获取所有可用属性,以下是我看到的所有属性。

  • JMS_IBM_Format
  • JMS_IBM_PutDate
  • JMS_IBM_Character_Set
  • JMSXDeliveryCount
  • JMS_IBM_MsgType
  • JMSXUserID
  • JMS_IBM_Encoding
  • JMS_IBM_PutTime
  • JMSXAppID
  • JMS_IBM_PutApplType

【问题讨论】:

  • 如果您为队列定义了 BOTHRESH 和 BOQNAME 属性,MQ JMS 会自动将有害消息(主要是在传递计数超过 BOTHRESH 时)移动到 BOQNAME。
  • @Shashi,是的,没错。但我需要在将有害消息移动到 BOQNAME 之前从 Java 应用程序生成警报。而且我只想为一条消息生成一次此警报。
  • BOTHRESH 和 BOQNAME 或 IBM MQ 特定队列属性,它们不是 JMS 规范的一部分。因此,您无法使用 JMS API 获取这些详细信息。为此,您将需要 IBM MQ 提供的 PCF 类。为什么不在消息到达 BOQ 后生成警报?
  • @Shashi 我明白了。那时我将不得不考虑另一种方法。 BOQ 可能包含其他不相关的消息,因此可能是不可能的。感谢您的评论。

标签: java spring-boot websphere ibm-mq spring-jms


【解决方案1】:

这样做可以,但代码确实需要管理员访问管理员通道,这可能不是客户端应用程序的最佳选择。

配置

import com.ibm.mq.*;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.ibm.mq.constants.CMQC;

import java.util.Hashtable;

@Configuration
public class MQConfiguration {
    protected final Log logger = LogFactory.getLog(getClass());

    @Value("${ibm.mq.queueManager:QM1}")
    public String qMgrName;

    @Value("${app.mq.admin.channel:DEV.ADMIN.SVRCONN}")
    private String adminChannel;

    @Value("${app.mq.host:localhost}")
    private String host;

    @Value("${app.mq.host.port:1414}")
    private int port;

    @Value("${app.mq.adminuser:admin}")
    private String adminUser;

    @Value("${app.mq.adminpassword:passw0rd}")
    private String password;


    @Bean
    public MQQueueManager mqQueueManager() {
        try {
            Hashtable<String,Object> connectionProperties = new Hashtable<String,Object>();

            connectionProperties.put(CMQC.CHANNEL_PROPERTY, adminChannel);
            connectionProperties.put(CMQC.HOST_NAME_PROPERTY, host);
            connectionProperties.put(CMQC.PORT_PROPERTY, port);
            connectionProperties.put(CMQC.USER_ID_PROPERTY, adminUser);
            connectionProperties.put(CMQC.PASSWORD_PROPERTY, password);

            return new MQQueueManager(qMgrName, connectionProperties);

        } catch (MQException e) {
            logger.warn("MQException obtaining MQQueueManager");
            logger.warn(e.getMessage());
        }

        return null;
    }
    
}


获取队列的回退阈值


import com.ibm.mq.*;
import com.ibm.mq.constants.CMQC;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

@Component
public class Runner {
    protected final Log logger = LogFactory.getLog(getClass());

    @Value("${app.mq.queue:DEV.QUEUE.1}")
    private String queueName = "";

    private final MQQueueManager mqQueueManager;

    Runner(MQQueueManager mqQueueManager) {
        this.mqQueueManager = mqQueueManager;
    }

    @Bean
    CommandLineRunner init() {
        return (args) -> {
            logger.info("Determining Backout threshold");
            try {
                int[] selectors = {
                        CMQC.MQIA_BACKOUT_THRESHOLD,
                        CMQC.MQCA_BACKOUT_REQ_Q_NAME };
                int[] intAttrs = new int[1];
                byte[] charAttrs = new byte[MQC.MQ_Q_NAME_LENGTH];

                int openOptions = MQC.MQOO_INPUT_AS_Q_DEF | MQC.MQOO_INQUIRE | MQC.MQOO_SAVE_ALL_CONTEXT;
                MQQueue myQueue = mqQueueManager.accessQueue(queueName, openOptions, null, null, null);
                logger.info("Queue Obtained");

                MQManagedObject moMyQueue = (MQManagedObject) myQueue;
                moMyQueue.inquire(selectors, intAttrs, charAttrs);

                int boThresh = intAttrs[0];
                String backoutQname = new String(charAttrs);

                logger.info("Backout Threshold: " + boThresh);
                logger.info("Backout Queue: " + backoutQname);

            } catch (MQException e) {
                logger.warn("MQException Error obtaining threshold");
                logger.warn(e.getMessage());
            }
        };
    }
}

【讨论】:

    【解决方案2】:

    这听起来像是在混合可重试和不可重试错误处理。 如果您正在跟踪重新交付并需要发送警报,那么您可能不想设置 BOTHRESH 值,而是在客户端代码中对其进行管理。

    推荐的消费者错误处理模式:

    1. 如果消息无效(即.. 错误的 JSON 或 XML)立即移动到 DLQ。消息的质量永远不会提高,没有理由反复重试。

    2. 如果处理中的“下一步”停止(即数据库)拒绝传递并允许重新传递延迟和退出重试启动。这还有一个好处是允许队列中的其他消费者尝试处理消息和消除了一个消费者因持有消息而死路的问题。

    此外,请考虑使用客户端消费者代码进行监控和警报可能会出现问题,因为它结合了不同的功能。如果您的目标是跟踪无效消息,那么监控 DLQ 通常是一种更好的设计模式,它会从您的消费者代码中删除“监控”代码。

    【讨论】:

    • 嗨,马特。 BOQNAME/BOTHRESH 是 QLOCAL 上的参数,回退计数是消息的 MQMD 中的一个字段,但实际回退逻辑是 JMS 的 IBM MQ 类的一部分。有关更多详细信息,请参阅 IBM MQ 知识中心页面 Handling poison messages in IBM WebSphere MQ classes for JMS
    • 回退逻辑是 QMgr 端(又名服务器端)。它是相同的 MQI 或 JMS。如果客户端调用回滚或拒绝消息,QMgr 将处理重新传递并最终移动 BOQNAME(并添加 DLH 标头)。 MQMD.backoutCount 映射到 JMS 的 JMSXDeliveryCount 标头。
    • 请查看 IBM MQ 知识中心页面 DEFINE queues,这两个属性都声明“除了允许查询其值,队列管理器不会根据此属性的值执行任何操作。”
    • 在上面的 KC 页面中,BOQUEUE 继续说“用于 JMS 的 IBM MQ 类将一条消息传输到此队列,该消息被回退了最大次数。” BOQNAME 继续说“用于 JMS 的 IBM MQ 类使用该属性来确定返回消息的次数。” KC 页面 Poison messages in XMS 显示 XMS 也是如此。
    • JoshMc--感谢您指出这一点。这是我的疏忽。我将编辑我的回复以澄清。
    猜你喜欢
    • 2011-04-10
    • 2023-03-05
    • 2020-09-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-06-16
    • 2018-04-06
    • 1970-01-01
    相关资源
    最近更新 更多