JMS集成IBM Websphere MQ疑难杂症: 设置JMSMessageID无效, 无法映射为MQMD.MessageID

问题背景

客户系统使用ESB架构, 通信方式IBM MQ, 除了基础规范之外, 有一条约定是以MsgId作为同一笔交易的标识, 使用JMS集成时手动指定MsgId不生效, 导致请求方无法取回响应报文, 导致消息堆积

产生原因

为什么手动指定JMSMessageID无效

  • 首先IBM MQ分配的messageId是在调用MQPUT操作时设置到MQMD中的, 它是一个24字节二进制的id, 而消费者经过JMS接收到的消息的id是JMSMessageID, 这是一个48个字符的十六进制字符串, 同时带有前缀"ID:"
  • 但实际上, 很容易发现JMSMessageID实际上就是由MQMD.MessageId二进制转为十六进制同时拼接的前缀"ID:"的字符串
  • 实际上JMS是提供了设置JMSMessageID的方法的, javax.jms.Message#setJMSMessageID, 但是会发现, 作为生产者, 指定了JMSMessageID后, 消费者端接收到的始终是一个随机id, 即使根据上面的转换规则手动转换, 或者尝试使用原值覆盖, 都是无效的, 这是因为JMS的规范声明中, JMSMessageID必须由JMS提供程序设置, 因此在所有常规情况下, 这个设置方法实际是被忽略/无效的, 在send()之前设置到JMSMessageID的值都会被覆盖

解决方法

查阅文档发现, IBM MQ消息id是MQMD中的属性, 通过指定MQMD中JMS_IBM_MQMD_MsgId最终会覆盖JMSMessageID, 不过这样做意味着会破坏JMS的规范

Reading and writing the message descriptor from an IBM MQ classes for JMS application
Some IBM® MQ applications require specific values to be set in the MQMD of messages sent to them. IBM MQ classes for JMS provides message attributes that allow JMS applications to set MQMD fields and so enable JMS applications to “drive” IBM MQ applications.

具体实现

获取原MQMD.MessageId

想到两种方式

  • 在配置时设置队列的WMQ_MQMD_READ_ENABLED属性为true
    // 其他配置...
    mqQueueConnectionFactory.setBooleanProperty(WMQConstants.WMQ_MQMD_READ_ENABLED, true);
    
    javax.jms.Message消息体中获取
    byte[] msgId = (byte[]) message.getObjectProperty(JmsConstants.JMS_IBM_MQMD_MSGID);
    
  • 直接根据转换规则从JMSMessageID中获取
    Pattern hexJmsId = Pattern.compile("[A-Fa-f0-9]{48}")
    String hexId = ReUtil.getGroup0(hexJmsId, message.getJMSMessageID())
    
    转为byte[]
    // 48个十六进制字符同时带前缀"ID:"的jmsId转为24字节的二进制值
    byte[] binaryId = DatatypeConverter.parseHexBinary(hexId);
    

覆盖JMSMessageID

首先在配置时, 设置WMQ_MQMD_WRITE_ENABLEDtrue

// ...其他配置
mqQueueConnectionFactory.setBooleanProperty(WMQConstants.WMQ_MQMD_WRITE_ENABLED, true);

设置响应消息MQMD属性

message.setObjectProperty(JmsConstants.JMS_IBM_MQMD_MSGID, binaryId);

使用JmsTemplate发送时如果遇到WMQ_TARGET_CLIENT, WMQ_MQMD_READ_ENABLED, WMQ_MQMD_WRITE_ENABLED, WMQ_MQMD_MESSAGE_CONTEXT等属性不生效, 那就在发送时指定destinationName为:

"queue:///{你的目标队列名}?targetClient=1&mdMessageContext=2&mdWriteEnabled=true"
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值