<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          .NET Core中的RabbitMQ消費(fèi)者CPU高,竟然是這個(gè)原因

          共 2174字,需瀏覽 5分鐘

           ·

          2022-06-09 10:44

          ?

          在 RabbitMQ 中有一個(gè) vhsot 機(jī)制,可以用來做租戶隔離,當(dāng)產(chǎn)品從單租戶演化為多租戶時(shí),正好可以用到這個(gè)特性,不同 vhost 中的交換機(jī)、隊(duì)列互不影響。

          起初在產(chǎn)品中引入 RabbitMQ 的時(shí)候,版本如下:

          • RabbitMQ:3.7.2 (后來升級(jí)為 3.8.2)
          • RabbitMQ Client:5.1.2
          • .NET Core:3.1

          通過一段時(shí)間的努力,產(chǎn)品終于支持多租戶模式了,測(cè)試在做測(cè)試的時(shí)候發(fā)現(xiàn)了一個(gè)問題,隨著租戶數(shù)添加的越來越多,RabbitMQ 消費(fèi)者的 CPU 占用也越來越高。

          100 左右的租戶數(shù),每個(gè)租戶隊(duì)列大概 10 幾個(gè),這時(shí) CPU 占用穩(wěn)定在 50% 左右,即使系統(tǒng)沒有任何人訪問。

          分析下可能的原因:

          • 因產(chǎn)品比較復(fù)雜,可能是其代碼影響到;
          • 可能是 RabbitMQ 的參數(shù)問題;
          • 可能是 .NET Core 中的驅(qū)動(dòng)的問題,可以嘗試下 Java 。

          正式進(jìn)入問題的排查。

          簡(jiǎn)單示例

          1、在 .NET Core 3.1 中編寫一個(gè)簡(jiǎn)單的 RabbitMQ 示例:

          public?void?Start()
          {
          ????Console.WriteLine("App?Start...");
          ????_defMqConfig?=?new?MQConfig()
          ????{
          ????????MQAutomaticRecoveryEnabled?=?true,
          ????????MQHeartBeat?=?5,
          ????????MQNetworkRecoveryInterval?=?5,
          ????????MQVHost?=?"/",
          ????????MQHostName?=?_mqHostName,
          ????????MQUserName?=?_mqUserName,
          ????????MQPassword?=?_mqPassword,
          ????????MQPort?=?_mqPort,
          ????????MQServerPort?=?string.IsNullOrEmpty(_mqServerPort)???$"1{_mqPort}"?:?_mqServerPort
          ????};

          ????Console.WriteLine("?MQ?vhost?init?Start...");
          ????string?prefix?=?"testhost";
          ????for?(int?i?=?0;?i?200;?i++)
          ????{
          ????????string?vhost?=?$"{prefix}{i}";
          ????????InitAllVhost(vhost);
          ????????Console.WriteLine($"??初始化vhost:{vhost}...");
          ????}
          ????Console.WriteLine("?MQ?vhost?init?Done...");
          ????Console.WriteLine("App?Start?Done...");
          }
          private?void?InitAllVhost(string?vhost)
          {
          ????string?url?=?$"http://{_mqHostName}:{_mqServerPort}";
          ????_mqManager.AddVirtualHost(url,?vhost,?_mqUserName,?_mqPassword);

          ????_defMqConfig.MQVHost?=?vhost;
          ????_mqManager.Subscribe(_defMqConfig);
          }

          2、監(jiān)聽的代碼如下:

          public?void?Subscribe(MQConfig?engineConfig)
          {
          ????var?factory?=?new?ConnectionFactory();
          ????factory.HostName?=?engineConfig.MQHostName;
          ????factory.UserName?=?engineConfig.MQUserName;
          ????factory.Password?=?engineConfig.MQPassword;
          ????factory.VirtualHost?=?engineConfig.MQVHost;

          ????factory.RequestedHeartbeat?=?(ushort)?engineConfig.MQHeartBeat;?
          ????factory.AutomaticRecoveryEnabled?=?true;
          ????factory.NetworkRecoveryInterval?=?new?TimeSpan(engineConfig.MQNetworkRecoveryInterval);

          ????var?connection?=?factory.CreateConnection();
          ????var?channel?=?connection.CreateModel();

          ????channel.QueueDeclare("TestQueue",?false,?false,?false,?null);
          ????channel.ExchangeDeclare("TestQueueExchange",?ExchangeType.Direct,?false,?false,?null);

          ????var?consumer?=?new?EventingBasicConsumer(channel);
          ????channel.BasicConsume("TestQueue",?false,?consumer);
          ????channel.QueueBind("TestQueue",?"TestQueueExchange",?"TestQueueExchange");

          ????consumer.Received?+=?(model,?ea)?=>
          ????{
          ????????var?body?=?ea.Body;
          ????????var?message?=?Encoding.UTF8.GetString(body);
          ????????Console.WriteLine("已接收:?{0}",?message);
          ????};
          }

          3、上面代碼創(chuàng)建了 200 個(gè) vhost ,每個(gè) vhost 中 1 個(gè)隊(duì)列,程序運(yùn)行后觀察 cpu 如下圖:

          4、在 Subscribe 方法中有創(chuàng)建 Connection 和 CreateModel 方法,如果使用 using 或在方法最后對(duì)其進(jìn)行釋放,CPU 會(huì)是一個(gè)正常的狀態(tài),但消息也就接收不到了。

          調(diào)整參數(shù)

          1、在 RabbitMQ 中有兩個(gè)參數(shù) MQHeartBeat、MQNetworkRecoveryInterval :

          • MQHeartBeat:心跳檢測(cè)
          • MQNetworkRecoveryInterval:掉線重連

          2、不斷調(diào)整這兩個(gè)參數(shù)的值,進(jìn)行嘗試,發(fā)現(xiàn) CPU 并沒有明顯改善。

          嘗試 Java

          當(dāng)沒有什么頭緒的時(shí)候,就會(huì)采用各種方式進(jìn)行嘗試,來排除問題,所以決定用 Java 試試。

          在 Java 程序中,使用的 RabbitMQ 客戶端為 rabbitmq-java-client ,版本為 5.14.2 ,因?yàn)橹霸?.NET 程序驗(yàn)證時(shí)已經(jīng)創(chuàng)建了 vhost ,所以在 Java 程序中只寫了消費(fèi)者進(jìn)行監(jiān)聽。

          當(dāng) Java 程序跑起來的時(shí)候,發(fā)現(xiàn) CPU 占用是正常的,在遍歷 vhost 監(jiān)聽的過程中 CPU 有所波動(dòng),遍歷完后 ,CPU 占用比較穩(wěn)定。

          真正的原因

          這時(shí)基本可以確定,是 .NET Core 的 RabbitMQ 客戶端的問題,到這時(shí)才想起有可能是 .NET Core ?RabbitMQ 客戶端的版本問題,檢查發(fā)現(xiàn)目前使用的版本是 5.1.2,而最新的版本為 6.3.0 。

          升級(jí) .NET Core RabbitMQ 到最新版本,升級(jí)后有兩個(gè)地方不兼容:

          • RequestedHeartbeat 類型變成了 TimeSpan;
          • 接收的消息由 byte[] 變成了 ReadOnlyMemory類型。

          修改這兩處后,趕緊運(yùn)行進(jìn)行測(cè)試,CPU 終于正常了。

          查看了下 RabbitMQ 客戶端在 GitHub 上的更新記錄,發(fā)現(xiàn)在版本 6.2.4 中有修復(fù)一個(gè)關(guān)于連接的 Bug:

          又繼續(xù)將版本回退到 6.2.3 進(jìn)行測(cè)試,問題又能重現(xiàn)了,就更加確定了這個(gè)問題是在 6.2.4 中解決了。

          最后

          現(xiàn)在無論是做項(xiàng)目還是做產(chǎn)品,都會(huì)使用很多中間件,這些中間件和相關(guān)的庫也是在不斷地更新迭代的,當(dāng)我們進(jìn)行功能迭代的同時(shí),也需要關(guān)注這些中間件的發(fā)展,在新的版本中提供了什么新特性,修復(fù)了什么問題,這給我們是否升級(jí)提供依據(jù)。

          希望本文對(duì)您有所幫助!

          瀏覽 198
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  www.欧美在线 | 欧美艹逼视频 | 人人草视频在线 | 爆操熟妇在线视频 | 国产se视频 |