kafka 服务端-网络-服务端处理Request和发送响应给客户端

接上篇

处理请求
KafkaServer.startup
requestHandlerPool = new KafkaRequestHandlerPool(config.brokerId, socketServer.requestChannel, apis, config.numIoThreads)
  //默认启动8个线程,一般情况下,生产环境里面我们是要去设置这个参数。
  for(i <- 0 until numThreads) {
    //创建线程
    runnables(i) = new KafkaRequestHandler(i, brokerId, aggregateIdleMeter, numThreads, requestChannel, apis)
    threads(i) = Utils.daemonThread("kafka-request-handler-" + i, runnables(i))
    //线程启动起来了
    threads(i).start()
  }

KafkaRequestHandler.run
          //TODO 获取request对象
          req = requestChannel.receiveRequest(300)
          //TODO  交给KafkaApis进行最终的处理
          apis.handle(req)


-> receiveRequest
//从队列里面获取Request对象
    requestQueue.poll(timeout, TimeUnit.MILLISECONDS)

-> KafkaApis.handle(req) 
def handle(request: RequestChannel.Request) {
          //因为我们用的是场景驱动的方式去分析的源码
          //我们之前一直分析的是 生产者那儿会发送过来请求
          //所以根据场景驱动的方式,我们就看这儿的代码。
          //TODO 处理生产者发送过来的请求
        case ApiKeys.PRODUCE => handleProducerRequest(request)
}

handleProducerRequest()
  def handleProducerRequest(request: RequestChannel.Request) {
    //获取到生产发送过来的请求信息
    val produceRequest = request.body.asInstanceOf[ProduceRequest]
    val numBytesAppended = request.header.sizeOf + produceRequest.sizeOf
    //按照分区的方式去遍历数据。

    //existingAndAuthorizedForDescribeTopics: 对应的主题也存在,也有权限。正常情况。
    //nonExistingOrUnauthorizedForDescribeTopics: 对应的主题不存在,没有权限。 不正常的情况。
    val (existingAndAuthorizedForDescribeTopics, nonExistingOrUnauthorizedForDescribeTopics) = produceRequest.partitionRecords.asScala.partition {
      //对方发送过来的数据进行一些判断
      //主要就是针对权限等等之类的事进行判断。
      case (topicPartition, _) => authorize(request.session, Describe, new Resource(auth.Topic, topicPartition.topic)) && metadataCache.contains(topicPartition.topic)
    }
    //existingAndAuthorizedForDescribeTopics再次处理数据
    //看是否有写权限。
    val (authorizedRequestInfo, unauthorizedForWriteRequestInfo) = existingAndAuthorizedForDescribeTopics.partition {
      case (topicPartition, _) => authorize(request.session, Write, new Resource(auth.Topic, topicPartition.topic))
    }
    // 经过前面代码的层层判断,最终我们需要处理的数据都是在authorizedRequestInfo对象里面。

    // the callback for sending a produce response
    def sendResponseCallback(responseStatus: Map[TopicPartition, PartitionResponse]) {

      val mergedResponseStatus = responseStatus ++ 
        unauthorizedForWriteRequestInfo.mapValues(_ =>
           new PartitionResponse(Errors.TOPIC_AUTHORIZATION_FAILED.code, -1, Message.NoTimestamp)) ++ 
        nonExistingOrUnauthorizedForDescribeTopics.mapValues(_ => 
           new PartitionResponse(Errors.UNKNOWN_TOPIC_OR_PARTITION.code, -1, Message.NoTimestamp))

      var errorInResponse = false

      mergedResponseStatus.foreach { case (topicPartition, status) =>
        if (status.errorCode != Errors.NONE.code) {
          errorInResponse = true
          debug("Produce request with correlation id %d from client %s on partition %s failed due to %s".format(
            request.header.correlationId,
            request.header.clientId,
            topicPartition,
            Errors.forCode(status.errorCode).exceptionName))
        }
      }

      def produceResponseCallback(delayTimeMs: Int) {
        //acks = 0
        //意味着生产者那儿不关心数据处理的结果。
        //所以我们不需要返回响应。
        if (produceRequest.acks == 0) {
          // no operation needed if producer request.required.acks = 0; however, if there is any error in handling
          // the request, since no response is expected by the producer, the server will close socket server so that
          // the producer client will know that some error has happened and will refresh its metadata
          if (errorInResponse) {
            val exceptionsSummary = mergedResponseStatus.map { case (topicPartition, status) =>
              topicPartition -> Errors.forCode(status.errorCode).exceptionName
            }.mkString(", ")
            info(
              s"Closing connection due to error during produce request with correlation id ${request.header.correlationId} " +
                s"from client id ${request.header.clientId} with ack=0\n" +
                s"Topic and partition to exceptions: $exceptionsSummary"
            )
            requestChannel.closeConnection(request.processor, request)
          } else {
            requestChannel.noOperation(request.processor, request)
          }
        } else {
          //如果代码要走这儿,说明我们把数据处理完了以后
          //需要给客户端(生产者)返回响应

          //封装一个请求头
          val respHeader = new ResponseHeader(request.header.correlationId)
          //封装一个请求体(响应消息)
          val respBody = request.header.apiVersion match {
            case 0 => new ProduceResponse(mergedResponseStatus.asJava)
            case version@(1 | 2) => new ProduceResponse(mergedResponseStatus.asJava, delayTimeMs, version)
            // This case shouldn't happen unless a new version of ProducerRequest is added without
            // updating this part of the code to handle it properly.
            case version => throw new IllegalArgumentException(s"Version `$version` of ProduceRequest is not handled. Code must be updated.")
          }

          //TODO 返回响应最重要的代码是在这儿
          //这儿给我们封装了一个Response的对象
          //这个对象就是服务端发送回给客户端(生产者)的
          requestChannel.sendResponse(new RequestChannel.Response(request, new ResponseSend(request.connectionId, respHeader, respBody)))
        }
      }

      // When this callback is triggered, the remote API call has completed
      request.apiRemoteCompleteTimeMs = SystemTime.milliseconds

      quotas.produce.recordAndMaybeThrottle(
        request.session.sanitizedUser,
        request.header.clientId,
        numBytesAppended,
        produceResponseCallback)
    }//TODO 暂时不看

    if (authorizedRequestInfo.isEmpty)
      sendResponseCallback(Map.empty)
    else {
      val internalTopicsAllowed = request.header.clientId == AdminUtils.AdminClientId

      // Convert ByteBuffer to ByteBufferMessageSet
      val authorizedMessagesPerPartition = authorizedRequestInfo.map {
        case (topicPartition, buffer) => (topicPartition, new ByteBufferMessageSet(buffer))
      }

      // call the replica manager to append messages to the replicas
      /**
       *  TODO
        * 把接收到的数据追加到磁盘上面。
        *
        * 大家目前只需要知道,我们的代码走到这儿
        * 最终数据就会被写到磁盘上面。
        */
      replicaManager.appendMessages(
        produceRequest.timeout.toLong,
        produceRequest.acks,
        internalTopicsAllowed,
        authorizedMessagesPerPartition,
        sendResponseCallback)

      // if the request is put into the purgatory, it will have a held reference
      // and hence cannot be garbage collected; hence we clear its data here in
      // order to let GC re-claim its memory since it is already appended to log
      produceRequest.clearPartitionRecords()
    }
  }

