报文格式

这一节我们来讲Cobar Handshake的过程。

MySQL服务端和客户端交互的所有的包格式都是统一的,报文格式如下图:

MySQL报文的消息头共有4个字节,前3字节表示的是实际数据的长度(不包含消息头),并且字节是按照小端模式排放的。

第四个字节MySQL为了防止串包用的,其原理是每收到一个报文,都在sequence id上加1。如果数据库检测到sequence id是连续的,则表明没有串包,如果不连续,则表示串包,数据库会直接丢弃该连接。

小端模式就是低位字节排放在内存的低地址端,高位字节排放在内存的高地址端。

大端模式则相反。

下面是Handshake包的结构,括号内表示该字段的字节数:

seed部分是加密种子,分为前后两个部分,通过随机数生成。

源码分析

上一节我们分析到,当一个前端连接过来,并不是直接和selector绑定,而是先插入到R线程的注册队列中,这样能释放NIOAcceptor的压力,处理更多前端连接。所以,连接和selector的绑定过程是在R线程中进行的,由register方法实现,代码如下:

private void register(Selector selector) {

NIOConnection c = null;

while ((c = registerQueue.poll()) != null) {

try {

c.register(selector);

} catch (Throwable e) {

c.error(ErrorCode.ERR_REGISTER, e);

}

}

}

实际的绑定操作是由NIOConnection的register方法实现的,NIOConnection接口的抽象类是AbstractConnection,我们来看它实现的register方法:

@Override

public void register(Selector selector) throws IOException {

try {

// 该连接只监听socket可读事件

processKey = channel.register(selector, SelectionKey.OP_READ, this);

isRegistered = true;

} finally {

if (isClosed.get()) {

clearSelectionKey();

}

}

}

我们发现,前端连接注册选择器时,只监听了可读事件。这是考虑到,Java的NIO属于水平触发LT(只要满足条件,就触发一个事件),使用水平触发时,如果应用程序不需要写就不要关注socket可写的事件,否则就会无限次地立即返回write ready notification,长期关注socket可写事件会出现CPU打满的情况,所以在使用JDK的NIO编程时,如果没有数据往外写,就取消写事件,有数据往外写时再注册写事件。

FrontendConnection继承了AbstractConnection,它又重新实现了register方法,代码如下:

@Override

public void register(Selector selector) throws IOException {

// 调用父类的register方法

super.register(selector);

if (!isClosed.get()) {

// 生成认证数据

byte[] rand1 = RandomUtil.randomBytes(8);

byte[] rand2 = RandomUtil.randomBytes(12);

// 保存认证数据

byte[] seed = new byte[rand1.length + rand2.length];

System.arraycopy(rand1, 0, seed, 0, rand1.length);

System.arraycopy(rand2, 0, seed, rand1.length, rand2.length);

this.seed = seed;

// 发送握手数据包

HandshakePacket hs = new HandshakePacket();

hs.packetId = 0;

hs.protocolVersion = Versions.PROTOCOL_VERSION;

hs.serverVersion = Versions.SERVER_VERSION;

hs.threadId = id;

hs.seed = rand1;

hs.serverCapabilities = getServerCapabilities();

hs.serverCharsetIndex = (byte) (charsetIndex & 0xff);

hs.serverStatus = 2;

hs.restOfScrambleBuff = rand2;

// 异步写入Handshake包

hs.write(this);

}

}

该方法生成了HandShake包,和上面结构图相一致,关键是最后异步写入HandShake包的write方法,代码如下:

public void write(FrontendConnection c) {

// 分配缓存

ByteBuffer buffer = c.allocate();

// 将HandShake包写入缓存

BufferUtil.writeUB3(buffer, calcPacketSize());

buffer.put(packetId);

buffer.put(protocolVersion);

BufferUtil.writeWithNull(buffer, serverVersion);

BufferUtil.writeUB4(buffer, threadId);

BufferUtil.writeWithNull(buffer, seed);

BufferUtil.writeUB2(buffer, serverCapabilities);

buffer.put(serverCharsetIndex);

BufferUtil.writeUB2(buffer, serverStatus);

buffer.put(FILLER_13);

// buffer.position(buffer.position() + 13);

BufferUtil.writeWithNull(buffer, restOfScrambleBuff);

// 将ByteBuffer中的数据异步写入Socket

c.write(buffer);

}

我们再来看最后一行的write方法:

@Override

public void write(ByteBuffer buffer) {

// 检查连接是否关闭,若关闭则将缓存回收

if (isClosed.get()) {

processor.getBufferPool().recycle(buffer);

return;

}

if (isRegistered) {

try {

// 将缓存先插入对队列中,其实就是一个循环数组,如数组已满,则 wait;

// 这个队列是AbstractConnection的一个成员变量

writeQueue.put(buffer);

} catch (InterruptedException e) {

error(ErrorCode.ERR_PUT_WRITE_QUEUE, e);

return;

}

// 插入队列后,调用NIOProcessor的postWrite方法,其实就是NIOReacor的postWrite方法

processor.postWrite(this);

} else {

// 若连接未注册,也回收缓存

processor.getBufferPool().recycle(buffer);

close();

}

}

我们看NIOReactor的postWrite方法:

final void postWrite(NIOConnection c) {

reactorW.writeQueue.offer(c);

}

其实是将连接插入到W线程的writeQueue阻塞队列中,我们再来看W线程的run方法,

@Override

public void run() {

NIOConnection c = null;

for (;;) {

try {

if ((c = writeQueue.take()) != null) {

write(c);

}

} catch (Throwable e) {

LOGGER.warn(name, e);

}

}

}

private void write(NIOConnection c) {

try {

c.writeByQueue();

} catch (Throwable e) {

c.error(ErrorCode.ERR_WRITE_BY_QUEUE, e);

}

}

