使用C#和IBM MQSeries进行消息发布订阅
2011-6-22 创建
2011-8-16 在持久预定后,在关闭时删除预定信息
MQ提供了一个使用C#进行发布订阅到例程MQPubSubSample.cs,首先对其中两个重要的选项进行解释,这两个选项的组合影响了发布订阅中的主题打开方式和消息的订阅接收。
1,托管/非托管 (managed/unmanaged):
托管使用MQ的动态队列存放订阅信息,非托管由用户指定队列保存
2,持久/非持久(durable/undurable):
消息订阅分为非持久订阅(non-durable subscription)和持久订阅(durable subscrip-tion),非持久订阅只有当客户端处于激活状态,也就是和MQ队列管理器保持连接状态才能收到
发送到某个主题的消息,而当客户端处于离线状态,这个时间段发到主题的消息将会丢失,永远不会收到。持久订阅时,客户端向MQ队列管理器注册一个识别自己身份的ID,当这个客户端处于离线时,MQ队列管理器会为这个ID 保存所有发送到主题的消息,当客户再次连接时,会根据自己的ID 得到所有当自己处于离线时发送到主题的消息。缺省为非持久。
根据MQ文档,下面4种情形下订阅才会被清除.
1,Connection loss with a non-durable
2,MQCLOSE with MQCO_REMOVE_SUB
3,Administrative DELETE SUB command
4,订阅过期,在设置订阅时指定了订阅期限
因此总的来说,非持久订阅可以得到在连接期间发布的所有信息,而持久订阅可以得到注册订阅和取消订阅之间发布的所有信息。该持久性与消息的持久性不同,因此与发布者无关。
以下代码说明不同的选项使用不同的打开方式
int openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_MANAGED | MQC.MQSO_NON_DURABLE;
string topicName = "BomStatus";
string topicObject = null;
int destType = MQC.MQOT_TOPIC;
string subName = "WUHAN_" + "Sample1";
MQTopic destForGet = null;
MQDestination unmanagedDest = null;
string unmanagedDestName = DEFAULT_QUEUE;
int unmanagedDestOpenOptions = MQC.MQOO_INPUT_EXCLUSIVE | MQC.MQOO_FAIL_IF_QUIESCING;
//Switch flag to test
bool managed = true;
bool durable = true;
if (managed && durable) //托管持久
{
openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_RESUME | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_MANAGED | MQC.MQSO_DURABLE;
subName = "WUHAN_" + "Sample1";
destForGet = mqQMgr.AccessTopic(topicName, topicObject, openOptionsForGet, null, subName);
destForGet.SubscriptionReference.CloseOptions = MQC.MQCO_REMOVE_SUB;
}
else if (!managed && durable) //非托管持久
{
openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_DURABLE;
subName = "WUHAN_" + "Sample2";
unmanagedDestName = DEFAULT_QUEUE;
unmanagedDestOpenOptions = MQC.MQOO_INPUT_EXCLUSIVE | MQC.MQOO_FAIL_IF_QUIESCING;
unmanagedDest = mqQMgr.AccessQueue(unmanagedDestName, unmanagedDestOpenOptions);
destForGet = mqQMgr.AccessTopic(unmanagedDest, topicName, topicObject, openOptionsForGet, null, subName);
destForGet.SubscriptionReference.CloseOptions = MQC.MQCO_REMOVE_SUB;
}
else if (managed && !durable) //托管非持久
{
openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_MANAGED | MQC.MQSO_NON_DURABLE;
destForGet = mqQMgr.AccessTopic(topicName, topicObject, MQC.MQTOPIC_OPEN_AS_SUBSCRIPTION, openOptionsForGet);
}
else if (!managed && !durable) //非托管非持久
{
openOptionsForGet = MQC.MQSO_CREATE | MQC.MQSO_FAIL_IF_QUIESCING | MQC.MQSO_NON_DURABLE;
unmanagedDestName = DEFAULT_QUEUE;
unmanagedDestOpenOptions = MQC.MQOO_INPUT_EXCLUSIVE | MQC.MQOO_FAIL_IF_QUIESCING;
unmanagedDest = mqQMgr.AccessQueue(unmanagedDestName, unmanagedDestOpenOptions);
destForGet = mqQMgr.AccessTopic(unmanagedDest, topicName, topicObject, openOptionsForGet);
}
在设置这些参数后,可以开始发布和订阅
//发布
messageForPut = new MQMessage();
String publishStr = "Publish test data at " + DateTime.Now.ToString();
messageForPut.WriteString(publishStr);
mqQMgr.Put(destType, topicObject, null, topicName, messageForPut);
MessageBox.Show("Publish message success, message is: " + publishStr);
//订阅
try
{
messageForGet = new MQMessage();
destForGet.Get(messageForGet);
String messageDataFromGet = messageForGet.ReadLine();
MessageBox.Show("Subscribe message is: " + messageDataFromGet);
}
catch (MQException mqE)
{
MessageBox.Show("MQException caught. " + mqE.Message);
}
在使用持久后,MQ资源管理器中预定中出现预定记录,如下图所示:
在使用MQ例程时,在持久情况下,第二次打开主题(AccessTopic)时,程序报主题已经存在的错误MQRC_SUB_ALREADY_EXISTS,(REASON CODE 2432)
需要在打开时使用MQC.MQSO_CREATE | MQC.MQSO_RESUME选项避免该错误
用户可以使用下面的用例体会持久和非持久的区别
1,将durable设为false,发布一个新消息,不断开连接接收订阅,应该可以得到该发布消息
2,将durable设为false,发布一个新消息,断开连接后接收订阅,应该收不到该发布消息
3,将durable设为true,发布一个新消息,断开连接后,重新使用相同的SUB进行连接,接收订阅,应该可以得到该发布消息