kafka.network.SocketServer分析
当Kafka启动时,会启动这个SocketServer来接收客户端的连接,处理客户端请求,发送响应。
这个类的注释说明了这个socket server的结构
/**
* An NIO socket server. The threading model is
* 1 Acceptor thread that handles new connections
* N Processor threads that each have their own selector and read requests from sockets
* M Handler threads that handle requests and produce responses back to the processor threads for writing.
*/
即
其中:
其中使用的类主要包括:
Acceptor : 它是一个SocketServer, 接受新的连接,并且分配连接给Processor
Processor: 读取请求,发送响应
Handler: 处理请求,产生响应。这里的Handler由kafka.server.RequestHandler实现。
RequestChannel: 它包括了一个request queue 和 一个 response queue. 是Handler和Processsor交互时使用的队列。Request由Processor放入RequestChannel, 由Handler取出,然后把Response放回RequestChannel.
Acceptor在接受连接后,就把相当的SocketChannel设成非阻塞模式。因此Processor对这些SocketChannel的读写都是使用Selector,采用非阻塞的处理模式。
问题:
(1) Acceptor是如何把新来的连接分配给对应的Processor,这个算法是什么?是round robin吗?
(2) Processor应该把对应的SocketChannel在自己的Selector上如何注册?
(3) Processor如何读取请求?
/*
* Process reads from ready sockets
*/
def read(key: SelectionKey) {
val socketChannel = channelFor(key) //获取可读的SocketChannel
var receive = key.attachment.asInstanceOf[Receive] //获取attach到SelectionKey的Receive对象
if(key.attachment == null) { //如果attachment是空,说明这是第一次读,就新建一个Receive对象,attach到这个SocketChannel的SelectionKey上。如果不是空,说明之前已经从中读了一些数据,只是没读完。
receive = new BoundedByteBufferReceive(maxRequestSize)
key.attach(receive)
}
val read = receive.readFrom(socketChannel) //从SocketChannel中读数据
val address = socketChannel.socket.getRemoteSocketAddress();
trace(read + " bytes read from " + address)
if(read < 0) { //如果读的数据数小于0,就关闭socket连接。实际上从BoundedByteBufferReceive的实现来看,read的值不会小于0
close(key)
} else if(receive.complete) {//如果读完了,就构造request,发送给requestChannel
val req = RequestChannel.Request(processor = id, requestKey = key, buffer = receive.buffer, startTimeMs = time.milliseconds, remoteAddress = address)
requestChannel.sendRequest(req)
key.attach(null) //取消attach的Receive对象
// explicitly reset interest ops to not READ, no need to wake up the selector just yet
key.interestOps(key.interestOps & (~SelectionKey.OP_READ))//显示地把这个SocketChannel设为非OP_READ,等到Response发给这个SocketChannel以后,它会被再设为OP_READ,以继续处理来自这个SocketChannel的请求。
} else {//如果没有读完,就把这个SocketChannel注册为OP_READ,然后wakeup对应的selector,继续从SocketChannel中读数据。所以下一次再处理这个SocketChannel时,attach到它的SelectionKey的Receive对象就不是空了。
// more reading to be done
trace("Did not finish reading, registering for read again on connection " + socketChannel.socket.getRemoteSocketAddress())
key.interestOps(SelectionKey.OP_READ)
wakeup()
}
}
那么BoundedByteBufferReceive是如何知道一个请求读没读完呢?
原来每个Request的前4个字节标识了这个Request有多长,BoundedByteBufferReceive从SocketChannel中读取前4个字节,转换成整形,以这个整数为大小构造一个ByteBuffer,如果这个ByteBuffer没有写满,就说明请求的内容还没有读完。receive.complete就不被设为true,否则就说明这个Request已经从channel中完全读出。
if(!contentBuffer.hasRemaining) {
contentBuffer.rewind()
complete = true
}
Request Header (all single non-multi requests begin with this)
012301234567890123456789012345678901+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| REQUEST_LENGTH |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| REQUEST_TYPE | TOPIC_LENGTH |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+/ // TOPIC (variable length) /+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| PARTITION |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(4) Processor如何接收Handler产生的response?
kafka.network.SocketServer分析的更多相关文章
- Kafka Network层解析,还是有人把它说清楚了
我们知道kafka是基于TCP连接的.其并没有像很多中间件使用netty作为TCP服务器.而是自己基于Java NIO写了一套. 几个重要类 先看下Kafka Client的网络层架构. 本文主要分析 ...
- kafka Network
Kafka network Processor SocketServer.Processor override def run() { startupComplete() try { while (i ...
- Apache Kafka源码分析 – Broker Server
1. Kafka.scala 在Kafka的main入口中startup KafkaServerStartable, 而KafkaServerStartable这是对KafkaServer的封装 1: ...
- kafka.network.AbstractServerThread中的线程协作机制
这个虚类是kafka.network.Acceptor和kafka.network.Processor的父类,提供了一个抽象的Sever线程. 它的有趣之处在于为子类的启动和停止提供了线程间的协作机制 ...
- Kafka工作流程分析
Kafka工作流程分析 生产过程分析 写入方式 producer采用推(push)模式将消息发布到broker,每条消息都被追加(append)到分区(patition)中,属于顺序写磁盘(顺序写磁盘 ...
- Kafka源码分析系列-目录(收藏不迷路)
持续更新中,敬请关注! 目录 <Kafka源码分析>系列文章计划按"数据传递"的顺序写作,即:先分析生产者,其次分析Server端的数据处理,然后分析消费者,最后再补充 ...
- Kafka源码分析(一) - 概述
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 实际问题 二. 什么是Kafka, 如何解决这些问题的 三. 基本原理 1. 基本 ...
- Kafka源码分析(三) - Server端 - 消息存储
系列文章目录 https://zhuanlan.zhihu.com/p/367683572 目录 系列文章目录 一. 业务模型 1.1 概念梳理 1.2 文件分析 1.2.1 数据目录 1.2.2 . ...
- kafka源码分析之一server启动分析
0. 关键概念 关键概念 Concepts Function Topic 用于划分Message的逻辑概念,一个Topic可以分布在多个Broker上. Partition 是Kafka中横向扩展和一 ...
随机推荐
- JavaScript之表格修改
讲到表格,我们不免都了解它的属性及用途. colspan跨列(纵向的)和rowspan跨行(横向的). 表格中<tr></tr>标签标示行标签:<td></t ...
- TSQL基础(三)
字符处理函数 sql中常见的字符类型有: char(n) --长度固定 不可变 非Unicode 字符数据 n 必须是一个介于 1 和 8,000 之间的数值 nchar(n) --长度固定 不可变 ...
- R语言diagram包画订单状态流图
代码如下: library("diagram") #a <- read.table(file="clipboard",header=TRUE) write ...
- iOS app应用界面加载卡顿的问题
刚发布版本,忽然发现加载界面需要3-5秒延迟,那么问题来了. 首先,发现问题: 1.看代码,基于之前版本更新都没出问题,还是比较确信不是代码中的bug,以防万一,还是仔细看了下关于界面跳转部分的代码, ...
- OC9_文件操作
// // main.m // OC9_文件操作 // // Created by zhangxueming on 15/6/19. // Copyright (c) 2015年 zhangxuemi ...
- Windows 右键添加「cmd 打开」
1. 2. 3. 参考: 1.Windows右键添加"使用CMD打开" 2.WIN7.WIN8 右键在目录当前打开命令行Cmd窗口(图文)
- Codevs 2296 仪仗队 2008年省队选拔赛山东
2296 仪仗队 2008年省队选拔赛山东 时间限制: 1 s 空间限制: 256000 KB 题目等级 : 大师 Master 题解 题目描述 Description 作为体育委员,C君负责这次运动 ...
- C++ sizeof操作符的用法和strlen函数的区别
摘要:本人首先介绍了C++中sizeof操作符的用法和注意事项,其次对比了和strlen的区别和使用,方便大家在写代码的时候查阅,和面试.笔试的时候复习. 目录: sizeof的用法: sizeof和 ...
- 【转】VS2012发布网站详细步骤
1.打开你的VS2012网站项目,右键点击项目>菜单中 重新生成一下网站项目:再次点击右键>发布: 2.弹出网站发布设置面板,点击<新建..>,创建新的发布配置文件: 输入你自 ...
- 【转】c#文件操作大全(二)
61.文件夹移动到整合操作 FolderDialog aa = new FolderDialog(); aa.DisplayDialog(); if (aa ...