Node.js中ibmmq调用GetDone后无法停止队列监听的问题
问题描述
我正在使用ibmmq模块,需求是通过CorrelId获取消息后停止监听队列。
以下是listen方法代码:
async listen(queue: string, messageId?: string, waitInterval?: number) { let mqmd = new mq.MQMD() let gmo = new mq.MQGMO() gmo.Options = this.MQC.MQGMO_NO_SYNCPOINT | this.MQC.MQGMO_WAIT | this.MQC.MQGMO_CONVERT | this.MQC.MQGMO_NO_PROPERTIES | this.MQC.MQGMO_FAIL_IF_QUIESCING gmo.MatchOptions = this.MQC.MQMO_MATCH_CORREL_ID mqmd.CorrelId = this.hexToBytes(messageId) gmo.WaitInterval = this.MQC.MQWI_UNLIMITED mq.Get(obj as mq.MQObject, mqmd, gmo, getCB) }
以及getCB回调函数:
getCB(err: mq.MQError, hObj: mq.MQObject, gmo: mq.MQGMO, mqmd: mq.MQMD, buf: Buffer, hConn: mq.MQQueueManager) { if (err) { // 省略错误处理逻辑 } else { // 省略消息处理逻辑 console.log('GetDone:', hObj) mq.GetDone(hObj, err => { console.log('GetDoneError:', err) }) } }
启动监听后,放入带对应CorrelId的消息,监听器能获取到消息,终端打印'GetDone'。但之后再放入同CorrelId的消息,仍能收到消息并触发错误:
GetDoneError: MQError: GetDone: MQCC = MQCC_FAILED [2] MQRC = MQRC_HOBJ_ERROR [2019] at Object.exports.GetDone (/home/apps/connector/node_modules/ibmmq/lib/mqi.js:2316:11) at MqiConnector.getCB (/home/apps/connector/src/wmq-mqi-connector.js:206:20) at /home/apps/connector/node_modules/ibmmq/lib/mqi.js:2263:14 at Object.<anonymous> (/home/apps/connector/node_modules/ffi-napi/lib/_foreign_function.js:115:9) { mqcc: 2, mqccstr: 'MQCC_FAILED', mqrc: 2019, mqrcstr: 'MQRC_HOBJ_ERROR', version: '1.0.0', verb: 'GetDone' }
调用GetDone后getCB的循环并未停止,只要发送同CorrelId的消息就能收到,且每次都会触发该错误,监听器仍在运行。请问我哪里操作有误?
问题分析与解决方案
错误原因
MQRC_HOBJ_ERROR本质:这个错误表示传入GetDone的队列句柄hObj已失效。因为使用mq.Get回调模式且gmo.Options包含MQGMO_WAIT时,IBMMQ底层会自动循环触发getCB;第一次处理消息后调用GetDone会终止当前等待循环并使句柄失效,但后续有匹配消息时,底层仍会尝试复用失效句柄触发回调,导致报错。- 缺少终止循环的正确逻辑:没有在回调中明确阻止下一次
mq.Get调用,也未正确关闭队列句柄。
修正方案
方案1:使用一次性获取模式(推荐,匹配“获取后停止监听”需求)
修改gmo.Options移除MQGMO_WAIT,改为一次性获取消息,无需持续监听:
async listen(queue: string, messageId?: string, waitInterval?: number) { let mqmd = new mq.MQMD() let gmo = new mq.MQGMO() // 移除MQGMO_WAIT,改为一次性获取 gmo.Options = this.MQC.MQGMO_NO_SYNCPOINT | this.MQC.MQGMO_CONVERT | this.MQC.MQGMO_NO_PROPERTIES | this.MQC.MQGMO_FAIL_IF_QUIESCING gmo.MatchOptions = this.MQC.MQMO_MATCH_CORREL_ID mqmd.CorrelId = this.hexToBytes(messageId) // 设置合理等待间隔,也可保留MQWI_UNLIMITED配合一次性模式 gmo.WaitInterval = waitInterval || this.MQC.MQWI_UNLIMITED mq.Get(obj as mq.MQObject, mqmd, gmo, getCB) }
回调中处理完消息后直接关闭队列句柄:
getCB(err: mq.MQError, hObj: mq.MQObject, gmo: mq.MQGMO, mqmd: mq.MQMD, buf: Buffer, hConn: mq.MQQueueManager) { if (err) { // 错误处理(如超时/无消息)时关闭句柄 mq.Close(hObj, 0, (closeErr) => { if (closeErr) console.error('关闭队列失败:', closeErr) }) } else { // 消息处理逻辑 console.log('获取到消息:', buf.toString()) // 关闭队列句柄,终止监听 mq.Close(hObj, 0, (closeErr) => { if (closeErr) console.error('关闭队列失败:', closeErr) else console.log('队列已关闭,监听停止') }) } }
方案2:在回调中明确终止循环(需保留队列句柄场景)
若需保留队列句柄但停止监听,需避免再次调用mq.Get,并正确使用GetDone:
getCB(err: mq.MQError, hObj: mq.MQObject, gmo: mq.MQGMO, mqmd: mq.MQMD, buf: Buffer, hConn: mq.MQQueueManager) { if (err) { // 错误处理 mq.GetDone(hObj, (doneErr) => { if (doneErr) console.error('GetDone失败:', doneErr) // 可选:关闭队列句柄 mq.Close(hObj, 0, (closeErr) => {}) }) } else { // 消息处理逻辑 console.log('获取到消息:', buf.toString()) // 调用GetDone终止等待循环 mq.GetDone(hObj, (doneErr) => { if (doneErr) console.error('GetDone失败:', doneErr) else { console.log('监听已停止') // 可选:关闭队列句柄 mq.Close(hObj, 0, (closeErr) => {}) } }) // 关键:不要在回调中再次调用mq.Get,否则会重启循环 } }
注意事项
- 句柄生命周期:队列句柄
hObj调用Close后失效,不能再用于任何操作(包括GetDone)。 - 回调循环机制:使用
MQGMO_WAIT时,IBMMQ会自动持续触发getCB,直到调用GetDone或关闭句柄,必须明确终止循环。 - 一次性获取场景:若需求是“获取到匹配消息后立即停止”,优先用方案1,直接关闭队列句柄避免无效回调。
内容的提问来源于stack exchange,提问作者Lena Leontieva
相关产品推荐
相关产品推荐

