致命药丸:一个可识别的对象,置于队列中意味着当你得到他的时候,停止一切服务,在先进先出队列中致命药丸可以保证之前的请求都被处理完毕,生产者不应该在提交致命药丸之后提交任何工作。
kafka服务端请求处理的大体流程:Acceptor线程接受客户端的请求并建立socket channel,然后将channel分发给Processor线程,Processor 线程消费阻塞队列中的socket channel并与之建立连接接受request,再将request放到request channel当中,之后KafkaRequestHandler将消费request channel中的request并对其进行下一步的处理。
其中包含一个典型的生产者消费者模型(其实是两个),Processor为生产者,KafkaRequestHandler为消费者,那么如何停止这个基于线程的服务呢?
KafkaRequestHandler的run方法,处理方法见注释
def run() {
while (!stopped) {
val startSelectTime = time.nanoseconds
val req = requestChannel.receiveRequest(300)
val endTime = time.nanoseconds
val idleTime = endTime - startSelectTime
aggregateIdleMeter.mark(idleTime / totalHandlerThreads.get)
req match {
//处理致命药丸,退出该线程
case RequestChannel.ShutdownRequest =>
debug(s"Kafka request handler $id on broker $brokerId received shut down command")
shutdownComplete.countDown()
return
case request: RequestChannel.Request =>
try {
request.requestDequeueTimeNanos = endTime
trace(s"Kafka request handler $id on broker $brokerId handling request $request")
apis.handle(request)
} catch {
case e: FatalExitError =>
shutdownComplete.countDown()
Exit.exit(e.statusCode)
case e: Throwable => error("Exception when handling request", e)
} finally {
request.releaseBuffer()
}
case null => // continue
}
}
shutdownComplete.countDown()
}
RequestChannel中用来停止服务的方法,处理方法见注释
//发送致命药丸
def sendShutdownRequest(): Unit = requestQueue.put(ShutdownRequest)