C#隊列學習筆記:MSMQ入門二

来源:https://www.cnblogs.com/atomy/archive/2020/03/27/12579282.html
-Advertisement-
Play Games

一、引言 按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程式才可以調用的隊列,有些情況下為了安全起見定義為私有隊列。所以剛開始的時候認為,要想訪問遠程消息隊列,只能使用公共隊列。但是後來發現,公共隊列依賴Domain Controller(域控),在 ...


    一、引言

    按照專用隊列解釋: MachineName\Private$\QueueName,只針對於本機的程式才可以調用的隊列,有些情況下為了安全起見定義為私有隊列。所以剛開始的時候認為,要想訪問遠程消息隊列,只能使用公共隊列。但是後來發現,公共隊列依賴Domain Controller(域控),在實際部署的時候,要求使用消息隊列的應用一定要在某個域中,有些太苛刻!後來發現,私有隊列也是可以遠程訪問的。(很困惑為什麼私有隊列只能本地訪問,這句話,到處都能看到?!)

    二、工作組下的本地C/S

    2.1、項目建立

    新建4個項目:

    2.2、項目代碼

    2.2.1、Model項目

    /// <summary>
    /// 消息隊列實體
    /// </summary>
    [Serializable]
    public class MqMessage
    {
        /// <summary>
        /// 對應Message的Label
        /// </summary>
        public string Label { get; set; }

        /// <summary>
        /// 對應Message的Body,CommandType為操作類型,List<string>為操作列表。
        /// </summary>
        public Dictionary<CommandType, List<string>> Body { get; set; } = new Dictionary<CommandType, List<string>>();

        /// <summary>
        /// 無參構造函數
        /// </summary>
        public MqMessage()
        {
        }

        /// <summary>
        /// 有參構造函數
        /// </summary>
        /// <param name="label"></param>
        /// <param name="body"></param>
        public MqMessage(string label, Dictionary<CommandType, List<string>> body)
        {
            Label = label;
            Body = body;
        }
    }

    /// <summary>
    /// 操作類型
    /// </summary>
    public enum CommandType
    {
        Create = 1, //創建
        Update = 2, //更新
        Delete = 3  //刪除
    }
MqMessage.cs

    2.2.2、Common項目

    /// <summary>
    /// 日誌幫助類
    /// </summary>
    public static class LogHelper
    {
        private static readonly string errLogSavePath = ConfigurationManager.AppSettings["ErrLogSavePath"] ?? AppDomain.CurrentDomain.BaseDirectory;

        /// <summary>
        /// 異常日誌方法重載
        /// </summary>
        /// <param name="ex">異常信息</param>
        public static void WriteLog(Exception ex)
        {
            WriteLog(GetErrMsg(ex));
        }

        /// <summary>
        /// 異常日誌方法重載
        /// </summary>
        /// <param name="message">日誌內容</param>
        public static void WriteLog(string message)
        {
            WriteLog(errLogSavePath, message);
        }

        /// <summary>
        /// 異常日誌方法重載
        /// </summary>
        /// <param name="filepath">日誌文件路徑</param>
        /// <param name="message">日誌內容</param>
        public static void WriteLog(string filepath, string message)
        {
            try
            {
                if (!Directory.Exists(filepath))
                {
                    Directory.CreateDirectory(filepath);
                }
                string filename = DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
                using (StreamWriter sw = new StreamWriter(filepath + "\\" + filename, true))
                {
                    sw.WriteLine("--------------------------------------------");
                    sw.WriteLine($"{DateTime.Now.ToLongTimeString()}:{DateTime.Now.Millisecond}\t{message}");
                    sw.Close();
                }
            }
            catch (Exception ex)
            {
                throw new Exception(GetErrMsg(ex));
            }
        }

        /// <summary>
        /// 獲取異常詳細信息
        /// </summary>
        /// <param name="ex"></param>
        /// <returns></returns>
        private static string GetErrMsg(Exception ex)
        {
            string errMessage = "";
            for (Exception tempException = ex; tempException != null; tempException = tempException.InnerException)
            {
                errMessage += tempException.Message + Environment.NewLine + Environment.NewLine;
            }
            errMessage += ex.ToString();
            return errMessage;
        }
    }
