NIOネットワーク通信モデルカフカブローカー端末

まず、全体的なフレームワークは、カフカネットワーク通信モデルを概説しました

カフカネットワーク通信モデルは、原子炉のマルチスレッドモデルベースNIOが設計されています。主に1(アクセプタスレッド1)+ N(Nスレッドプロセッサの数)+ M(Mサービス処理スレッド)以下、次の表に示します簡単に:

スレッド スレッド名 指定スレッド
1 カフカソケット-acceptor_%X アクセプタスレッドは、クライアントが開始要求を監視する責任があります
N カフカ・ネットワークthread_%D プロセッサのスレッドがソケットを読み書きする責任があります
M カフカ・リクエスト・ハンドラ-_%D ワーカースレッド、プロセス、および特定のビジネスロジックを返す応答を生成し、

図の完全なフレームワークカフカネットワーク通信層の下に示すように:

 

通信層モデルカフカメッセージキュー-1 + N + Mモデル.PNG

ただ、いくつかは理解していないかもしれフレーム・チャートのトップを見始めて、それは問題で、私はあなたが一般的な理解を持っているネットワーク層のフレーム構造カフカに通信できますしません。後でカフカを詳細に上記のプロセスへの結合を、この記事の重要な源でセクション。ここでは、簡単にいくつかの重要な概念、ネットワーク通信モデルをまとめることができます:
(1)、アクセプター:1スレッドを受け、新しい接続要求を監視するための責任があり、かつ登録OP_ACCEPTイベント、に従い、新しい接続「ラウンドロビン」十字架の道スレッドに対応するプロセッサと、
(2)、プロセッサ:Nプロセッサスレッド、対応する登録アクセプタたSocketChannel OP_READイベントに割り当てられた独自のセレクタプロセッサを有し、その各々は、Nは、のサイズである「num.networker .threads「決定;
(3)KafkaRequestHandler:-KafkaRequestHandlerPool、-requestQueueリクエストキューとグローバルRequestChannel KafkaApisプロセスからのデータ取得要求を含むスレッド・プール内のM要求処理スレッドによってMのサイズ」NUM。 io.threads「決定;
(4)、RequestChannel:サービスを要求カフカチャネルが終了され、データ構造は、スレッドKafkaRequestHandlerプロセッサKafkaApisと交換データに供給要求と、グローバルリクエストキューとRESPONSEQUEUEを対応するキューに応答してローカルREQUESTQUEUEプロセッサプロセッサの複数を含みます。
(5)、NetworkClient:それぞれのパッケージのための基礎となるのJava NIO、カフカにあるネットワーク・インターフェース層です。方法カフカは、メッセージプロデューサオブジェクトを送信-KafkaProducerメインNetworkClient呼完了メッセージを、
(6)SocketServer:NIOサービスであり、それはまた、スレッドとアクセプタプロセッサのプロセッサスレッドを複数の受信を開始します。;反応器は、典型的なマルチスレッドモードを提供されるクライアント要求及び処理要求の相分離を受けている
、(7)KafkaServer:カフカブローカの代表例を、起動方法は、起動入口のインスタンスである、
(8)KafkaApisは:リクエストの種類を処理するためのAPIのカフカビジネスロジックの処理、責任など; 「送信メッセージ」「ゲットオフセット-offsetメッセージ」「ハートビート処理要求」等。

第二に、ネットワーク通信層カフカの設計と実現

このセクションでカフカは、設計および実装の詳細を分析するために、ソースネットワーク通信層を結合する本明細書のネットワーク通信層のいくつかの重要な要素を紹介-SocketServer、アクセプター、プロセッサ、RequestChannelとKafkaRequestHandler。この分析のソース部分は、カフカの0.11.0バージョンに基づいています。

1、SocketServer

ソケットSocketServerは、受信クライアントは接続要求を処理するコアを要求し、処理の結果を返し、アクセプターとプロセッサの初期化は、処理ロジックは、ここで実現されます。KafkaServerインスタンスの初期化方法は、その起動を呼び出す開始するとき、それは次のように実装されているアクセプターおよびNプロセッサスレッドを(エンドポイントは一般的に、ポートを設定するだけサーバーをそれぞれ初期化する)、初期化します。

 