先从数组里面取出对应Processor一个队列,然后把这个响应放入到这个队列里面。
requestChannel.sendResponse
  def sendResponse(response: RequestChannel.Response) {
    //把响应存入到了一个队列里面。
    //先从数组里面取出对应Processor一个队列,然后把这个响应放入到这个队列里面。
    responseQueues(response.processor).put(response)
    for(onResponse <- responseListeners)
      onResponse(response.processor)
  }

处理响应的。绑定 OP_WRITE
SocketSever.Processor.run processNewResponses(){
sendResponse-> selector.send -> channel.setSend(send)
}
 public void setSend(Send send) {
        if (this.send != null)
            throw new IllegalStateException("Attempt to begin a send operation with prior send operation still in progress.");

        //往KafkaChannel里面绑定一个发送出去的请求。
        this.send = send;

        //关键的代码来了
        //这儿绑定了一个OP_WRITE事件。
        //一旦绑定了这个事件以后,我们就可以往服务端发送请求了。
        this.transportLayer.addInterestOps(SelectionKey.OP_WRITE);
    }

响应消息发送给客户端
SocketSever.Processor.run
poll() ->  selector.poll -> pollSelectionKeys(){
                //核心代码,处理发送请求的事件
                //selector 注册了一个OP_WRITE
                //selector 注册了一个OP_READ
                if (channel.ready() && key.isWritable()) {

                    //获取到我们要发送的那个网络请求。
                    //是这句代码就是要往服务端发送数据了。

                    //TODO:服务端
                    //里面我们发现如果消息被发送出去了,然后移除OP_WRITE
                    Send send = channel.write();

                    //已经完成响应消息的发送
                    if (send != null) {
                        this.completedSends.add(send);
                        this.sensors.recordBytesSent(channel.id(), send.size());
                    }
                }
}
this.completedSends.add(send); 这个加入队列后processCompletedSends()处理我们已经发送出去的响应
  private def processCompletedSends() {
    selector.completedSends.asScala.foreach { send =>
      //移除数据结构里面的一些数据信息
      val resp = inflightResponses.remove(send.destination).getOrElse {
        throw new IllegalStateException(s"Send for ${send.destination} completed, but not in `inflightResponses`")
      }
      resp.request.updateRequestMetrics()
      // TODO
      selector.unmute(send.destination)
    }
  }
    public void unmute() {
        //重新监听 OP_READ
        transportLayer.addInterestOps(SelectionKey.OP_READ);
    }


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值