2012-10-18 136 views
8

1.)從.net客戶端,我如何測試客戶端是否連接到服務器(即可以發送和接收)是的,我可以在try塊中發送消息並捕獲隨後的異常,但我希望能有一個更優雅的解決方案。如何獲得Websphere MQ連接狀態,以及如何重置連接:

2)如何打開,關閉和重新打開連接?在我試圖解決上面的問題1時,我發現如果我打開一個連接,然後調用connection.Close()我無法從連接工廠獲得另一個連接(請參閱下面的代碼片段)。我收到錯誤消息XMSCC0008

我正在使用非常標準的vanilla MQ配置。以下是我的客戶端如何連接:

ISession session = MQAccess.GetSession(MQAccess.Connection); 
IDestination destination = session.CreateTopic(SubTopicName); 
Consumer = MQAccess.GetConsumer(session, destination); 
Consumer.MessageListener = new MessageListener(HandleMQSubEvent); 
MQAccess.Connection.Start(); 

其中MQAccess是一個小工具類。

編輯的問題補充MQAccess代碼:

public static class MQAccess 
{ 
    public static readonly MQConfigurationSectionHandler ConfigSettings; 
    public static readonly IConnectionFactory ConnectionFactory; 

    private static readonly IConnection connection; 
    public static IConnection Connection 
    { 
     get { return connection; } 
    } 

    static MQAccess() 
    { 
     ConfigSettings = (MQConfigurationSectionHandler) 
      ConfigurationManager.GetSection("mq-configuration"); 

     XMSFactoryFactory factory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ); 
     ConnectionFactory = factory.CreateConnectionFactory(); 
     ConnectionFactory.SetStringProperty(XMSC.WMQ_HOST_NAME, ConfigSettings.Hostname); 
     ConnectionFactory.SetIntProperty(XMSC.WMQ_PORT, ConfigSettings.Port); 
     ConnectionFactory.SetStringProperty(XMSC.WMQ_CHANNEL, ConfigSettings.Channel); 

     if (ConfigSettings.QueueManager == string.Empty) 
     { 
      ConnectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, ""); 
     } 
     else 
     { 
      ConnectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, ConfigSettings.QueueManager); 
     } 

     connection = GetConnection(); 
    } 

    public static IConnection GetConnection() 
    { 
     return ConnectionFactory.CreateConnection(); 
    } 

    public static ISession GetSession(IConnection connection) 
    { 
     return connection.CreateSession(false, AcknowledgeMode.AutoAcknowledge); 
    } 

    public static IMessageProducer GetProducer(ISession session, IDestination destination) 
    { 
     return session.CreateProducer(destination); 
    } 

    public static IMessageConsumer GetConsumer(ISession session, IDestination destination) 
    { 
     return session.CreateConsumer(destination); 
    } 

    public static void MQPub(string TopicURI, string message) 
    { 
     using (var session = GetSession(Connection)) 
     { 
      using (var destination = session.CreateTopic(TopicURI)) 
      { 
       using (var producer = GetProducer(session, destination)) 
       { 
        producer.Send(session.CreateTextMessage(message)); 
       } 
      } 
     } 
    } 

    public static void MQPub(string TopicURI, IEnumerable<string> messages) 
    { 
     using (var session = GetSession(Connection)) 
     { 
      using (var destination = session.CreateTopic(TopicURI)) 
      { 
       using (var producer = GetProducer(session, destination)) 
       { 
        foreach (var message in messages) 
        { 
         producer.Send(session.CreateTextMessage(message)); 
        } 
       } 
      } 
     } 
    } 
} 

編輯:重命名MQAccess類MQClient。根據T Rob的建議,將它作爲實例類。 Disconnect方法仍與上述

public class MQClient : IDisposable 
{ 
    public MQConfigurationSectionHandler ConfigSettings { get; private set; } 
    public IConnectionFactory ConnectionFactory { get; private set; } 

    public IConnection Connection { get; private set; } 

    public IMessageConsumer Consumer { get; private set; } 
    public IMessageProducer Producer { get; private set; } 
    // Save sessions as fields for disposing and future subscription functionality 
    private ISession ProducerSession; 
    private ISession ConsumerSession; 
    public string SubTopicName { get; private set; } 
    public string PubTopicName { get; private set; } 
    public bool IsConnected { get; private set; } 
    public event Action<Exception> ConnectionError; 
    private Action<IMessage> IncomingMessageHandler; 

    public MQClient(string subTopicName, string pubTopicName, Action<IMessage> incomingMessageHandler) 
    { 
     // Dont put connect logic in the constructor. If we lose the connection we may need to connect again. 
     SubTopicName = subTopicName; 
     PubTopicName = pubTopicName; 
     IncomingMessageHandler = incomingMessageHandler; 
    } 