def startup() {
    this.synchronized {

      connectionQuotas = new ConnectionQuotas(maxConnectionsPerIp, maxConnectionsPerIpOverrides)

      val sendBufferSize = config.socketSendBufferBytes
      val recvBufferSize = config.socketReceiveBufferBytes
      val brokerId = config.brokerId

      var processorBeginIndex = 0
      // 一个broker一般只设置一个端口
      config.listeners.foreach { endpoint =>
        val listenerName = endpoint.listenerName
        val securityProtocol = endpoint.securityProtocol
        val processorEndIndex = processorBeginIndex + numProcessorThreads
        //N 个 processor
        for (i <- processorBeginIndex until processorEndIndex)
          processors(i) = newProcessor(i, connectionQuotas, listenerName, securityProtocol, memoryPool)
        //1个 Acceptor
        val acceptor = new Acceptor(endpoint, sendBufferSize, recvBufferSize, brokerId,
          processors.slice(processorBeginIndex, processorEndIndex), connectionQuotas)
        acceptors.put(endpoint, acceptor)
        KafkaThread.nonDaemon(s"kafka-socket-acceptor-$listenerName-$securityProtocol-${endpoint.port}", acceptor).start()
        acceptor.awaitStartup()

        processorBeginIndex = processorEndIndex
      }
    }

2、アクセプター

アクセプターは、抽象クラスAbstractServerThreadからスレッドクラスの継承です。受容主なタスクは、(特定のSocketChannelがキューと同時処理スレッドウェイクプロセッサに追加された)スレッドプロセッサの後端に、次いで、ラウンドロビン方式、データ伝送経路-SocketChannelを確立しながら、監視し、クライアントからの要求を受信することです。
メインスレッドクラスの二つの重要な変数に従うことができる:
(1)nioSelectorを:NSelector.open()メソッドは、Java NIOセレクタ関連する操作をカプセル化する変数を作成することにより、
(2)、ServerChannelを:監視するためソケットソケットオブジェクトサーバポート、
メインrunメソッド・アクセプター・ソースで見てみましょう:

 

def run() {
    //首先注册OP_ACCEPT事件
    serverChannel.register(nioSelector, SelectionKey.OP_ACCEPT)
    startupComplete()
    try {
      var currentProcessor = 0
      //以轮询方式查询并等待关注的事件发生
      while (isRunning) {
        try {
          val ready = nioSelector.select(500)
          if (ready > 0) {
            val keys = nioSelector.selectedKeys()
            val iter = keys.iterator()
            while (iter.hasNext && isRunning) {
              try {
                val key = iter.next
                iter.remove()
                if (key.isAcceptable)
                  //如果事件发生则调用accept方法对OP_ACCEPT事件处理
                  accept(key, processors(currentProcessor))
                else
                  throw new IllegalStateException("Unrecognized key state for acceptor thread.")
                //轮询算法
                // round robin to the next processor thread
                currentProcessor = (currentProcessor + 1) % processors.length
              } catch {
                case e: Throwable => error("Error while accepting connection", e)
              }
            }
          }
        }
       //代码省略
  }

  def accept(key: SelectionKey, processor: Processor) {
    val serverSocketChannel = key.channel().asInstanceOf[ServerSocketChannel]
    val socketChannel = serverSocketChannel.accept()
    try {
      connectionQuotas.inc(socketChannel.socket().getInetAddress)
      socketChannel.configureBlocking(false)
      socketChannel.socket().setTcpNoDelay(true)
      socketChannel.socket().setKeepAlive(true)
      if (sendBufferSize != Selectable.USE_DEFAULT_BUFFER_SIZE)
        socketChannel.socket().setSendBufferSize(sendBufferSize)

      processor.accept(socketChannel)
    } catch {
        //省略部分代码
    }
  }

  def accept(socketChannel: SocketChannel) {
    newConnections.add(socketChannel)
    wakeup()
  }

あなたはポートを監視するためのサーバソケットオブジェクト-ServerSocketChannelに登録OP_ACCEPTする最初のイベント、アクセプタスレッドが開始すると、上記のソースで見ることができます。その後、関心起こるのラウンドロビン方式のイベントを待ちます。イベントが発生した場合、コールは受け入れ()メソッドのOP_ACCEPTがイベントを処理します。ここで、プロセッサによってラウンドロビン選択処理は、これは、実質的に均一なプロセッサスレッド複数の背後にその荷重を確保することができます。
受け入れるの受容作用()メソッドは、主に次の通り:
(1)対応するとSelectionKeyのServerSocketChannel例により取得し、クライアントとの接続を確立するために受け入れる()メソッドを呼び出し、
(2)connectionQuotas.incを(コール)接続を増加させる方法ステップたSocketChannelプロパティ戻りで作成され、また、第一(1)を設定する(例えばsendBufferSize、キープアライブ、TCPNODELAY、configureBlocking、等);計数統計
処理のためのSocketChannel processor.accept()メソッド〜(3)。ここでは、メインのSocketChannelが同時キューnewConnectionsキュープロセッサプロセッサに参加され、その後、プロセッサのスレッドはキューからのSocketChannelを取得し、処理目覚めます。これは、newConnectionsはConcurrentLinkedQueueキュー(リンクノードに基づく、アンバウンド形式のスレッドセーフなキュー)であるのでnewConnectionsは、同時実行スレッドとプロセッサアクセプタスレッドアクセス操作になります

3、プロセッサ

単に抽象クラスを継承AbstractServerThreadスレッドクラスとアクセプターとのプロセッサ。メインデータは、クライアントの要求とKafkaRequestHandlerは、クライアントに結果を処理し、応答から読み込まれます。:いくつかの重要な変数は、次のメインスレッドクラスの
(1)、newConnections:上記アクセプタ一つは既に述べたように、それがキューConcurrentLinkedQueue【のSocketChannel]の一種で、新しい相互接続を保持たSocketChannelがプロセッサによって処理;
(2)、inflightResponses ;地図応答を記録するために送信されていない、コレクション[文字列、RequestChannel.Response]タイプである
(図3)、セレクター:KSelectorタイプが管理するための変数でありますネットワーク接続、
実行方法を実行するフローチャートプロセッサプロセッサスレッドは、以下に与えられます。

スレッド処理のフローチャート.PNG Kafk_Processor


从上面的流程图中能够可以看出Processor处理器线程在其主流程中主要完成了这样子几步操作:
(1),处理newConnections队列中的socketChannel。遍历取出队列中的每个socketChannel并将其在selector上注册OP_READ事件;
(2),处理RequestChannel中与当前Processor对应响应队列中的Response。在这一步中会根据responseAction的类型(NoOpAction/SendAction/CloseConnectionAction)进行判断,若为“NoOpAction”,表示该连接对应的请求无需响应;若为“SendAction”,表示该Response需要发送给客户端,则会通过“selector.send”注册OP_WRITE事件,并且将该Response从responseQueue响应队列中移至inflightResponses集合中;“CloseConnectionAction”,表示该连接是要关闭的;
(3),调用selector.poll()方法进行处理。该方法底层即为调用nioSelector.select()方法进行处理。
(4),处理已接受完成的数据包队列—completedReceives。在processCompletedReceives方法中调用“requestChannel.sendRequest”方法将请求Request添加至requestChannel的全局请求队列—requestQueue中,等待KafkaRequestHandler来处理。同时,调用“selector.mute”方法取消与该请求对应的连接通道上的OP_READ事件;
(5),处理已发送完的队列—completedSends。当已经完成将response发送给客户端,则将其从inflightResponses移除,同时通过调用“selector.unmute”方法为对应的连接通道重新注册OP_READ事件;
(6),处理断开连接的队列。将该response从inflightResponses集合中移除,同时将connectionQuotas统计计数减1;

 

4、RequestChannel

在Kafka的网络通信层中,RequestChannel为Processor处理器线程与KafkaRequestHandler线程之间的数据交换提供了一个数据缓冲区,是通信过程中Request和Response缓存的地方。因此,其作用就是在通信中起到了一个数据缓冲队列的作用。Processor线程将读取到的请求添加至RequestChannel的全局请求队列—requestQueue中;KafkaRequestHandler线程从请求队列中获取并处理,处理完以后将Response添加至RequestChannel的响应队列—responseQueue中,并通过responseListeners唤醒对应的Processor线程,最后Processor线程从响应队列中取出后发送至客户端。

5、KafkaRequestHandler

KafkaRequestHandler也是一种线程类,在KafkaServer实例启动时候会实例化一个线程池—KafkaRequestHandlerPool对象(包含了若干个KafkaRequestHandler线程),这些线程以守护线程的方式在后台运行。在KafkaRequestHandler的run方法中会循环地从RequestChannel中阻塞式读取request,读取后再交由KafkaApis来具体处理。

6、KafkaApis

KafkaApis是用于处理对通信网络传输过来的业务消息请求的中心转发组件。该组件反映出Kafka Broker Server可以提供哪些服务

发布了504 篇原创文章 · 获赞 610 · 访问量 114万+

おすすめ

転載: blog.csdn.net/asdfsadfasdfsa/article/details/104029715