MSMQ稍复杂的包装类

继MSMQ简单包装类后,又把MSMQ再更新了一下。主要加入一些事件,有消息到达时,可用外部事件处理(ProcessMessageHandler),以及消息格式不是预期格式时的事件(InvalidTypeHandler),设置接收超时时间(Timeout);MSMQ里没有消息或接收超时的处理事件(NoMessageOrTimeoutHandler);可以设置接受到消息后是同步执行(ThreadCount = 0时)还是异步执行(ThreadCount > 0时);并可以限制多线程执行时的数量(ThreadCount = n);

使用MSMQ的准备工作参见http://www.cnblogs.com/iwteih/archive/2010/01/18/1650399.html

先上接口:

namespace XXX.Msmq 

{ 

    public interface IMsmq<T> : IDisposable 

    { 

        /// <summary> 

        /// Serializes and deserializes objects to or from the body of a message  

        /// </summary> 

        MessageFormatter Formatter { get; set; } 



        /// <summary> 

        /// The count of threads to process object in queue. 

        /// If omitted or 0, synchronous execution, otherwise asynchronous execution. 

        /// </summary> 

        int ThreadCount { get; set; } 



        /// <summary> 

        /// The time to be spent to get a message from Msmq. 

        /// If omitted , the hook will be alive all the time 

        /// </summary> 

        TimeSpan Timeout { get; set; } 



        /// <summary> 

        /// Push an object into MSMQ 

        /// </summary> 

        /// <param name="element">The object to be pushed into MSMQ</param> 

        void Push(T element); 



        /// <summary> 

        /// Pop the element in MSMQ 

        /// </summary> 

        /// <returns>object in msmq</returns> 

        T Pop(); 



        /// <summary> 

        /// Start to listen Msmq 

        /// </summary> 

        void StartListener(); 



        /// <summary> 

        /// Stop listening Msmq. Make sure that StopListen & StartListen are paired matched 

        /// or no StopListen. 

        /// </summary> 

        void StopListener(); 



        /// <summary> 

        /// The real funtion to process messsage. 

        /// </summary> 

        event ProcessMessageHandler ProcessMessage; 



        /// <summary> 

        /// Triggered when no message in MSMQ or timeout 

        /// </summary> 

        event NoMessageOrTimeoutHandler ProcessNoMessageOrTimeout; 



        /// <summary> 

        /// If message type is expected, use this event for handling 

        /// </summary> 

        event InvalidTypeHandler ProcessInvalidType; 

    } 

} 



实现如下:

using System; 

using System.Collections.Generic; 

using System.Linq; 

using System.Text; 

using System.Messaging; 

using System.Threading; 

using System.Runtime.InteropServices; 

using log4net; 



namespace XXX.Msmq 

{ 

    public enum MessageFormatter 

    { 

        XmlMessageFormatter, 

        BinaryMessageFormatter, 

    } 





    public class MessageArgs : EventArgs 

    { 

        /// <summary> 

        /// The object poped from queue 

        /// </summary> 

        public object ObjectToProcess { get; set; } 

    } 





    public delegate void ProcessMessageHandler(object sender, MessageArgs args); 



    public delegate void NoMessageOrTimeoutHandler(object sender, EventArgs args); 



    public delegate void InvalidTypeHandler(object sender, EventArgs args); 





    internal class Msmq<T> : IMsmq<T> 

    { 