轮询阻塞队列,若队列不为空,则取出连接,基于队列写方法writeByQueue将缓存中的数据写入socket,下一节再分析writeByQueue方法。

总结

阅读源码后,发现Cobar从前端连接的accept并注册selector到发送Handshake包都是异步,本质是将连接插入到R线程和W线程的阻塞队列中,不立即进行注册和写操作,从而实现整个过程的异步化,提高了Cobar的吞吐量。

以上。

原文链接

java cobar_Cobar源码解析(二)相关推荐

  1. Java Executor源码解析(3)—ThreadPoolExecutor线程池execute核心方法源码【一万字】

    基于JDK1.8详细介绍了ThreadPoolExecutor线程池的execute方法源码! 上一篇文章中,我们介绍了:Java Executor源码解析(2)-ThreadPoolExecutor ...

  2. 【深度学习模型】智云视图中文车牌识别源码解析(二)

    [深度学习模型]智云视图中文车牌识别源码解析(二) 感受 HyperLPR可以识别多种中文车牌包括白牌,新能源车牌,使馆车牌,教练车牌,武警车牌等. 代码不可谓不混乱(别忘了这是职业公司的准产品级代码 ...

  3. Java Executor源码解析(7)—Executors线程池工厂以及四大内置线程池

    详细介绍了Executors线程池工具类的使用,以及四大内置线程池. 系列文章: Java Executor源码解析(1)-Executor执行框架的概述 Java Executor源码解析(2)-T ...

  4. Java HashSet源码解析

    本解析源码来自JDK1.7,HashSet是基于HashMap实现的,方法实现大都直接调用HashMap的方法 另一篇HashMap的源码解析文章 概要 实现了Set接口,实际是靠HashMap实现的 ...

  5. Java SPI 源码解析及 demo 讲解

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:Java实现QQ登录和微博登录个人原创+1博客:点击前往,查看更多 作者:JlDang 来源:https://s ...

  6. Java String源码解析

    String类概要 所有的字符串字面量都属于String类,String对象创建后不可改变,因此可以缓存共享,StringBuilder,StringBuffer是可变的实现 String类提供了操作 ...

  7. Java Thread 源码解析

    Thread 源码解析 线程的方法大部分都是使用Native使用,不允许应用层修改,是CPU调度的最基本单元.线程的资源开销相对于进程的开销是相对较少的,所以我们一般创建线程执行,而不是进程执行. T ...

  8. android网络框架retrofit源码解析二

    注:源码解析文章参考了该博客:http://www.2cto.com/kf/201405/305248.html 前一篇文章讲解了retrofit的annotation,既然定义了,那么就应该有解析的 ...

  9. erlang下lists模块sort(排序)方法源码解析(二)

    上接erlang下lists模块sort(排序)方法源码解析(一),到目前为止,list列表已经被分割成N个列表,而且每个列表的元素是有序的(从大到小) 下面我们重点来看看mergel和rmergel ...

  10. Kubernetes学习笔记之Calico CNI Plugin源码解析(二)

    女主宣言 今天小编继续为大家分享Kubernetes Calico CNI Plugin学习笔记,希望能对大家有所帮助. PS:丰富的一线技术.多元化的表现形式,尽在"360云计算" ...

最新文章

  1. 设置WebStorm像VSCode一样每行代码结尾自动格式化加入“;”分号(JavaScript、TypeScript格式化)
  2. 美国芯片简史:军方大力扶持下的产物 但一度被日 韩超越
  3. 大数据WEB阶段Spring框架 AOP面向切面编程(一)
  4. git和gitlab安装
  5. signature=0880bf79d1c426abd0c8ca4bc897d06f,index.html
  6. php检测是否存在敏感词,如何用PHP+Ajax判断是否有敏感词汇
  7. 智能优化算法:静电放电算法-附代码
  8. 拓端tecdat|R语言如何解决线性混合模型中畸形拟合(Singular fit)的问题
  9. 内网穿透外网访问内网 MySQL 数据库教程
  10. 无标号有根树计数与无标号无根树计数
  11. 域名注册网站服务比较
  12. 汽车零部件:供应生产仍需持续恢复中
  13. 汇集各种 webservice工厂,快递,ip,天气,身份证,手机,翻译,火车时刻,股票,邮编,二维码,公交,ISBN,ICP 查询接口 API
  14. python 画彩虹_python – 在matplotlib中,我如何绘制多色线,如彩虹
  15. 输入方向的流量控制 --ifb
  16. char *const p ,char const *p,const char *p的区别
  17. Apache站点下载大文件不完整原因及解决办法(128M自动中断)
  18. 国产ARM核心工控主板介绍
  19. AutoSAR系列讲解(入门篇)1.1-AutoSAR发展
  20. Incorrect string value: '\xE7\xBB\xA0\xEF\xBC\x84...' for column 'name_zh' at row 1

热门文章

  1. dnf 服务器每周维护,DNF:7.22官方公告出炉,凌晨2点维护7小时,新增3个活动有玄机?...
  2. 《密码编码学与网络安全》William Stalling著---学习笔记(一)【知识点速过】【传统密码+经典对称加密算法+经典公钥密码算法+密码学Hash函数】
  3. 淘宝技术这十年 -- 目录
  4. 动易CMS 实现ctrl+v粘贴图片并上传、word粘贴带图片
  5. SEO学习必上的网址大全
  6. 思科CCNP培训中OSPF协议之详细图解-IELAB
  7. 推荐一款绘画软件krita,开源正版免费,适合ps用户
  8. Python数据库sqlite3详解
  9. 热门文献|陈国生:实证化中医基础理论依据及应用
  10. syslinux制作U盘启动器