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

2023-08-30 15:00:21

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


作者:西瓜程式猿
主頁傳送門:xx

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


四、消費端實現

4.1-建立消費者

4.1.1-建立Windows服務專案

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

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


(2)修改專案名稱,專案名稱[西瓜程式猿]寫的是【RocketMQ.Consumer】,然後框架選擇的是【.NET Farmework 4.8】,這個可以根據自己的需要填寫和選擇,然後點選【建立】。

建立好的目錄如下:【Program.cs】是主程式的入口,【Service1.cs】是服務的入口,可以建立多個,然後在Prodrams.cs中設定就好了。

(3)【Service1】服務名稱可以重新命名修改,此處我重新命名為【RocketMQConsumerService】, Program.cs檔案中也相對應的也要進行修改。


(4)然後我們就可以在【RocketMQConsumerService】中寫業務邏輯程式碼了,有很多種方式可以定位到要寫的具體程式碼檔案,先列舉兩種常用的。
方法一:在【program.cs】檔案中,找到這個類,按鍵盤上的F12可以直接進入檢視檔案。

方法二:直接右擊,然後點選【檢視程式碼】。

業務程式碼寫到這裡面:

到這一步消費者服務就建立好了,然後就寫具體的業務程式碼就行了。注意:服務必須至少重寫 OnStart 和 OnStop 才有用。


4.1.2-專案依賴設定

(1)在使用Visual Studio(VS)開發.NET的應用程式和類庫時,預設的目標平臺為「Any CPU」。但是.NET SDK僅支援Windows 64-bit作業系統,所以需要自行設定。先右擊【RocketMQ.Consumer】專案,然後點選【屬性】。

(2)點選左側選項的【生成】,然後將目標平臺改為【x64】。

(3)將資源包【ONSClient4CPP】資料夾裡面所有的檔案,複製到【bin/Debug】目錄下。
資源包:

專案:


4.1.3-設定紀錄檔(log4net)

(1)為了方便測試,先介紹一下如何使用log4net做紀錄檔記錄,當紀錄檔啟動時和停止時我們記錄一下。我們在專案目錄下新建一個資料夾【LogConfig】,然後再建立一個檔案為【log4net.config】。

(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>

(3)並且右擊【log4net.config】檔案,點選【屬性】,然後將[複製到輸出目錄]設定為【始終複製】。

(4)然後安裝log4net。在專案目錄中右擊【參照】,然後點選【管理NuGet程式包】

(5)然後點選瀏覽,搜尋【log4net】,右側點選安裝。

(6)重要:然後設定【AssemblyInfo.cs 】檔案,如果不設定,是輸出不了紀錄檔的。

新增到底部即可:(如果你的【log4net.config】檔案路徑和我的不一樣,記得修改成跟自己設定路徑一樣的)。

程式碼:

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

(7)在服務啟動方法【OnStart】中,設定啟動log4net。

程式碼:

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

(8)然後就可以使用log4net了,首先在Windows服務中獲得log4net的範例。

程式碼:

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

4.2-設定連線資訊

(1)然後右擊【RocketMQ.Consumer】專案下,點選【參照】,然後將【RocketMQ.Core】專案勾選上確定。

(2)然後將前期準備的基本資訊放在組態檔中。在【App.config】檔案進行設定。

程式碼:

<!--設定為雲訊息佇列 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】組態檔的幫助類。

程式碼:

    /// <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方法,這個方法主要是消費者具體要執行的任務。

程式碼:

/// <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】方法來啟動生產者。

程式碼:

//建立消費者
            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】的類作為例子。

程式碼:

 /// <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】,然後右擊點選【新增安裝程式】。

(2)然後可以看到下面多出來了一個檔案,就是安裝程式。

(3)然後可以修改基本資訊,服務元件中的【服務名稱】【服務描述】等等。我們右擊【serviceInstall1】點選屬性,然後進行修改。

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


5.2-服務執行與釋出

當我們直接按F5或者其他方式直接執行專案時,會提示:"無法從命令列或偵錯程式啟動服務。必須首先安裝 Windows服務(使用installutil.exe),然後用ServerExplorer、Windows服務管理工具或 NET START命令啟動它。"。不是這樣執行的,跟著下面步驟來操作執行與釋出Windows服務吧。

前提注意:如果你設定的目標平臺是x64,開啟的目錄會不一樣,不然導致服務執行不起來。可以右擊專案名,點選【屬性】——>【生成】——>【目標平臺】檢視。

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

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

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

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

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

(2)然後點選【系統】

(3)點選【高階系統設定】

(4)點選【環境變數】

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

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

(7)測試是否設定成功,輸入這個命令檢視一下【InstallUtil】,如果是下面這樣的內容說明成功了。

(8)然後編輯解決方案和專案。

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

InstallUtil 專案啟動執行檔案全路徑

西瓜程式猿的例子:

InstallUtil D:\專案演示臨時儲存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe

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

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

(12)如果要解除安裝服務,可以執行這個命令:

InstallUtil /u 專案啟動執行檔案全路徑

西瓜程式猿的例子:

InstallUtil /u D:\專案演示臨時儲存\MyDemoService\MyDemoService\bin\Debug\MyDemoService.exe


5.3-常見命令

1、安裝服務:InstallUtil 專案啟動執行檔案全路徑
2、啟動服務:net start 服務名
3、停止服務:net stop 服務名
4、解除安裝服務:InstallUtil /u 專案啟動執行檔案全路徑


5.4-測試消費訊息

(1)首先可以先看一下紀錄檔,看一下這個消費者服務是否啟動成功了。

(2)然後再紀錄檔裡面記錄下消費的消費,在根據訊息Key或者訊息ID在阿里雲後臺查詢一下這一條訊息的【訊息軌跡】,如果提示消費成功就說明確實已經進行消費了。

最後,還有可能會出現訊息生產失敗、訊息消費失敗等場景,大佬們可以根據實際情況進行設計和跳轉哈。


六、防踩坑指南

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

異常詳情:

「ons.ONSClient4CPPPINVOKE」的型別初始值設定項引發異常。

解決方案:
第一步:在使用Visual Studio(VS)開發.NET的應用程式和類庫時,預設的目標平臺為「Any CPU」。但是.NET SDK僅支援Windows 64-bit作業系統,所以需要自行設定。先右擊【RocketMQ.Producer】專案,然後點選【屬性】,點選左側選項的【生成】,然後將目標平臺改為【x64】。

第二步:將資源包【ONSClient4CPP】資料夾裡面所有的檔案,複製到【bin】目錄下。


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.」

異常截圖:

解決方案:
這個問題一般是沒有連結上RocketMQ,檢查一下組態檔中資訊是否與RocketMQ資訊一致。尤其是[ons_name_srv] RocketMQ 版控制檯獲取的接入點資訊,類似「rmq-cn-XXXX.rmq.aliyuncs.com:8080」切記不要加"http://或者https