在.NET Framework中使用RocketMQ(阿裡雲版)實戰【第二章】

来源:https://www.cnblogs.com/kimiliucn/archive/2023/08/30/17667200.html
-Advertisement-
Play Games

上一章節主要介紹了RocketMQ基本介紹和前期準備,以及如何創建生產者。那這一章節主要介紹一下消費端的實現、如何發佈消費端,以及遇到的坑怎麼去解決。 ...


章節
第一章:https://www.cnblogs.com/kimiliucn/p/17662052.html
第二章:https://www.cnblogs.com/kimiliucn/p/17667200.html


cover(1).png

作者:西瓜程式猿
主頁傳送門:https://www.cnblogs.com/kimiliucn/

上一章節主要介紹了RocketMQ基本介紹和前期準備,以及如何創建生產者。那這一章節主要介紹一下消費端的實現、如何發佈消費端,以及遇到的坑怎麼去解決。


四、消費端實現

4.1-創建消費者

4.1.1-創建Windows服務項目

(1)右擊解決方案,然後依次點擊【添加】——>【新建項目】,然後選擇【 Windows 服務(.NET Framework) 】,點擊下一步。

註意:Windows服務只有在.NET Framework版本中才有了,在跨平臺中使用Worker Service。

image.png
(2)修改項目名稱,項目名稱[西瓜程式猿]寫的是【RocketMQ.Consumer】,然後框架選擇的是【.NET Farmework 4.8】,這個可以根據自己的需要填寫和選擇,然後點擊【創建】。
image.png
創建好的目錄如下:【Program.cs】是主程式的入口,【Service1.cs】是服務的入口,可以創建多個,然後在Prodrams.cs中配置就好了。
image.png
(3)【Service1】服務名稱可以重命名修改,此處我重命名為【RocketMQConsumerService】, Program.cs文件中也相對應的也要進行修改。
image.png
image.png
(4)然後我們就可以在【RocketMQConsumerService】中寫業務邏輯代碼了,有很多種方式可以定位到要寫的具體代碼文件,先列舉兩種常用的。
方法一:在【program.cs】文件中,找到這個類,按鍵盤上的F12可以直接進入查看文件。
image.png
方法二:直接右擊,然後點擊【查看代碼】。
image.png
業務代碼寫到這裡面:
image.png
到這一步消費者服務就創建好了,然後就寫具體的業務代碼就行了。註意:服務必須至少重寫 OnStart 和 OnStop 才有用。


4.1.2-項目依賴配置

(1)在使用Visual Studio(VS)開發.NET的應用程式和類庫時,預設的目標平臺為“Any CPU”。但是.NET SDK僅支持Windows 64-bit操作系統,所以需要自行設置。先右擊【RocketMQ.Consumer】項目,然後點擊【屬性】。
image.png
(2)點擊左側選項的【生成】,然後將目標平臺改為【x64】。
image.png

(3)將資源包【ONSClient4CPP】文件夾裡面所有的文件,複製到【bin/Debug】目錄下。
資源包:
image.png
項目:
image.png


4.1.3-配置日誌(log4net)

(1)為了方便測試,先介紹一下如何使用log4net做日誌記錄,當日誌啟動時和停止時我們記錄一下。我們在項目目錄下新建一個文件夾【LogConfig】,然後再創建一個文件為【log4net.config】。
image.png
(2)【log4net.config】內容如下。