        private static readonly ILog logger = LogManager.GetLogger( 

                    System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 



        MessageQueue queue = null; 



        int upperLimit = 0; 



        int currentThreadCount = 0; 





        #region Constructor 

        /// <summary> 

        /// Initial a Msmq object 

        /// </summary> 

        /// <param name="qName">Queue name</param> 

        public Msmq(string qName) 

            : this(qName, 0) 

        { 

        } 





        /// <summary> 

        /// Initial a Msmq object. 

        /// </summary> 

        /// <param name="qName">Queue name</param> 

        /// <param name="limit">Upper limit in this msmq</param> 

        public Msmq(string qName, int limit) 

        { 

            if (!string.IsNullOrEmpty(qName) && !MessageQueue.Exists(qName)) 

            { 

                MessageQueue.Create(qName); 

            } 

            queue = new MessageQueue(qName); 



            upperLimit = limit; 

        } 



        #endregion 



        #region IMsmq<T> Members 



        IMessageFormatter msgFormatter = new XmlMessageFormatter(new Type[] { typeof(T) }); 

        private MessageFormatter formatter; 

        /// <summary> 

        /// Serializes and deserializes objects to or from the body of a message  

        /// </summary> 

        MessageFormatter IMsmq<T>.Formatter 

        { 

            get { return formatter; } 

            set 

            { 

                formatter = value; 

                if (formatter == MessageFormatter.BinaryMessageFormatter) 

                { 

                    msgFormatter = new BinaryMessageFormatter(); 

                } 

                else 

                { 

                    msgFormatter = new XmlMessageFormatter(new Type[] { typeof(T) }); 

                } 

            } 

        } 



        /// <summary> 

        /// How many threads to process message. If omitted or 0, synchronous execution, otherwise asynchronous execution  

        /// </summary> 

        private int threadCount = 0; 

        int IMsmq<T>.ThreadCount 

        { 

            get { return threadCount; } 

            set 

            { 

                threadCount = value; 

                if (threadCount < 0) 

                    threadCount = 0; 

            } 

        } 



        /// <summary> 

        /// The time to wait while trying to get an object from Msmq. 

        /// </summary> 

        private TimeSpan timeout = TimeSpan.MaxValue; 

        TimeSpan IMsmq<T>.Timeout 

        { 

            get { return timeout; } 

            set { timeout = value; } 

        } 



        private event ProcessMessageHandler internelProcessMessage; 

        /// <summary> 

        /// The real funtion to process messsage. 

        /// </summary> 

        //public event ProcessMessageHandler ProcessMessage; 

        event ProcessMessageHandler IMsmq<T>.ProcessMessage 

        { 

            add { internelProcessMessage += value; } 

            remove { internelProcessMessage -= value; } 

        } 



        private event NoMessageOrTimeoutHandler noMessageOrTimeoutHandler; 

        /// <summary> 

        /// Triggered when no message in MSMQ or timeout 

        /// </summary> 

        event NoMessageOrTimeoutHandler IMsmq<T>.ProcessNoMessageOrTimeout 

        { 

            add { noMessageOrTimeoutHandler += value; } 

            remove { noMessageOrTimeoutHandler -= value; } 

        } 



        private event InvalidTypeHandler invalidTypeHandler; 

        /// <summary> 

        /// If message type is expected, use this event for handling 

        /// </summary> 

        event InvalidTypeHandler IMsmq<T>.ProcessInvalidType 

        { 

            add { invalidTypeHandler += value; } 

            remove { invalidTypeHandler -= value; } 

        } 



        /// <summary> 

        /// Push an object into MSMQ 

        /// </summary> 

        /// <param name="element">The object to be pushed into MSMQ</param> 

        void IMsmq<T>.Push(T element) 

        { 

            Send(element); 

        } 





        void Send(object element) 

        { 

            using (System.Messaging.Message message = new System.Messaging.Message()) 

            { 

                message.Body = element; 

                message.Formatter = msgFormatter; 



                queue.Send(message); 

            } 



            //In original status, MSMQ is empty, error will throw when calling CurrentCount. Because queue is not open. 

            //so i allow insert action happen before calling CurrentCount. 

            //If reach the upper message limit number, sleep for one minute. 

            while (upperLimit != 0 && (CurrentMessageCount >= upperLimit)) 

            { 

                System.Threading.Thread.Sleep(60000); 

            } 

        } 





        /// <summary> 

        /// Pop the element in MSMQ 

        /// </summary> 

        /// <returns>object in msmq</returns> 

        T IMsmq<T>.Pop() 

        { 

            return Receive(); 

        } 



        T Receive() 

        { 

            T element = default(T); 

            try 

            { 

                using (Message message = queue.Receive(new TimeSpan(0, 0, 10))) 

                { 

                    message.Formatter = msgFormatter; 

                    element = (T)message.Body; 

                } 

            } 

            catch (MessageQueueException mqex) 

            { 

                //Ingore the exception when queue is empty 

                if (mqex.MessageQueueErrorCode != MessageQueueErrorCode.IOTimeout) 

                { 

                    logger.Error(mqex); 

                } 

            } 

            return element; 

        } 



        /// <summary> 

        /// Start to listen Msmq 

        /// </summary> 

        void IMsmq<T>.StartListener() 

        { 

            queue.ReceiveCompleted += new ReceiveCompletedEventHandler(queue_ReceiveCompleted); 

            BeginReceiveMessage(); 

        } 





        /// <summary> 

        /// Stop listening Msmq. Make sure that StopListen & StartListen are paired matched 

        /// or no StopListen. 

        /// </summary> 

        void IMsmq<T>.StopListener() 

        { 

            queue.ReceiveCompleted -= new ReceiveCompletedEventHandler(queue_ReceiveCompleted); 

            currentThreadCount = 0; 

        } 



        #endregion 





        void BeginReceiveMessage() 

        { 

            queue.BeginReceive(timeout); 

        } 



        void queue_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e) 

