数据库路由中间件MyCat - 源代码篇(6)
此文已由作者张镐薪授权网易云社区发布。
欢迎访问网易云社区,了解更多网易技术产品运营经验。
3. 连接模块
3.3 AbstractConnection:
3.3.2 NIOHandler
NIOHandler实际上就是对于业务处理方法的封装,对于不同的连接有不同的处理方法,也就是不同的NIOHandler
public interface NIOHandler { void handle(byte[] data);
}
它的实现以及子类会在之后的对应的处理模块细讲。
3.3.3 NIOSocketWR
实现对于AbstractConnection(实际就是对里面封装的channel)进行异步读写,将从channel中读取到的放到AbstractConnection的readBuffer中,将writeBuffer和写队列中的数据写入到channel中。可以这么说,AbstractConnection的方法只对它里面的buffer进行操作,而buffer与channel之间的交互,是通过NIOSocketWR的方法完成的。
下面是它的方法以及对应的说明:
public void register(Selector selector) throws IOException { try {
processKey = channel.register(selector, SelectionKey.OP_READ, con);
} finally { if (con.isClosed.get()) {
clearSelectionKey();
}
}
} private void clearSelectionKey() { try {
SelectionKey key = this.processKey; if (key != null && key.isValid()) {
key.attach(null);
key.cancel();
}
} catch (Exception e) {
AbstractConnection.LOGGER.warn("clear selector keys err:" + e);
}
}
调用关系:这个方法就是之前讲的AbstractionConnection与RW线程绑定,AbstractionConnection封装的channel需要在RW线程的selector上注册读事件以监听读事件。
public void doNextWriteCheck() { //检查是否正在写,看CAS更新writing值是否成功
if (!writing.compareAndSet(false, true)) { return;
} try { //利用缓存队列和写缓冲记录保证写的可靠性,返回true则为全部写入成功
boolean noMoreData = write0(); //因为只有一个线程可以成功CAS更新writing值,所以这里不用再CAS
writing.set(false); //如果全部写入成功而且写入队列为空(有可能在写入过程中又有新的Bytebuffer加入到队列),则取消注册写事件
//否则,继续注册写事件
if (noMoreData && con.writeQueue.isEmpty()) { if ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) != 0)) {
disableWrite();
}
} else { if ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) == 0)) {
enableWrite(false);
}
}
} catch (IOException e) { if (AbstractConnection.LOGGER.isDebugEnabled()) {
AbstractConnection.LOGGER.debug("caught err:", e);
}
con.close("err:" + e);
}
} private boolean write0() throws IOException { int written = 0;
ByteBuffer buffer = con.writeBuffer; if (buffer != null) { //只要写缓冲记录中还有数据就不停写入,但如果写入字节为0,证明网络繁忙,则退出
while (buffer.hasRemaining()) {
written = channel.write(buffer); if (written > 0) {
con.netOutBytes += written;
con.processor.addNetOutBytes(written);
con.lastWriteTime = TimeUtil.currentTimeMillis();
} else { break;
}
} //如果写缓冲中还有数据证明网络繁忙,计数并退出,否则清空缓冲
if (buffer.hasRemaining()) {
con.writeAttempts++; return false;
} else {
con.writeBuffer = null;
con.recycle(buffer);
}
} //读取缓存队列并写channel
while ((buffer = con.writeQueue.poll()) != null) { if (buffer.limit() == 0) {
con.recycle(buffer);
con.close("quit send"); return true;
}
buffer.flip(); while (buffer.hasRemaining()) {
written = channel.write(buffer); if (written > 0) {
con.lastWriteTime = TimeUtil.currentTimeMillis();
con.netOutBytes += written;
con.processor.addNetOutBytes(written);
con.lastWriteTime = TimeUtil.currentTimeMillis();
} else { break;
}
} //如果写缓冲中还有数据证明网络繁忙,计数,记录下这次未写完的数据到写缓冲记录并退出,否则回收缓冲
if (buffer.hasRemaining()) {
con.writeBuffer = buffer;
con.writeAttempts++; return false;
} else {
con.recycle(buffer);
}
} return true;
} private void disableWrite() { try {
SelectionKey key = this.processKey;
key.interestOps(key.interestOps() & OP_NOT_WRITE);
} catch (Exception e) {
AbstractConnection.LOGGER.warn("can't disable write " + e + " con "
+ con);
}
} private void enableWrite(boolean wakeup) { boolean needWakeup = false; try {
SelectionKey key = this.processKey;
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
needWakeup = true;
} catch (Exception e) {
AbstractConnection.LOGGER.warn("can't enable write " + e);
} if (needWakeup && wakeup) {
processKey.selector().wakeup();
}
}
这个doNextWriteCheck方法之前也讲过,看调用关系:第一个调用关系没意义,WriteEventCheckRunner这个类从没被调用过。
第二个调用很。。。就是将这个方法简单封装,估计是为了好修改,之后会提两种写策略对比。
第三个调用是主要调用,所有往AbstractionConnection中写入都会调用Abstraction.write(ByteBuffer),这个方法先把要写的放入缓存队列,之后调用上面这个doNextWriteCheck方法。
第四个和第五个都是定时检查任务,为了检查是否有AbstractionConnection的写缓存没有写完的情况
@Override
public void asynRead() throws IOException {
ByteBuffer theBuffer = con.readBuffer; //如果buffer为空,证明被回收或者是第一次读,新分配一个buffer给AbstractConnection作为readBuffer
if (theBuffer == null) {
theBuffer = con.processor.getBufferPool().allocate();
con.readBuffer = theBuffer;
} //从channel中读取数据,并且保存到对应AbstractConnection的readBuffer中,readBuffer处于write mode,返回读取了多少字节
int got = channel.read(theBuffer); //调用处理读取到的数据的方法
con.onReadData(got);
}
这个方法之前也讲过,异步将channel中的数据读取到readBuffer中,之后调用对应AbstractConnection的处理方法。
调用关系:按理说,应该只有在RW线程检测到读事件之后,才会调用这个异步读方法。但是在FrontendConnection的register()方法和BackendAIOConnection的register()方法都调用了。这是因为这两个方法在正常工作情况下为了注册一个会先主动发一个握手包,另一个会先读取一个握手包。所以都会执行异步读方法。
更多网易技术、产品、运营经验分享请点击。
相关文章:
【推荐】 Android输入法弹出时覆盖输入框问题
【推荐】 Android TV 开发(4)
数据库路由中间件MyCat - 源代码篇(6)的更多相关文章
- 数据库路由中间件MyCat - 源代码篇(1)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...
- 数据库路由中间件MyCat - 源代码篇(13)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...
- 数据库路由中间件MyCat - 源代码篇(7)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...
- 数据库路由中间件MyCat - 源代码篇(15)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. public static void handle(String stmt, ServerConnectio ...
- 数据库路由中间件MyCat - 源代码篇(17)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...
- 数据库路由中间件MyCat - 源代码篇(14)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...
- 数据库路由中间件MyCat - 源代码篇(4)
此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(2)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...
- 数据库路由中间件MyCat - 源代码篇(16)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...
- 数据库路由中间件MyCat - 源代码篇(10)
此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...
随机推荐
- cocos2d-js添加艾盟插屏(通过jsb反射机制)
1.导入jar包 2.修改AndroidManifest.xml文件 添加: <activity android:name="com.xingka ...
- 性能测试--siege
siege 这是Linux系统下的一个测试工具,完全使用C语言实现,可以对HTTP和FTP服务器进行负载和性能测试.通过使用Siege 提供的功能,可以很容易的制定测试计划:包括规定使用并发用户数.重 ...
- JavaScript 如何创建search字段
<!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- imagick图片压缩。
选择一个合适的图片处理扩展包. 常见的扩展如GD,imagick,Gmagick. 老古董的GD丢掉吧,效率很低,而且压缩的图片体积很大=.= imagick是个不错的选择,在PHP的图片处理扩展 ...
- springmvc fastjson 反序列化时间格式化
第一种情况是从后台拿到数据,进行反序列化,反序列化格式时间:试了一下很多网上的方法,最后发现还是在实体类上面的日期字段加上如下注解,可以完成格式化操作,否则默认就都是时间戳的格式: @JSONFiel ...
- Java互斥语义的实现
锁 对象头(Object Header) HotSpot 虚拟机的对象头包括两部分信息:Mark Word(标记字段)和 Klass Pointer(类型指针) Mark Word 用于存储对象自 ...
- 《程序员代码面试指南》第八章 数组和矩阵问题 找到无序数组中最小的k 个数
题目 找到无序数组中最小的k 个数 java代码 package com.lizhouwei.chapter8; /** * @Description: 找到无序数组中最小的k 个数 * @Autho ...
- Java中的内存泄漏
[转]介绍Java中的内存泄漏 1. 什么是内存泄漏? 内存泄漏的定义:对象已经没有被应用程序使用,但是垃圾回收器没办法移除它们,因为还在被引用着. 要想理解这个定义,我们需要先了解一下对象在内存中的 ...
- bzoj5093: [Lydsy1711月赛]图的价值
不难想到考虑每个点的贡献,ans=n*sigema(1~n)i C(n-1,i)*(2^C(n-1,2))*i^k 直接套第二类斯特林拆柿子即可.提示:sigema(1~n)i C(n,i)*C(i, ...
- legend2---开发日志15(功能需求明确,设计好类和结构的好处是)
legend2---开发日志15(功能需求明确,设计好类和结构的好处是) 一.总结 一句话总结: 极快简化编程,节约大量时间 1.多个类型的物品,比如商店和寻宝的丹药,装备,特性书,英雄石等等 应该怎 ...