<?xml version="1.0" encoding="utf-8"?>
<configuration>
	<configSections>
		<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net"/>
	</configSections>

	<system.web>
		<compilation debug="true" targetFramework="4.5.2" />
		<httpRuntime targetFramework="4.5.2" />
	</system.web>
	<log4net>
		<!--錯誤日誌:::記錄錯誤日誌-->
		<!--按日期分割日誌文件 一天一個-->
		<!-- appender 定義日誌輸出方式   將日誌以回滾文件的形式寫到文件中。-->
		<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路徑:下麵路徑項目啟動的時候自動在C盤中創建log、logError文件-->
			<file value="log/error/error_" />
			<!-- 如果想在本項目中添加路徑,那就直接去掉C:\\  只設置log\\LogError   項目啟動中預設創建文件 -->
			<appendToFile value="true"/>
			<!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--這是按日期產生文件夾-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只寫到一個文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件數量 超過此數量後 自動刪除之前的   好像只有在 按Size分割時有效 設定值value="-1"為不限文件數-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的輸出格式,也可以是xml  一個Appender只能是一個layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每條日誌末尾的文字說明-->
				<!--輸出格式 模板-->
				<!-- <param name="ConversionPattern"  value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger   
        操作者ID:%property{Operator} 操作類型:%property{Action}%n  當前機器名:%property%n當前機器名及登錄用戶:%username %n  
        記錄位置:%location%n 消息描述:%property{Message}%n   異常:%exception%n 消息:%message%newline%n%n" />-->

				<!--樣例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別: %-5level %n錯誤描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日誌級別】%-5level
                                  %n【記錄時間】%date
                                  %n【執行時間】[%r]毫秒
                                  %n【出錯文件】%F
                                  %n【出錯行號】%L
                                  %n【出錯的類】%logger 屬性[%property{NDC}]
                                  %n【錯誤描述】%message
                                  %n【錯誤詳情】%newline"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="ERROR" />
				<levelMax value="FATAL" />
			</filter>
		</appender>

		<!--DEBUG:::記錄DEBUG日誌-->
		<!--按日期分割日誌文件 一天一個-->
		<!-- appender 定義日誌輸出方式   將日誌以回滾文件的形式寫到文件中。-->
		<appender name="DebugAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路徑:下麵路徑項目啟動的時候自動在C盤中創建log、logError文件-->
			<file value="log/debug/debug_" />
			<!-- 如果想在本項目中添加路徑,那就直接去掉C:\\  只設置log\\LogError   項目啟動中預設創建文件 -->
			<appendToFile value="true"/>
			<!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--這是按日期產生文件夾-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只寫到一個文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件數量 超過此數量後 自動刪除之前的   好像只有在 按Size分割時有效 設定值value="-1"為不限文件數-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的輸出格式,也可以是xml  一個Appender只能是一個layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每條日誌末尾的文字說明-->
				<!--輸出格式 模板-->
				<!-- <param name="ConversionPattern"  value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger   
        操作者ID:%property{Operator} 操作類型:%property{Action}%n  當前機器名:%property%n當前機器名及登錄用戶:%username %n  
        記錄位置:%location%n 消息描述:%property{Message}%n   異常:%exception%n 消息:%message%newline%n%n" />-->

				<!--樣例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別: %-5level %n錯誤描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日誌級別】%-2level
                                  %n【記錄時間】%date
                                  %n【執行時間】[%r]毫秒
                                  %n【debug文件】%F
                                  %n【debug行號】%L
                                  %n【debug類】%logger 屬性[%property{NDC}]
                                  %n【debug描述】%message"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="DEBUG" />
				<levelMax value="WARN" />
			</filter>
		</appender>


		<!--INFO:::記錄INFO日誌-->
		<!--按日期分割日誌文件 一天一個-->
		<!-- appender 定義日誌輸出方式   將日誌以回滾文件的形式寫到文件中。-->
		<appender name="INFOAppender" type="log4net.Appender.RollingFileAppender">
			<!--保存路徑:下麵路徑項目啟動的時候自動在C盤中創建log、logError文件-->
			<file value="log/info/info_" />
			<!-- 如果想在本項目中添加路徑,那就直接去掉C:\\  只設置log\\LogError   項目啟動中預設創建文件 -->
			<appendToFile value="true"/>
			<!--按照何種方式產生多個日誌文件(日期[Date],文件大小[Size],混合[Composite])-->
			<rollingStyle value="Date"/>
			<!--這是按日期產生文件夾-->
			<datePattern value="yyyy-MM-dd'.log'"/>
			<!--是否只寫到一個文件中-->
			<staticLogFileName value="false"/>
			<!--保留的log文件數量 超過此數量後 自動刪除之前的   好像只有在 按Size分割時有效 設定值value="-1"為不限文件數-->
			<param name="MaxSizeRollBackups" value="100"/>
			<!--每個文件的大小。只在混合方式與文件大小方式下使用。超出大小後在所有文件名後自動增加正整數重新命名,數字最大的最早寫入。可用的單位:KB|MB|GB。不要使用小數,否則會一直寫入當前日誌-->
			<maximumFileSize value="50MB" />
			<!-- layout 控制Appender的輸出格式,也可以是xml  一個Appender只能是一個layout-->
			<layout type="log4net.Layout.PatternLayout">
				<!--每條日誌末尾的文字說明-->
				<!--輸出格式 模板-->
				<!-- <param name="ConversionPattern"  value="記錄時間:%date 線程ID:[%thread] 日誌級別:%-5level 記錄類:%logger   
        操作者ID:%property{Operator} 操作類型:%property{Action}%n  當前機器名:%property%n當前機器名及登錄用戶:%username %n  
        記錄位置:%location%n 消息描述:%property{Message}%n   異常:%exception%n 消息:%message%newline%n%n" />-->

				<!--樣例:2008-03-26 13:42:32,111 [10] INFO  Log4NetDemo.MainClass [(null)] - info-->
				<!--<conversionPattern value="%newline %n記錄時間:%date %n線程ID:[%thread] %n日誌級別: %-5level %n錯誤描述:%message%newline %n"/>-->
				<conversionPattern value="%n==========
                                  %n【日誌級別】%-2level
                                  %n【記錄時間】%date
                                  %n【執行時間】[%r]毫秒
                                  %n【info文件】%F
                                  %n【info行號】%L
                                  %n【info類】%logger 屬性[%property{NDC}]
                                  %n【info描述】%message"/>
			</layout>
			<filter type="log4net.Filter.LevelRangeFilter,log4net">
				<levelMin value="INFO" />
				<levelMax value="WARN" />
			</filter>
		</appender>

		<!--Set root logger level to DEBUG and its only appender to A1-->
		<root>
			<!--控制級別,由低到高: ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF-->
			<level value="ALL" />
			<appender-ref ref="DebugAppender" />
			<appender-ref ref="ErrorAppender" />
			<appender-ref ref="INFOAppender" />
		</root>
	</log4net>