        { 

            Message msg = null; 

            MessageStatus status = MessageStatus.Unknown; 

            try 

            { 

                msg = ((MessageQueue)sender).EndReceive(e.AsyncResult); 

                status = MessageStatus.OK; 

            } 

            catch (MessageQueueException qexp) 

            { 

                status = MessageStatus.QueueError; 

                if (qexp.MessageQueueErrorCode == MessageQueueErrorCode.IOTimeout) 

                { 

                    status = MessageStatus.IOTimeout; 

                } 

                else 

                { 

                    logger.Error(qexp); 

                } 

            } 

            catch (Exception exp) 

            { 

                logger.Error(exp); 

            } 



            //Handle message in exception condition 

            if (msg == null) 

            { 

                switch (status) 

                { 

                    case MessageStatus.IOTimeout: 

                        if (noMessageOrTimeoutHandler != null) 

                        { 

                            noMessageOrTimeoutHandler(this, null); 

                        } 

                        break; 

                    default: 

                        break; 

                } 

                BeginReceiveMessage(); 

                return; 

            } 



            msg.Formatter = msgFormatter; 



            if (msg.Body is T) 

            { 

                T element = (T)msg.Body; 



                if (internelProcessMessage != null) 

                { 

                    //asynchronously execute 

                    if (threadCount > 0) 

                    { 

                        Action<T> callback = RunAsnyc; 

                        callback.BeginInvoke(element, AfterRunAsnyc, null); 



                        //control the multi-thread work flow to make sure only specified threads are working 

                        while (true) 

                        { 

                            if (currentThreadCount >= threadCount) 

                            { 

                                Thread.Sleep(1000); 

                            } 

                            else 

                            { 

                                Interlocked.Increment(ref currentThreadCount); 

                                BeginReceiveMessage(); 

                            } 

                        } 

                    } 

                    // synchronously execute 

                    else 

                    { 

                        internelProcessMessage(this, new MessageArgs { ObjectToProcess = element }); 

                        BeginReceiveMessage(); 

                    } 

                } 

            } 

            else 

            { 

                status = Msmq<T>.MessageStatus.InvalidType; 

                //If invalidTypeHandler defined, using it otherwise resend message into Msmq 

                if (invalidTypeHandler != null) 

                { 

                    invalidTypeHandler(msg, null); 

                } 

                else 

                { 

                    Send(msg); 

                } 

            } 

            msg.Dispose(); 

        } 



        void AfterRunAsnyc(IAsyncResult itfAR) 

        { 

            Interlocked.Decrement(ref currentThreadCount); 

        } 



        private void RunAsnyc(T element) 

        { 

            foreach (var v in internelProcessMessage.GetInvocationList()) 

            { 

                ProcessMessageHandler pmh = (ProcessMessageHandler)v; 

                pmh.Invoke(this, new MessageArgs { ObjectToProcess = element }); 

            } 

        } 





        /// <summary> 

        /// This works well for the situation that MSMQ and KEXQueue is on the same machine. 

        /// I am not sure it can work well if the two are separated. 

        /// </summary> 

        private int CurrentMessageCount 

        { 

            get 

            { 

                //MSMQ.MSMQManagement msmq = new MSMQ.MSMQManagement(); 



                object server = null; 

                object path = queue.Path; 

                object format = null; 



                //msmq.Init(ref server, ref path, ref format); 



                //int count = msmq.MessageCount; 



                //Marshal.ReleaseComObject(msmq); 



                //return count; 

                return 0; 

            } 

        } 



        enum MessageStatus 

        { 

            /// <summary> 

            /// Message received successfully 

            /// </summary> 

            OK, 

            /// <summary> 

            /// Queue is empty or occur when time out 

            /// </summary> 

            IOTimeout, 

            /// <summary> 

            /// Cannot convert the messsage to expected object type 

            /// </summary> 

            InvalidType, 

            /// <summary> 

            /// Exception occurs when receiving the message 

            /// </summary> 

            QueueError, 

            /// <summary> 

            /// Exception thrown not by Msmq 

            /// </summary> 

            Unknown 

        } 



        #region IDisposable Members 



        void IDisposable.Dispose() 

