[カフカ]生産者と消費者の本当の家族の練習システム

あなたは全うするために、両方の、それはマイクロサービスされているかどうか、実際には、プロジェクトの戦闘でDDDを見つける設計原理を意味する、低カップリングを高凝集し、RabbitMQのか、カフカ、メッセージキューシステムを介してすべての方法かどうかで、デカップリング放棄するエントリー最近のプロジェクトのために詳細に私が紹介シリーズその背景と使用モデルは、RabbitMQのとカフカを使用していますので、単純に生産者と消費者の単純なモデルを構築します。
ここに画像を挿入説明

プロデューサー終了

プロデューサーの終わり、プロデューサーの生産停止メッセージは、そのトピックとパーティションに基づいて、カフカのサーバークラスタ上で送ら:
ここに画像を挿入説明
次のように外側の包装方法は、合格することが必要であるとき、私たちは、カフカのメッセージが送られたカフカのトピックをA、アイデンティティとメッセージニーズを計算するために使用されるパーティションtenantIdが渡されます

public static bool SendKafkaExportData(
      string appName,
      int tenantId,
      int userId,
      string metaObjName,
      string viewName,
      string exportFileName,
      SearchCondition condition,
      string version = null,
      int total = -1,
      ExportFileType fileType = ExportFileType.Xlsx,
      string applicationContext = null,
      string msgTemplate = null)
    {
      Common.HelperObjects.ArgumentHelper.AssertNotEmpty(appName, nameof (appName));
      Common.HelperObjects.ArgumentHelper.AssertNotEmpty(metaObjName, nameof (metaObjName));
      Common.HelperObjects.ArgumentHelper.AssertNotEmpty(viewName, nameof (viewName));
      Common.HelperObjects.ArgumentHelper.AssertNotEmpty(exportFileName, nameof (exportFileName));
      Common.HelperObjects.ArgumentHelper.AssertPositive(tenantId, nameof (tenantId));
      Common.HelperObjects.ArgumentHelper.AssertPositive(userId, nameof (userId));
      Common.HelperObjects.ArgumentHelper.AssertNotNull<SearchCondition>(condition, nameof (condition));
      bool flag = true;
      try
      {
        ExportRequestDataModel exportRequestData = ExportRequestDataModel.GetExportRequestData(appName, tenantId, userId, metaObjName, viewName, exportFileName, condition, version, total, fileType, applicationContext, msgTemplate);
        long num = KafkaProducer.Send<ExportRequestDataModel>("TMLSent", tenantId, exportRequestData);
        ExportRequestDataModel.logger.Debug((object) string.Format("{0}-{1}-{2}发送Kafka消息{3}成功", (object) appName, (object) tenantId, (object) userId, (object) num));
      }
      catch (Exception ex)
      {
        ExportRequestDataModel.logger.Error((object) string.Format("{0}-{1}-{2}发送Kafka消息异常", (object) appName, (object) tenantId, (object) userId), ex);
        flag = false;
      }
      return flag;
    }

前記コアの方法:long num = KafkaProducer.Send<ExportRequestDataModel>("TMLSent", tenantId, exportRequestData);次のように実装ロジックは、バイナリアレイに配列カフカメッセージを運ぶ、です。

    /// <summary>Send a message to a topic.</summary>
    /// <param name="topic">The name of the topic to send the message to.</param>
    /// <param name="tenant">The id of the tenant the message belongs to.</param>
    /// <param name="value">The message content.</param>
    /// <returns>The offset of the message.</returns>
    public static long Send<T>(string topic, int tenant, T value) where T : IBinarySerializable
    {
      ArgumentHelper.AssertNotEmpty(topic, nameof (topic));
      ArgumentHelper.AssertPositive(tenant, nameof (tenant));
      return KafkaProducer.Send(topic, tenant, (object) value == null ? (byte[]) null : BigEndianEncoder.Encode<T>(value));
    }