LogHelper.cs
    /// <summary>
    /// 消息隊列管理器
    /// </summary>
    public class MqManager : IDisposable
    {
        private MessageQueue _mq = null;
        private readonly LinkType linkType = LinkType.LocalHost;    //鏈接類型,遠程時使用LinkType.RemoteServer。
        private readonly string remoteServer = "192.168.2.165";     //遠程伺服器IP地址

        public static MqManager LinkServer { get; } = new MqManager();

        /// <summary>
        /// 初始化函數
        /// </summary>
        /// <param name="linkType">鏈接類型</param>
        public void MqManagerInit(LinkType linkType)
        {
            if (_mq == null)
            {
                string _path;
                if (linkType == LinkType.LocalHost)
                {
                    _path = @".\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "HelloWorld");
                }
                else
                {
                    _path = "FormatName:DIRECT=TCP:" + remoteServer + @"\private$\" + (ConfigurationManager.AppSettings["MSMQName"] ?? "HelloWorld");
                }
                _mq = new MessageQueue(_path)
                {
                    Formatter = new BinaryMessageFormatter()
                };
            }
        }

        /// <summary>
        /// 有參構造函數
        /// </summary>
        public MqManager()
        {
            MqManagerInit(linkType);
        }

        /// <summary>
        /// 發送消息隊列(事務)
        /// </summary>
        /// <param name="message"></param>
        public void Send(MqMessage message)
        {
            MessageQueueTransaction transaction = new MessageQueueTransaction();
            transaction.Begin();
            _mq.Send(message.Body, message.Label, transaction);
            transaction.Commit();
        }

        /// <summary>
        /// 接收消息隊列
        /// </summary>
        /// <returns></returns>
        public Message Receive()
        {
            Message msg = null;
            try
            {
                msg = _mq.Receive(new TimeSpan(0, 0, 1));
            }
            catch (Exception ex)
            {
                throw new Exception(ex.Message);
            }

            return msg;
        }

        /// <summary>
        /// 釋放資源
        /// </summary>
        public void Dispose()
        {
            if (_mq != null)
            {
                _mq.Close();
                _mq.Dispose();
                _mq = null;
            }
        }
    }

    /// <summary>
    /// 鏈接類型
    /// </summary>
    public enum LinkType
    {
        LocalHost = 1,      //本地伺服器
        RemoteServer = 2    //遠程伺服器
    }
MqManager.cs

    2.2.3、Send項目

    class Program
    {
        static void Main(string[] args)
        {
            MqMessage mqMessage = new MqMessage();
            List<string> list = new List<string>();

            Console.WriteLine("請輸入內容按回車發送,多個內容請用英文逗號隔開,退出請輸入Exit。");
            string receiveKey = Console.ReadLine();

            while (receiveKey.ToLower() != "exit")
            {
                if (receiveKey.Length > 0)
                {
                    mqMessage.Label = Guid.NewGuid().ToString();

                    list.Clear();
                    list = receiveKey.Split(new char[] { ',' }).ToList();
                    mqMessage.Body.Clear();
                    mqMessage.Body.Add(CommandType.Create, list);
                    try
                    {
                        MqManager.LinkServer.Send(mqMessage);
                        Console.WriteLine("內容已發送成功。");
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.Message);
                        LogHelper.WriteLog(ex);
                    }
                }
                receiveKey = Console.ReadLine();
            }

            MqManager.LinkServer.Dispose();
        }
    }