</configuration>

image.png(3)並且右擊【log4net.config】文件,點擊【屬性】,然後將[複製到輸出目錄]設置為【始終複製】。
image.png
(4)然後安裝log4net。在項目目錄中右擊【引用】,然後點擊【管理NuGet程式包】
image.png
(5)然後點擊瀏覽,搜索【log4net】,右側點擊安裝。
image.png
(6)重要:然後配置【AssemblyInfo.cs 】文件,如果不配置,是輸出不了日誌的。
image.png
添加到底部即可:(如果你的【log4net.config】文件路徑和我的不一樣,記得修改成跟自己配置路徑一樣的)。
image.png
代碼:

[assembly: log4net.Config.XmlConfigurator(ConfigFileExtension = "config", ConfigFile = "LogConfig/log4net.config", Watch = true)]

(7)在服務啟動方法【OnStart】中,配置啟動log4net。
image.png
代碼:

         XmlConfigurator.Configure(new System.IO.FileInfo("LogConfig/log4net.config"));

(8)然後就可以使用log4net了,首先在Windows服務中獲得log4net的實例。
image.png
代碼:

private static readonly log4net.ILog logger = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

4.2-配置連接信息

(1)然後右擊【RocketMQ.Consumer】項目下,點擊【引用】,然後將【RocketMQ.Core】項目勾選上確定。
image.png
(2)然後將前期準備的基本信息放在配置文件中。在【App.config】文件進行配置。
image.png
代碼:

<!--設置為雲消息隊列 RocketMQ 版控制台實例詳情頁的實例用戶名。-->
<add key="ons_access_key" value="xxx" />
<!--設置為雲消息隊列 RocketMQ 版控制台實例詳情頁的實例密碼。-->
<add key="ons_secret_key" value="xxx" />
<!--您在雲消息隊列 RocketMQ 版控制台創建的Topic。-->
<add key="ons_topic" value="XG_CXY_Test" />
<!--設置為您在雲消息隊列 RocketMQ 版控制台創建的Group ID。-->
<add key="ons_groupId" value="XG_CXY_Group_Test" />
<!--設置為您從雲消息隊列 RocketMQ 版控制台獲取的接入點信息,類似“rmq-cn-XXXX.rmq.aliyuncs.com:8080”-->
<add key="ons_name_srv" value="xxx-xxx-xxx-xxx.rmq.aliyuncs.com:8080" />
<!--消費者/生產者目標來源-->
<add key="ons_client_code" value="XG_CXY_Consumer_Develop" />

(3)然後創建一個【Config】文件夾,寫一個獲得【ConfigSetting】配置文件的幫助類。
image.png
代碼:

    /// <summary>
    /// 配置文件
    /// </summary>
    public class ConfigGeter
    {
        private static T TryGetValueFromConfig<T>(Func<string, T> parseFunc, Func<T> defaultTValueFunc, [CallerMemberName] string key = "", string supressKey = "")
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(supressKey))
                {
                    key = supressKey;
                }

                var node = ConfigurationManager.AppSettings[key];
                return !string.IsNullOrEmpty(node) ? parseFunc(node) : defaultTValueFunc();
            }
            catch (Exception ex)
            {
                return default(T);
            }
        }

        #region 消息隊列:RocketMQ
        /// <summary>
        /// 設置為雲消息隊列 RocketMQ 版控制台實例詳情頁的實例用戶名。
        /// </summary>
        public static string ons_access_key
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }

        /// <summary>
        /// 設置為雲消息隊列 RocketMQ 版控制台實例詳情頁的實例密碼。
        /// </summary>
        public static string ons_secret_key
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }

        /// <summary>
        ///  您在雲消息隊列 RocketMQ 版控制台創建的Topic。
        /// </summary>
        public static string ons_topic
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }

        /// <summary>
        /// 設置為您在雲消息隊列 RocketMQ 版控制台創建的Group ID。
        /// </summary>
        public static string ons_groupId
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }

        /// <summary>
        /// 設置為您從雲消息隊列 RocketMQ 版控制台獲取的接入點信息,類似“rmq-cn-XXXX.rmq.aliyuncs.com:8080”。
        /// </summary>
        public static string ons_name_srv
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }

        /// <summary>
        /// 消息來源(生產者/消費端客戶端編碼)
        /// </summary>
        public static string ons_client_code
        {
            get
            {
                return TryGetValueFromConfig(_ => _, () => string.Empty);
            }
        }
        #endregion
    }

4.3-封裝核心代碼

(1)新建一個【ConsumerStartup】文件,這個類繼承自【MessageListener】類,然後實現consume方法,這個方法主要是消費者具體要執行的任務。
image.png
代碼:

/// <summary>
    /// 消費端啟動
    /// </summary>
    public class ConsumerStartup : MessageListener
    {
        private readonly static ILog logger = LogManager.GetLogger(typeof(ConsumerStartup));
        private readonly static ConsumerManager manager = new ConsumerManager();
        private readonly string _consumerClientCode;
        private readonly string _ons_groupId;

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="consumerClientCode">消費者客戶端Code</param>
        /// <param name="ons_groupId">消費者消費的分組</param>
        public ConsumerStartup(string consumerClientCode, string ons_groupId)
        {
            _consumerClientCode = consumerClientCode;
            _ons_groupId = ons_groupId;
        }

        ~ConsumerStartup()
        {
        }

        /// <summary>
        /// 消費者任務
        /// </summary>
        /// <param name="value"></param>
        /// <param name="context"></param>
        /// <returns></returns>
        public override ons.Action consume(Message value, ConsumeContext context)
        {
            Console.WriteLine("【消費者任務】:消費者消息進來了...");
            logger.Info($"【消費者任務】:消費者消息進來了...");

            string topic = value.getTopic();
            string business_id = value.getKey();
            string message_id = value.getMsgID();
            string msg_tag = value.getTag();
            byte[] bytes = Encoding.Default.GetBytes(value.getBody());
            string msg_body = Encoding.Default.GetString(bytes);
            if (string.IsNullOrEmpty(msg_body))
            {
                return ons.Action.CommitMessage;
            };

            string log_body = $"本次消費的消息:【消費序列:{value.getQueueOffset()}】【消息key:{business_id}】【消息ID:{message_id}】【Tag:{msg_tag}】";
            Console.WriteLine(log_body);
            logger.Info(log_body);
            logger.Info($"【消費內容】:{msg_body}");

            int status = 1;
            string error_msg = "";
            long sys_msg_id = 0;
            QueueOnsCommonModel consumerModel = null;

            try
            {
                //調度到具體的消費者
                consumerModel = JsonUtility.DeserializeJSON<QueueOnsCommonModel>(msg_body);
                if (consumerModel != null)
                {
                    logger.Info($"【消費者任務】:真正開始執行了(消息key:{consumerModel.MessageId})");
                    if (!long.TryParse(consumerModel.MessageId, out sys_msg_id))
                    {
                        logger.Info("sys_msg_id 轉換失敗!");
                    }

                    manager.ExecuteConsumer(consumerModel.Tag, consumerModel.EventType, consumerModel);

                    logger.Info($"【消費者任務】:執行完成了(消息key:{consumerModel.MessageId})");
                }
                else
                {
                    status = 2;
                    error_msg = "【調度到具體的消費者】解析消息body內容為空,無法進行消費";
                    logger.Error($"【調度到具體的消費者】解析消息body內容為空,無法進行消費");
                }
            }
            catch (Exception ex)
            {
                logger.Error($"【消費者任務】:發生異常了:{ex.Message}", ex);
                status = 2;
                error_msg = ex.Message;
            }

            return ons.Action.CommitMessage;
        }
    }

4.4-啟動消費者

在【RocketMQConsumerService.cs】文件OnStart方法中創建生產者,主要就是從配置文件中獲得配置信息,然後調用【QueueOnsProducer.CreatePushConsumer】方法創建消息隊列生產者,通過調用【QueueOnsProducer.SetPushConsumer】方法來設置生產者,最後通過調用【QueueOnsProducer.StartPushConsumer】方法來啟動生產者。
image.png
代碼:

//創建消費者
            string ons_access_key = ConfigSetting.ons_access_key;
            string ons_secret_key = ConfigSetting.ons_secret_key;
            string ons_topic = ConfigSetting.ons_topic;
            string ons_groupId = ConfigSetting.ons_groupId;
            string ons_name_srv = ConfigSetting.ons_name_srv;
            string ons_client_code = ConfigSetting.ons_client_code;
            QueueOnsProducer.CreatePushConsumer(new ONSPropertyConfigModel()
            {
                AccessKey = ons_access_key,
                SecretKey = ons_secret_key,
                Topics = ons_topic,
                GroupId = ons_groupId,
                NAMESRV_ADDR = ons_name_srv,
                OnsClientCode = ons_client_code,
            });
            //設置消費者
            QueueOnsProducer.SetPushConsumer(new ConsumerStartup(ons_client_code, ons_groupId), "*");
            //啟動消費者
            QueueOnsProducer.StartPushConsumer();

4.5-接收消費消息

我們如果要創建一個具體消費者去消費某一條消息,需要先創建一個類,然後實現【IConsumerMsg】介面中的【Consume】方法。需要在這個方法上面標註兩個特性,也可以是一個(意味著滿足一個條件即可),一個是【ConsumerTag】Tag標簽,表示要消費哪個生產的Tag標簽,一個是【EventType】,表示要消費哪個生產的事件類型。如果有多個不同的消費者,就按照上面的方式創建多個即可。[西瓜程式猿]這邊創建一個名為【SampleConsumer】的類作為例子。
image.png
代碼:

 /// <summary>
    /// 消費者Sample
    /// </summary>
    [ConsumerTag(QueueTagConsts.XG_Blog_Sample_Tag)]
    [EventType(QueueOnsEventType.RocketMQ_TEST)]
    public class SampleConsumer :  IConsumerMsg
    {
        private readonly static ILog logger = LogManager.GetLogger(typeof(SampleConsumer));

        public void Consume(QueueOnsCommonModel model)
        {
            logger.Info($"【西瓜程式猿-消費者Sample】:測試消費者進來了");
            if (model != null)
            {
                Console.WriteLine("tag:" + model.Tag);
                Console.WriteLine("body" + model.Body);
            }
            Console.WriteLine("【西瓜程式猿-消費者Sample】消費成功了!");
        }
    }