の配列に直接送信することができるパーティションtenantId識別ストリングとバイナリメッセージを計算するための所望のトピックを得るために、次のようにメッセージ伝送機構です。

    /// <summary>Send a message to a topic.</summary>
    /// <param name="topic">The name of the topic to send the message to.</param>
    /// <param name="tenant">The id of the tenant the message belongs to.</param>
    /// <param name="value">The message content.</param>
    /// <returns>The offset of the message.</returns>
    public static long Send(string topic, int tenant, byte[] value)
    {
      ArgumentHelper.AssertNotEmpty(topic, nameof (topic));
      ArgumentHelper.AssertPositive(tenant, nameof (tenant));
      try
      {
        return KafkaProtocol.Produce(topic, tenant, value);
      }
      catch (ConnectionPoolException ex)
      {
        return KafkaProtocol.Produce(topic, tenant, value);
      }
      catch (KafkaException ex)
      {
        if (ex.Error == ErrorCode.NotLeaderForPartition || ex.Error == ErrorCode.LeaderNotAvailable)
          return KafkaProtocol.Produce(topic, tenant, value);
        throw;
      }
    }

伝送方式のコアです。

public static long Produce(string topic, int tenant, byte[] value)
    {
      TopicConfig topicConfig = BaseConfig<KafkaMapping>.Instance.GetTopicConfig(topic);
      int num = tenant % KafkaProtocol.GetTopicPartitionCount(topic);  //计算
      int partitionLeader = KafkaProtocol.GetPartitionLeader(topic, num);  //设置leader
      try
      {
        using (KafkaSession kafkaSession = new KafkaSession(topicConfig.Cluster, partitionLeader))  //创建一个kafka消息发送实例
        {
          Message message = new Message(value, TimeUtil.CurrentTimestamp);
          ProduceRequest request = new ProduceRequest((IDictionary<TopicAndPartition, MessageSet>) new Dictionary<TopicAndPartition, MessageSet>()
          {
            {
              new TopicAndPartition(topic, num),   //设置topic和partition
              new MessageSet(topicConfig.Codecs, (IList<Message>) new List<Message>()
              {
                message
              })
            }
          });   //设置要发送的消息
          ProduceResponse produceResponse = kafkaSession.Issue<ProduceRequest, ProduceResponse>(request);   //发送Kafka消息并
          KafkaProtocol.CheckErrorCode(produceResponse.Error, topic, new int?(num), new int?(tenant));
          return produceResponse.Offset;
        }
      }
      catch (Exception ex)
      {
        KafkaProtocol.RefreshPartitionMetadata(topic);
        throw;
      }
    }

我々が通過する必要があることをこのようなメッセージは、トピックに送信され、(別のパーティションがそう同じIデータのテナント数が同じパーティションに配置されます取る、異なるマシン上に格納されてもよい)に対応するパーティションに対応している、もはや自分のメッセージの配布パッケージ。

消費者側

グループの場合は、消費者の数は、パーティションの数よりも多くすべきではない:消費者側では、クラスタパーティションの消費量を消費者に提供することに留意すべきであるグループでは、各パーティションにのみアップバインドすることができますので、消費者が複数のパーティションを消費することができることを消費者に、パーティションが唯一の消費者支出を与えることができます(メッセージのパーティションは、グループで行われ、消費者のための競争ではないだろうことを確実にする)場合は、それゆえ、消費者グループの数は、パーティションの数よりも大きい場合、過剰消費者は任意のメッセージを受信しません
ここに画像を挿入説明
消費者の最後で、マシンのニーズはウォームアップすると当然のことながら、ニュース消費者サービスをオンにするだけでなく、近くにニュースサービスへの道、消費者サービスを開きます手段オープンメッセージはクローズメッセージサービスメッセージが共感閉鎖手段とクローズメッセージ処理スレッドを受信し、メッセージ処理スレッドを受信して開きます。

  /// <summary>
  /// 接收导出消息的服务
  /// </summary>
  public class ReceiveMsgProvider : IReceiveMsgProvider
  {
      #region 日志、构造方法以及单例
 
      protected static readonly LogWrapper Logger = new LogWrapper();
 
      private ReceiveMsgProvider()
      {
      }
 
      public static ReceiveMsgProvider Instance { get; } = new ReceiveMsgProvider();
 
      #endregion 日志、构造方法以及单例
 
      #region 开启消息接收服务
 
      public bool _ActivateService()
      {
          // 预热
         Cloud.Plugins.Helper.ESBProxy.WarmUp();
 
          //开启消息接收服务
          StartMessageService();
 
          //开始处理ExportQueue队列中的消息
          ExportConsumer.Instance.BeginImportData();
 
          Logger.Debug("_ActivateService was called.");
 
          return true;
      }
 
      protected void StartMessageService()
      {
          try
          {
              //开始消费消息
              ExportConsumer.Instance.Start();
          }
          catch (Exception ex)
          {
              Logger.Error(ex);
          }
      }
 
      #endregion 开启消息接收服务
 
      #region 关闭消息接收服务
 
      public bool _UnActivateService()
      {
          //关闭消息接收服务
          StopMessageService();
 
          //关闭处理queue的线程
          ExportConsumer.CloseQueueThreads(); 
 
          Logger.Debug("_UnActivateService was called.");
          return true;
      }
 
      protected void StopMessageService()
      {
          try
          {
              //停止消费消息
              ExportConsumer.Instance.Stop();
          }
          catch (Exception ex)
          {
              Logger.Error(ex);
          }
      }
 
     
  }