Program.cs

    2.2.4、Receive項目

    /// <summary>
    /// 接收消息隊列管理(線程)
    /// </summary>
    public class ReceiveManager : IDisposable
    {
        private Thread _thread = null;

        public static ReceiveManager Instance { get; set; } = new ReceiveManager();

        /// <summary>
        /// 開始
        /// </summary>
        public void Start()
        {
            StartReceive();
        }

        /// <summary>
        /// 接收線程
        /// </summary>
        private void StartReceive()
        {
            _thread = new Thread(new ThreadStart(Receive))
            {
                Name = "ReceiveThread",
                IsBackground = true
            };
            _thread.Start();
        }

        /// <summary>
        /// 接收線程調用方法
        /// </summary>
        private void Receive()
        {
            Message msg = null;
            while (true)
            {
                try
                {
                    msg = MqManager.LinkServer.Receive();
                    if (msg != null)
                    {
                        Console.WriteLine("----------------------------------------------------");
                        Console.WriteLine("Lable: " + msg.Label);
                        Dictionary<CommandType, List<string>> keyValuePairs = msg.Body as Dictionary<CommandType, List<string>>;
                        Console.WriteLine("Body CommandType: " + keyValuePairs.Keys.First());
                        Console.WriteLine("Body Details: ");
                        foreach (var item in keyValuePairs.Values.First())
                        {
                            Console.WriteLine(item);
                        }
                        Console.WriteLine("----------------------------------------------------");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    LogHelper.WriteLog(ex);
                }
                Thread.Sleep(1000);
            }
        }

        /// <summary>
        /// 結束
        /// </summary>
        public void Stop()
        {
            Dispose();
        }

        /// <summary>
        /// 釋放資源
        /// </summary>
        public void Dispose()
        {
            try
            {
                if (_thread != null)
                {
                    _thread.Abort();
                    _thread.Join();
                    _thread = null;
                }

                MqManager.LinkServer.Dispose();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
ReceiveManage.cs
    class Program
    {
        static void Main(string[] args)
        {
            ReceiveManager.Instance.Start();
            Console.WriteLine("退出請輸入Exit");
            string receiveKey = Console.ReadLine();
            while (receiveKey.ToLower() != "exit")
            {
                receiveKey = Console.ReadLine();
            }
            ReceiveManager.Instance.Stop();
            Console.Read();
        }
    }
Program.cs

    2.3、運行測試

    客戶端發送hello,world:

    服務端接收到的信息:

    三、工作組下的遠程C/S

    3.1、代碼調整

    工作組下的遠程C/S,代碼已經在上面的示例中提供,將Common\MqManager.cs下的:

    private readonly LinkType linkType = LinkType.LocalHost;改成private readonly LinkType linkType = LinkType.RemoteServer;即可。

    3.2、訪問許可權

    既然要與遠程伺服器交互(發送/接收)隊列信息,首當其衝的是訪問許可權問題,沒有許可權,一切免談。

    下麵講一下遠程伺服器(代碼中的192.168.2.165,Win7系統)要設置的內容:

    3.2.1、在運行中輸入compmgmt.msc->服務和應用程式->消息隊列->右鍵屬性->伺服器安全性->禁用未經身份驗證的 RPC 調用->把勾勾去掉->應用。

    3.2.2、在消息隊列->專用隊列->新建一個代碼中用到的HelloWorld隊列,勾上事務性->確定。

    為什麼要手工建HelloWorld消息隊列?因為要對這個隊列進行匿名訪問授權,後面會講到。至於事務性這個勾,這個要與代碼相一致。因為本示例中使用了MessageQueueTransaction來發送事務信息,所以必須得勾上這個勾,不然的話,發送時沒有任何的報錯信息,但是伺服器就是收不到隊列信息。

    3.2.3、專用隊列->HelloWorld->右鍵屬性->安全->ANONYMOUS LOGON->完全控制->應用。

    3.2.4、在運行中輸入regedit->HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\MSMQ\Parameters\security->新建兩個DWORD值:AllowNonauthenticatedRpc、NewRemoteReadServerDenyWorkgroupClient->分別雙擊將數值數據改成1。

    3.2.5、關於防火牆,我是關閉了的,假如您的電腦防火牆是打開了的話,請檢查一下Message Queuing是不是被允許的?

    3.3、運行測試

    客戶端發送A,B,C,D:

    伺服器端接收到的信息:

 

    參考自:

    https://www.cnblogs.com/xinhaijulan/archive/2010/08/22/1805768.html

    https://www.cnblogs.com/minily/p/7397746.html

    https://blog.csdn.net/jiyiqinlovexx/article/details/17803857

    https://www.cnblogs.com/mmbbflyer/p/7773303.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 下麵是互相轉換的代碼: 有想要瞭解更多關於python知識的請在下方評論或私信小編 ...
  • title: Java基礎語法(3) 運算符 blog: "CSDN" data: "Java學習路線及視頻" 1.算術運算符 算術運算符的註意問題 如果對負數取模,可以把模數負號忽略不記,如:5% 2=1。 但被模數是負數則不可忽略。此外,取模運算的結果不一定總是整數。 對於除號“/”,它的整數除 ...
  • 字元串的常用操作 很好理解 字元串可以用 ' + ' 連接,或者乘一個常數重覆輸出字元串 字元串的索引操作 通過一對中括弧可以找到字元串中的某個字元 可以通過正負數雙向操作噢 用一個中括弧來實現 為什麼沒有-0??去清醒腦子想想 -0 和 0 有差嗎? 還有一個切片操作 就像切菜那樣簡單,同樣是中括 ...
  • 中間件分類 ASP.NET Core 中間件的配置方法可以分為以上三種,對應的Helper方法分別是:Run(), Use(), Map()。 Run(),使用Run調用中間件的時候,會直接返回一個響應,所以後續的中間件將不會被執行了。 Use(),它會對請求做一些工作或處理,例如添加一些請求的上下 ...
  • 簡單的介紹一下集合,通俗來講就是用來保管多個數據的方案。比如說我們是一個公司的倉庫管理,公司有一堆貨物需要管理,有同類的,有不同類的,總而言之就是很多、很亂。我們對照集合的概念對倉庫進行管理的話,那麼 數組就是將一堆貨整整齊齊的碼在倉庫的某個地方,普通列表也是如此;Set就是在倉庫里有這麼一個貨架, ...
  • 前言 nuget 是 .net 的常用包管理器,目前已經內置到 Visual Studio 2012 以後的版本。大多數 .net 包都托管在 nuget.org,包括 .net core 框架基礎包,得益於 .net core 的模塊化設計,很多非核心包都可以進行一定程度的獨立升級。 製作並上傳 ...
  • 前言:由於公司占時沒有運維,出於微服務的需要,Apollo只能先裝在windows 阿裡雲上跑起來,由於環境及網路等問題,在安裝過程中遇到很多坑,算是一個個坑填完後,最終實現。 一. java jdk環境 java jdk 1.8下載地址: https://www.oracle.com/java/t ...
  • 本文只對api介面,header請求參數進行簡單驗證,起到拋磚引玉使用,需要深入驗證,請自行擴展 項目目錄結構如圖 中間件類 using ApiMiddleware.Common.DataEnityModel; using ApiMiddleware.Common.DbContext; using ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...