    public string Connect() 
    { 
     IsConnected = false; 
     string errorMsg = string.Empty; 

     ConfigSettings = (MQConfigurationSectionHandler) 
       ConfigurationManager.GetSection("mq-configuration"); 

     XMSFactoryFactory factory = XMSFactoryFactory.GetInstance(XMSC.CT_WMQ); 
     ConnectionFactory = factory.CreateConnectionFactory(); 
     ConnectionFactory.SetStringProperty(XMSC.WMQ_HOST_NAME, ConfigSettings.Hostname); 
     ConnectionFactory.SetIntProperty(XMSC.WMQ_PORT, ConfigSettings.Port); 
     ConnectionFactory.SetStringProperty(XMSC.WMQ_CHANNEL, ConfigSettings.Channel); 

     if (ConfigSettings.QueueManager == string.Empty) 
      ConnectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, ""); 
     else 
      ConnectionFactory.SetStringProperty(XMSC.WMQ_QUEUE_MANAGER, ConfigSettings.QueueManager); 

     Connection = ConnectionFactory.CreateConnection(); 


     if (!string.IsNullOrEmpty(PubTopicName)) 
     { 
      ProducerSession = Connection.CreateSession(false, AcknowledgeMode.AutoAcknowledge); 
      Producer = ProducerSession.CreateProducer(ProducerSession.CreateTopic(PubTopicName)); 
     } 

     if (!string.IsNullOrEmpty(SubTopicName) && IncomingMessageHandler != null) 
     { 
      ConsumerSession = Connection.CreateSession(false, AcknowledgeMode.AutoAcknowledge); 
      Consumer = ConsumerSession.CreateConsumer(ConsumerSession.CreateTopic(SubTopicName)); 
      Consumer.MessageListener = new MessageListener(IncomingMessageHandler); 
     } 

     try 
     { 
      Connection.Start(); 
      Connection.ExceptionListener = new ExceptionListener(ConnectionExceptionHandler); 
      IsConnected = true; 
     } 
     catch (TypeInitializationException ex) 
     { 
      errorMsg = "A TypeInitializationException error occured while attempting to connect to MQ. Check the Queue configuration in App.config. The error message is: " + ex.Message; 
     } 
     catch (IllegalStateException ex) 
     { 
      errorMsg = "An IllegalStateException error occured while attempting to connect to MQ. Check the Queue configuration in App.config. The error message is: " + ex.Message; 
     } 

     return errorMsg; 
    } 

    public void Disconnect() 
    { 
     if (Producer != null) 
     { 
      Producer.Close(); 
      Producer.Dispose(); 
      Producer = null; 
     } 

     if (ProducerSession != null) 
     { 
      // Call Unsubscribe here if subscription is durable 

      ProducerSession.Close(); 
      ProducerSession.Dispose(); 
      ProducerSession = null; 
     } 

     if (Connection != null) 
     { 
      Connection.Stop(); 

      //if (Connection.ExceptionListener != null) 
      // Connection.ExceptionListener = null; 

      // Per Shashi............ 
      //if (Consumer.MessageListener != null) 
      // Consumer.MessageListener = null; 

      Connection.Close(); 
      Connection.Dispose(); 
      Connection = null; 
     } 

     if (Consumer != null) 
     { 

      if (Consumer.MessageListener != null) 
       Consumer.MessageListener = null; 

      Consumer.Close(); 
      Consumer.Dispose(); 
      Consumer = null; 
     } 


     if (ConsumerSession != null) 
     { 
      // Call Unsubscribe here if subscription is durable 
      ConsumerSession.Close(); 
      ConsumerSession.Dispose(); 
      ConsumerSession = null; 
     } 

     IsConnected = false; 
    } 


    public void Publish(string message) 
    { 
     Producer.Send(ProducerSession.CreateTextMessage(message)); 
    } 


    public void Publish(string[] messages) 
    { 
     foreach (string msg in messages) 
      Publish(msg); 
    } 

    public void ConnectionExceptionHandler(Exception ex) 
    { 
     Disconnect(); // Clean up 

     if (ConnectionError != null) 
      ConnectionError(ex); 
    } 

    #region IDisposable Members 
    private bool disposed; 

    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (!this.disposed) 
     { 
      if (disposing) 
       Disconnect(); 

      disposed = true; 
     } 
    } 
    #endregion 

} 

回答

8

問題列爲錯誤的MSG崩潰就在這裏 - >where MQAccess is a small utility class.

問題的第一部分詢問如何判斷連接處於活動狀態。 WebSphere MQ的XMS類是非Java平臺的JMS規範的實現。他們非常密切地遵循JMS規範,並且JMS規範沒有關於連接或會話等同於isConnected的方法,因此XMS也沒有。但是,所有GET和PUT活動都應該發生在try/catch塊中,以便捕獲JMS異常。 (從中你總是打印linkedException,對不對?)當引發JMS異常時,應用程序將其視爲致命並死亡,否則它會關閉除Connection Factory之外的所有JMS對象,等待幾秒鐘,驅動連接順序。