次のように前記開閉メッセージは、コアサービスを受けました。


       /// <summary>
        /// ESB服务调用入口:启动
        /// </summary>
        public void Start()
        {
             _loggging.Debug("ESB服务调用入口:启动");
            _consumer = new KafkaGroupConsumer(ExportKafkaConst.ExportKafkaConsumerGroup, ExportKafkaConst.ExportKafkaTopic, OnMessage);   //OnMessage即是处理消费逻辑的方法
            _consumer .Start();
        }

        /// <summary>
        /// ESB服务调用入口:停止
        /// </summary>
        public void Stop()
        {
            _loggging.Debug("ESB服务调用入口:停止");
            if (_consumer  != null && _consumer .IsRunning)
            {
                _consumer .Stop();
            }
        }

もちろん、それが最も重要なこと作り上げる生産と消費のシステムのこのセットの後にメッセージを受信し、消費することを:

       /// <summary>
        /// 接收导出消息并放置到缓存队列里
        /// </summary>
        /// <param name="context"></param>
        /// <returns></returns>
        public bool OnMessage(Message context)
        {

            logger.Debug(string.Format("接收到消息:{0}", Newtonsoft.Json.JsonConvert.SerializeObject(context.Value)));
            ExportRequestDataModel data = null;
            try
            {
                //读取消息内容
                data = BigEndianEncoder.Decode<ExportRequestDataModel>(context.Value);
                if (data == null || data.MsgType != ExportMessageType.Export)
                {
                    //无消息需要处理
                    return true;
                }
                else
                {
                    logger.Debug(string.Format("成功取到数据:{0}", Newtonsoft.Json.JsonConvert.SerializeObject(data)));
                }


                bool enQueueResult = ExportQueue.En_Queue(data, ApplicationContext.Current.TenantId);

                if (!enQueueResult)
                {
                    logger.Error("导出数据 In_Queue失败:userId:" + ApplicationContext.Current.UserId + " tenantId:" + ApplicationContext.Current.TenantId);
                }
            }
            catch (Exception ex)
            {
                var contextInfo = JsonConvert.SerializeObject(context);
                logger.Error($"导出写入队列失败,接收到的消息为:{contextInfo} ,信息异常信息:" + ex);
            }
            return true;
        }

メッセージ次のステップの後にチームに一つずつ、我々はそれを消費するために始めた当然のスレッドの数を使用することです。

        /// <summary>
        /// 开始对ExportQueue队列中的数据进行梳理
        /// </summary>
        public void BeginImportData()
        {

            ///初始化线程List
            ExportQueue.InstanceExportThreadsList();
            int count = ExportQueue.ExportThreadsList.Count;

            for (int i = 0; i < count; i++)
            {
                logger.Debug("开启线程th_" + i + "");
                ExportQueue.ExportThreadsList[i] = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(DealExportDataInQueue));
                ExportQueue.ExportThreadsList[i].Start(i);
            }
            // 处理小队列
            ExportQueue.ExportSmallThread = new System.Threading.Thread(new System.Threading.ParameterizedThreadStart(DealExportDataInQueue));
            ExportQueue.ExportSmallThread.Start(ExportQueue.SmallIndex);
        }

全体的なフローシステムを達成するために使用カフカの生産者と消費者は、実装の詳細のいくつかの少しの知識だった正直私も方法ですが、プロセスの完全なセットを実行するために、どのように全体的なノウハウから期待されるより高次の認知のいくつかについては、を介して行われより詳細な調査の後に理解します。

公開された240元の記事 ウォンの賞賛114 ビュー180 000 +

おすすめ

転載: blog.csdn.net/sinat_33087001/article/details/103117029