接上篇
处理请求
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);
}