五、發佈消費端

然後來介紹一下如何部署消費端。之前看評論區說使用NSSM部署安裝Window服務更方便,後面我也試了一下確實還挺好用,但是針對目前這個程式始終運行不起來(各位大佬如果有更好的方法和建議可以在評論區提出來哈),所以這次還是用之前的方法來介紹如何部署Windows服務了。

5.1-服務基本配置

(1)點擊我們的服務【RocketMQConsumerService.cs】,然後右擊點擊【添加安裝程式】。
image.png

(2)然後可以看到下麵多出來了一個文件,就是安裝程式。
image.png
image.png

(3)然後可以修改基本信息,服務組件中的【服務名稱】【服務描述】等等。我們右擊【serviceInstall1】點擊屬性,然後進行修改。
image.png
image.png

(4)然後點擊【serviceProcessInstall1】右擊屬性,進行修改。
image.png
image.png


5.2-服務運行與發佈

當我們直接按F5或者其他方式直接運行項目時,會提示:"無法從命令行或調試程式啟動服務。必須首先安裝 Windows服務(使用installutil.exe),然後用ServerExplorer、Windows服務管理工具或 NET START命令啟動它。"。不是這樣運行的,跟著下麵步驟來操作運行與發佈Windows服務吧。
image.png
前提註意:如果你設置的目標平臺是x64,打開的目錄會不一樣,不然導致服務運行不起來。可以右擊項目名,點擊【屬性】——>【生成】——>【目標平臺】查看。
image.png

如果不是x64版本,複製這個地址:

C:\Windows\Microsoft.NET\Framework\v4.0.30319

如果是x64版本,複製這個地址:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319

不然會報類似這種錯誤:在初始化安裝時發生異常: System.BadImageFormatException: 未能載入文件或程式集...
(1)然後我們把上面的地址(根據自己的環境選擇)添加到環境變數中。點擊【控制面板】——>【系統和安全】
image.png

(2)然後點擊【系統】
image.png

(3)點擊【高級系統設置】
image.png

(4)點擊【環境變數】
image.png

(5)在【系統變數】中找到Path,然後點擊【編輯】。
image.png

(6)然後點擊【新建】,然後把我們拷貝的目錄複製到這裡。然後點擊確認即可。
image.png

(7)測試是否配置成功,輸入這個命令查看一下【InstallUtil】,如果是下麵這樣的內容說明成功了。
image.png

(8)然後編輯解決方案和項目。
image.png

(9)以管理員身份運行cmd命令,然後安裝服務。

InstallUtil 項目啟動執行文件全路徑

西瓜程式猿的例子:

InstallUtil D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

image.png

(10)出現這個說明安裝成功了。
image.png

(11)打開服務管理器,找到要啟動的服務,然後右擊啟動服務。
image.png

(12)如果要卸載服務,可以運行這個命令:

InstallUtil /u 項目啟動執行文件全路徑

西瓜程式猿的例子:

InstallUtil /u D:\項目演示臨時保存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

image.png


5.3-常見命令

1、安裝服務:InstallUtil 項目啟動執行文件全路徑
2、啟動服務:net start 服務名
3、停止服務:net stop 服務名
4、卸載服務:InstallUtil /u 項目啟動執行文件全路徑


5.4-測試消費消息

(1)首先可以先看一下日誌,看一下這個消費者服務是否啟動成功了。
image.png
(2)然後再日誌裡面記錄下消費的消費,在根據消息Key或者消息ID在阿裡雲後臺查詢一下這一條消息的【消息軌跡】,如果提示消費成功就說明確實已經進行消費了。
image.png
最後,還有可能會出現消息生產失敗、消息消費失敗等場景,大佬們可以根據實際情況進行設計和跳轉哈。