        { 

            if (queue != null) 

                queue.Dispose(); 

        } 



        #endregion 

    } 

} 



using System; 

using System.Collections.Generic; 

using System.Linq; 

using System.Text; 



namespace XXX.Msmq 

{     

    public class MsmqFactory<T> 

    { 

        private static object objLock = new object(); 



        private static Dictionary<string, IMsmq<T>> queuelist = new Dictionary<string, IMsmq<T>>(); 



        /// <summary> 

        /// Create a Msmq instance. 

        /// </summary> 

        /// <param name="queueName">Queue name</param> 

        /// <returns>A Msmq instance</returns> 

        /// <remarks>If calling CreateMsmq twice. The two returned objects are separated instances. 

        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 

        /// one and two are different objecct. 

        /// </remarks> 

        public static IMsmq<T> CreateMsmq(string queueName) 

        { 

            return new Msmq<T>(queueName); 

        } 



        /// <summary> 

        /// Create a Msmq instance with message count limitation. 

        /// </summary> 

        /// <param name="queueName">Queue name</param> 

        /// <param name="limit">Upper limit in this msmq</param> 

        /// <returns>A Msmq instance</returns> 

        /// <remarks>If calling CreateMsmq twice. The two returned objects are separated instances. 

        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 

        /// one and two are different objecct. 

        /// </remarks> 

        public static IMsmq<T> CreateMsmq(string queueName, int limit) 

        { 

            return new Msmq<T>(queueName, limit); 

        } 





        /// <summary> 

        /// Initial a singleton Msmq object 

        /// </summary> 

        /// <param name="qName">Queue name</param> 

        /// <returns>A Msmq instance</returns> 

        /// <remarks>If calling CreateMsmq twice. The two returned objects are the same instances if their queue names are the same.. 

        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 

        /// one and two are the same object because they have the same queuename. 

        /// </remarks> 

        public static IMsmq<T> CreateSingletonMsmq(string queueName) 

        { 

            return CreateSingletonMsmq(queueName, 0); 

        } 





        /// <summary> 

        /// Initial a singleton Msmq object 

        /// </summary> 

        /// <param name="qName">Queue name</param> 

        /// <param name="limit">Upper limit in this msmq</param> 

        /// <returns>A Msmq instance</returns> 

        /// <remarks>If calling CreateMsmq twice. The two returned objects are the same instances if their queue names are the same. 

        /// For example, object one = CreateMsmq("A"); object two = CreateMsmq("A"); 

        /// one and two are the same object because they have the same queuename. 

        /// </remarks>        

        public static IMsmq<T> CreateSingletonMsmq(string queueName, int limit) 

        { 

            lock (objLock) 

            { 

                if (!queuelist.ContainsKey(queueName)) 

                { 

                    Msmq<T> queue = new Msmq<T>(queueName); 

                    queuelist.Add(queueName, queue); 

                    return queue; 

                } 

                else 

                { 

                    return queuelist[queueName]; 

                } 

            } 

        } 





        /// <summary> 

        /// Dispose a queue by given queuename. 

        /// </summary> 

        /// <param name="qName">The queue with the name to be disposed </param> 

        public static void DisposeQueue(string queueName) 

        { 

            lock (objLock) 

            { 

                if (queuelist.ContainsKey(queueName)) 

                { 

                    queuelist[queueName].Dispose(); 

                    queuelist.Remove(queueName); 

                } 

            } 

        } 



        /// <summary> 

        /// Dispose all queues. 

        /// </summary> 

        public static void DisposeQueue() 

        { 

            lock (objLock) 

            { 

                foreach (var queue in queuelist.Values) 

                { 

                    queue.Dispose(); 

                } 

                queuelist.Clear(); 

            } 

        } 



    } 

} 



调用如下:
IMsmq<YourObj>  msmq = MsmqFactory<YourObj>.CreateMsmq("QueueName"); 

msmq.Formatter = MessageFormatter.XmlMessageFormatter; 

msmq.Timeout = new TimeSpan(0, 0, 30); 

msmq.ProcessMessage += new ProcessMessageHandler(ProcessMessage); 

msmq.ProcessNoMessageOrTimeout += new NoMessageOrTimeoutHandler(ProcessNoMessageOrTimeout); 

msmq.StartListener(); 
 

MSMQ接收消息的方式有很多,比如BeginPeek或EndPeek,可以根据需要自行改动。

NOTE: 若将MSMQ定义成Transaction,则在多线程接收message时会出现消息丢失现象。

你可能感兴趣的:(MQ)