根據問題中的新信息更新:
感謝您發佈MQAccess類。這提供了對發生的事情的深入瞭解,但仍然沒有任何代碼顯示連接關閉並重新打開的位置,如問題的第2部分所示。

但是,代碼顯示MQAccess類創建了一個私人實例ICONNECTION connection作爲類實例的構造,然後公開作爲MQAccess.GetConnection公開。目前發佈的MQAccess類沒有公共或私有類方法,它們將永遠替換由connection持有的連接句柄,因此如果調用了MQAccess.Connection.Close(),那麼MQAccess類中的IConnection對象實例將在持有無效連接句柄後永遠保留。一旦連接關閉,那個MQAccess實例就已經死亡。您必須刪除並重新實例MQAccess才能獲得新的連接。

MQAccess類不公開暴露連接工廠因此理論上將有可能調用從類的外部MQAccess.GetConnection並獲得有效的新IConnection對象,即使在關閉原來的後。但是,該實例將存在於MQAccess類的範圍之外,因此後續對MQAccess的調用將引用其已停用的實例變量connection,而不是在類外部創建的新連接實例。

如果您需要關閉並重新創建連接,則可以考慮從MQAccess內部管理該連接。一種低技術方法可能是編寫連接方法MQAccess.Close(),該方法將關閉現有連接,然後立即呼叫connection = GetConnection();,以便專用connection變量始終保持有效的連接句柄。

如果這樣不能解決問題,請張貼正在關閉並重新創建連接的代碼。

順便說一句,通過網絡連接的非事務處理會話可能會丟失或重複任何JMS提供程序(包括WMQ)的消息。這是你的意圖嗎?我已經解釋了爲什麼這是另一個SO帖子here

+0

謝謝T Rob這真的很有幫助。你是對的 - 類不應該在contstructor中啓動MQ。 linkedException是什麼意思? – Sam

+0

JMS異常(以及因爲JMS建模而導致的XMS異常)是一種多級結構。在JMS中,頂層包含「未能連接」等通用異常,但隨後的層次包含特定於提供者的數據,如「找不到QMgr」或「錯誤的QMgr名稱」。如果存在鏈接的異常,它可能具有有助於診斷的信息。請參閱['LinkedException'](http://pic.dhe.ibm.com/infocenter/wmqv7/v7r5/topic/com.ibm.mq.msc.doc/sapiexcpt.html#sapiexcpt_getlkex) –

+0

哦,不介意我看到linkedException是IllegalStateException的一個屬性。 – Sam

5

添加評論T.Rob。

問題1:
我希望你可以訪問源代碼MQAccess。如果是的話,你可以在MQAccess中公開一個屬性,指出連接是否處於活動狀態。如果您沒有訪問權限,那麼您可能不得不要求該類的作者添加此屬性。您可以執行以下操作來設置/重置屬性。

1)設置createConnection方法成功返回後的屬性。
2)爲連接設置一個Exception偵聽器。
3)重置異常處理程序中的屬性。檢查原因代碼並重置屬性,如果它是連接斷開錯誤(XMSWMQ1107和鏈接的異常可以具有MQRC 2009)。

問題2
如果你能告訴我們你是如何與closingreopening連接的話,這將有所幫助。我建議關閉連接是:
1)先做一個connection.Stop()。
2)刪除任何消息偵聽器,基本上做一個consumer.MessageListener = null。
3)然後做connection.Close()。
4)做一個連接=空

附加資料 這裏是我已經用於測試樣品。

private void OnException(Exception ex) 
    { 
     XMSException xmsex = (XMSException)ex; 
     Console.WriteLine("Got exception"); 
     // Check the error code. 
     if (xmsex.ErrorCode == "XMSWMQ1107") 
     { 
      Console.WriteLine("This is a connection broken error"); 
      stopProcessing = true; // This is a class member variable 
     } 
    } 

在創建連接的方法中,設置異常偵聽器。

 // Create connection. 
     connectionWMQ = cf.CreateConnection(); 
     connectionWMQ.ExceptionListener = new ExceptionListener(OnException); 

每當出現連接錯誤時,將調用異常偵聽器並將標誌設置爲true。

當不再需要對象時,處理對象是一種很好的做法。有父母關係,消費者,生產者等是Session的孩子,這些孩子又是Connection的孩子。因此,處置的順序可以是兒童第一和父母。但是,如果父母被處置,孩子也會自動處理。

+0

謝謝Shashi!我希望我不要問太多,但是你有沒有任何示例代碼可以說明你對問題1的回答?谷歌搜索XMSWMQ1107只有這個帖子! – Sam

+0

是否有必要處置生產者,消費者,會話和聽衆?看到這篇文章:http://stackoverflow.com/questions/12508473/ibm-mq-xms-subscription-not-closing什麼是正確的順序來做到這一點? – Sam

+0

我實現了一個ExceptionListener,但是它不檢測連接何時中斷,因此我的IsConnected標誌沒有被設置。 – Sam