六、防踩坑指南

5.1:ons.ONSClient4CPPPINVOKE的類型初始值設定項引發異常

異常詳情:

“ons.ONSClient4CPPPINVOKE”的類型初始值設定項引發異常。

解決方案:
第一步:在使用Visual Studio(VS)開發.NET的應用程式和類庫時,預設的目標平臺為“Any CPU”。但是.NET SDK僅支持Windows 64-bit操作系統,所以需要自行設置。先右擊【RocketMQ.Producer】項目,然後點擊【屬性】,點擊左側選項的【生成】,然後將目標平臺改為【x64】。
image.png
第二步:將資源包【ONSClient4CPP】文件夾裡面所有的文件,複製到【bin】目錄下。
image.png


5.2:Topic Route does not exist

異常詳情:

Topic Route does not exist, Topic:XG_CXY_Test exception:msg: No route info of this topic, ,error:-1,in file <..\src\producer\DefaultMQProducer.cpp> line:581
See https://github.com/alibaba/ons/issues/7 for further details.”

異常截圖:
image.png
解決方案:
這個問題一般是沒有鏈接上RocketMQ,檢查一下配置文件中信息是否與RocketMQ信息一致。尤其是[ons_name_srv] RocketMQ 版控制台獲取的接入點信息,類似“rmq-cn-XXXX.rmq.aliyuncs.com:8080”切記不要加"http://或者https

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

-Advertisement-
Play Games
更多相關文章
  • 本文是區塊鏈瀏覽器系列的第五篇,項目完整代碼在[這裡](https://github.com/mengbin92/browser/tree/main)。 在[上一篇文章](https://mengbin.top/2023-08-20-browser2/)中給瀏覽器增加了簡單的用戶認證,至此瀏覽器的基 ...
  • # Background knowledge 在Java中,Cloneable 介面是一個標記介面(Marker Interface,它們內部都沒有方法和屬性),實現Cloneable介面表示該對象能被克隆,能使用Object.clone()方法。 要實現克隆功能,需要滿足以下兩個條件: - 類實現 ...
  • # 第二章——python基礎語法1: ## 1、輸入和輸出 ### a、使用print()進行列印(輸出)。 ``` print('hello,python') print('hello,world',end='') ``` - pritnt)(輸出內容後,預設會換行。要使其不換行,**則使用pr ...
  • `Matplotlib` 庫是一個用於數據可視化和繪圖的 Python 庫。它提供了大量的函數和類,可以幫助用戶輕鬆地創建各種類型的圖表,包括直方圖、箱形圖、散點圖、餅圖、條形圖和密度圖等。 使用 `Matplotlib` 的過程中,遇到的難點並不在於繪製各類的圖形,因為每種圖形都有其對應的API。 ...
  • 本文在瞭解netty核心組件的前提下,進一步瞭解組件如何在整個伺服器啟動過程如何被創建,如何組件之間配合來使用。首先也是先瞭解下大概服務端的啟動過程,並且在瞭解過程中我們帶著自己的問題去在學習過程中探尋答案 ...
  • 最近做了個脫敏的需求,要對系統中的敏感信息,如手機號、車牌號、身份證號、銀行卡號等進行脫敏顯示。 效果類似下麵這樣: ![](https://images.zwwhnly.com/picture/2023/image-20230829095822435.png) 簡單來說,就是對敏感信息中的某幾位進 ...
  • ### 歡迎訪問我的GitHub > 這裡分類和彙總了欣宸的全部原創(含配套源碼):[https://github.com/zq2599/blog_demos](https://github.com/zq2599/blog_demos) ### 本篇概覽 - 本篇是《java與es8實戰》系列的第五 ...
  • ## 前言 眾所周知,C#中有兩種類型變數:那就是**值類型**和**引用類型**。對於值類型而言,copy就相當於是全盤複製了,真正的實現了複製,屬於**深拷貝**;而對於引用類型而言,一般的copy只是**淺拷貝**,只是copy到了引用對象的地址,相當於值傳遞了一個引用指針,==新的對象通過地 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...