MQ 介绍及安装

介绍

MQ,简单地说就是消息队列,应用程序把消息放进队列里,等待其他的应用程序或自己把它读走,用于进程间通信,并且可以像 socket 那样用于不同主机间的进程间通信。它有四个重要的概念:

队列管理器
用来管理队列。

队列
用来存放消息。

消息
就是要进行存储与传递的消息。

通道
队列管理器之间传递消息的管道。

安装

到这里下载 MQ for 64-bit linux。下载完解压后,先运行 MQ 的许可证程序:

./mqlisence.sh

然后安装你想要装的组件:

rpm -U MQSeriesXXXXXXX.rpm

MQ 基本操作

创建队列管理器

crtmqm -q qm_name

启动队列管理器

strmqm qm_name

查看队列管理器

dspmq -m qm_name

创建队列
创建队列不像创建队列管理器那样有直接的命令可以使用,而是要先运行命令:

runmqsc

再执行语句:

def ql(QUEUE1)

也可以把语句写到一个文件里面,再把这个文件当作 runmqsc 的输入:

runmqsc < statement.txt

如果在执行 strmqm qm_name 之后再运行 runmqsc,就表示是对 qm_name 这个队列管理器进行操作,如果要想对其他的队列管理器操作,则要在 runmqsc 后面加队列管理器名:

runmqsc qm_name

队列有很多属性,具体的细节可查看 IBM 的手册。

停止队列管理器

endmqm qm_name // 受控停止
endmqm -i qm_name // 立即停止
endmqm -p qm_name // 强制停止

删除队列管理器

dltmqm qm_name

错误与及解决办法
我在创建队列管理器时出现下以提示信息:

The queue manager is associated with installation ‘Installation1’. AMQ6024: Insufficient resources are available to complete a system request.

错误信息在 /var/mqm/errors/ 可以查看到:

cat AMQ7116.0.FDC
Probe Description :- AMQ6024: Insufficient resources are available to 
 complete a system request. 
 FDCSequenceNumber :- 0 
 Arith1 :- 18446744073709551615 (0xffffffffffffffff) 
 Arith2 :- 22 (0x16) 
 Comment1 :- Failed to get memory segment: shmget(0x00000000, 
 73834496) [rc=-1 errno=22] Invalid argument 
 Comment2 :- Invalid argument 
 Comment3 :- Configure kernel (for example, shmmax) to allow a 
 shared memory segment of at least 73834496 bytes

这个问题可以这样解决:

sudo sysctl -w kernel.shmmax=73834496

sysctl 可以动态修改内核参数而不用重启。

MQ client 封装库

MQI

Message Queue Interface 一共包含 13 个函数。它们分别是(可以在 cmqc.h 查看它们的原型):
MQCONN 和 MQCONNX
连接队列管理器。两者唯一的区别是 MQCONNX 比 MQCONN 多了一个连接选项参数。

MQDISC
断开与队列管理器的连接。

MQOPEN 和 MQCLOSE
每一个队列都必须单独调用 MQOPEN 来打开。并且这两者必须配对出现。

MQPUT
把消息放到一个打开的队列中。

MQPUT1
也是把消息放到一个打开的队列中。不过它比 MQPUT 多执行了两个函数:MQOPEN 和 MQCLOSE。

MQGET
从一个打开的队列中读取或浏览信息。在它的参数当中,有一个 MQGMO 的参数很重要,可以用来指示该操作是读取还是浏览的,如果是读取的,则从队列中读走消息;如果是浏览的,则消息还留在队列中。因为要提供一个 buffer 以及该 buffer 的长度给 MQGET 用来存放读取的消息,如果消息太长,buffer 存放不下,也可以设定该参数告诉 MQGET 到底是返回部分消息还是操作失败。

MQBEGIN

The MQBEGIN MQI function is only used when a queue manager has been configured to coordinate global units of work involing database products.

MQCMIT 和 MQBACK
MQCMIT 用来提交当前 unit of work。对局部的 unit of work 而言,仅仅包括 MQ 的操作:从应用程序连接到队列管理器或才最后一次执行 MQCMIT/MQBACK 以来的所有在同步点之下执行的 MQGET/MQPUT 操作。对全局的 units of work 而言,它包括数据库操作和 MQ 操作。
MQBACK 用来回滚当前 unit of work。这些操作和 MQCMIT 描述的操作相同。MQBACK 会导致所有的操作被撤销,put 到队列的消息会被移除掉,从队列 get 的消息则会重新返回到队列中。

MQINQ 和 MQSET
MQINQ 查询属性,而 MQSET 则设置属性。

以上每一个函数调用都返回一个 completion code 和一个 reason code。completion code 只有三种值:
1. MQCC_OK: 表示函数调用完全成功。
2. MQCC_WARNING: 表示函数调用部分完全。可以查看 reason code 了解更多的细节。
3. MQCC_FAILED: 表示函数调用失败。可以查看 reason code 了解更多的细节。
而 reason code 的值则有很多。具体的细节的可查看reason code list

mqclient

因为工作的需要,所以自己封装了 MQI 13 个函数中的部分接口。目前 mqclient(这里可以查看) 库只提供了 6 个接口:



int mqclient_connect();
int mqclient_disconnect();
int mqclient_get_open();
int mqclient_get();
int mqclient_put_open();
int mqclient_put();



一开始是把两个 open() 函数放在 put() 和 get() 里面的,但考虑到每 get() 一次都要 open() 和 close() 一次,会不会不够高效呢?如果把两个 open() 函数独立出来,那只调用一次 open() 后就可以连续的 get() 了。所以就把它们独立出来。而 close() 则放在 disconnect() 里面。

在使用的过程中发现,将 mqclient_get_open() 和 mqclient_put_open() 独立出来,会导致当使用同一个 struct mqclient 结构时,如果要操作多条写或读队列,则只有最后调用这两个接口的那条队列可以操作成功。例如有两条队列 Q1 Q2 都要进行 put() 操作,



mqclient_put_open(&mqc, "Q1", NULL);
mqclient_put_open(&mqc, "Q2", NULL);
mqclient_put(&mqc, "hello", strlen("hello")); // put to Q1
mqclient_put(&mqc, "world", strlen("world")); // put to Q2



但实际所有的操作都是对 Q2 进行的。当然可以先操作完 Q1 再操作 Q2。这里只是把一条消息添加到 Q1 和 Q2 上,但如果有多条消息分别要添加到 Q1 和 Q2 上呢,并且逻辑一定要先往 Q1 put 一条消息后再往 Q2 put 一条消息。这时就不得不要再添加一个 struct mqclient 结构了。然后代码就要写成:



mqclient_put_open(&mqc1, "Q1", NULL);
mqclient_put_open(&mqc2, "Q2", NULL);

{
... // put msgs to Q1 and Q2 loop
}