此文已由作者张镐薪授权网易云社区发布。

欢迎访问网易云社区,了解更多网易技术产品运营经验。

3. 连接模块

3.3 AbstractConnection:

3.3.2 NIOHandler

NIOHandler实际上就是对于业务处理方法的封装,对于不同的连接有不同的处理方法,也就是不同的NIOHandler

  1. public interface NIOHandler { void handle(byte[] data);
  2. }

它的实现以及子类会在之后的对应的处理模块细讲。

3.3.3 NIOSocketWR

实现对于AbstractConnection(实际就是对里面封装的channel)进行异步读写,将从channel中读取到的放到AbstractConnection的readBuffer中,将writeBuffer和写队列中的数据写入到channel中。可以这么说,AbstractConnection的方法只对它里面的buffer进行操作,而buffer与channel之间的交互,是通过NIOSocketWR的方法完成的。
下面是它的方法以及对应的说明:

  1.     public void register(Selector selector) throws IOException {        try {
  2.             processKey = channel.register(selector, SelectionKey.OP_READ, con);
  3.         } finally {            if (con.isClosed.get()) {
  4.                 clearSelectionKey();
  5.             }
  6.         }
  7.     }    private void clearSelectionKey() {        try {
  8.             SelectionKey key = this.processKey;            if (key != null && key.isValid()) {
  9.                 key.attach(null);
  10.                 key.cancel();
  11.             }
  12.         } catch (Exception e) {
  13.             AbstractConnection.LOGGER.warn("clear selector keys err:" + e);
  14.         }
  15.     }

调用关系:这个方法就是之前讲的AbstractionConnection与RW线程绑定,AbstractionConnection封装的channel需要在RW线程的selector上注册读事件以监听读事件。

  1.     public void doNextWriteCheck() {        //检查是否正在写,看CAS更新writing值是否成功
  2.         if (!writing.compareAndSet(false, true)) {            return;
  3.         }        try {            //利用缓存队列和写缓冲记录保证写的可靠性,返回true则为全部写入成功
  4.             boolean noMoreData = write0();            //因为只有一个线程可以成功CAS更新writing值,所以这里不用再CAS
  5.             writing.set(false);            //如果全部写入成功而且写入队列为空(有可能在写入过程中又有新的Bytebuffer加入到队列),则取消注册写事件
  6.             //否则,继续注册写事件
  7.             if (noMoreData && con.writeQueue.isEmpty()) {                if ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) != 0)) {
  8.                     disableWrite();
  9.                 }
  10.  
  11.             } else {                if ((processKey.isValid() && (processKey.interestOps() & SelectionKey.OP_WRITE) == 0)) {
  12.                     enableWrite(false);
  13.                 }
  14.             }
  15.  
  16.         } catch (IOException e) {            if (AbstractConnection.LOGGER.isDebugEnabled()) {
  17.                 AbstractConnection.LOGGER.debug("caught err:", e);
  18.             }
  19.             con.close("err:" + e);
  20.         }
  21.  
  22.     }    private boolean write0() throws IOException {        int written = 0;
  23.         ByteBuffer buffer = con.writeBuffer;        if (buffer != null) {            //只要写缓冲记录中还有数据就不停写入,但如果写入字节为0,证明网络繁忙,则退出
  24.             while (buffer.hasRemaining()) {
  25.                 written = channel.write(buffer);                if (written > 0) {
  26.                     con.netOutBytes += written;
  27.                     con.processor.addNetOutBytes(written);
  28.                     con.lastWriteTime = TimeUtil.currentTimeMillis();
  29.                 } else {                    break;
  30.                 }
  31.             }            //如果写缓冲中还有数据证明网络繁忙,计数并退出,否则清空缓冲
  32.             if (buffer.hasRemaining()) {
  33.                 con.writeAttempts++;                return false;
  34.             } else {
  35.                 con.writeBuffer = null;
  36.                 con.recycle(buffer);
  37.             }
  38.         }        //读取缓存队列并写channel
  39.         while ((buffer = con.writeQueue.poll()) != null) {            if (buffer.limit() == 0) {
  40.                 con.recycle(buffer);
  41.                 con.close("quit send");                return true;
  42.             }
  43.             buffer.flip();            while (buffer.hasRemaining()) {
  44.                 written = channel.write(buffer);                if (written > 0) {
  45.                     con.lastWriteTime = TimeUtil.currentTimeMillis();
  46.                     con.netOutBytes += written;
  47.                     con.processor.addNetOutBytes(written);
  48.                     con.lastWriteTime = TimeUtil.currentTimeMillis();
  49.                 } else {                    break;
  50.                 }
  51.             }            //如果写缓冲中还有数据证明网络繁忙,计数,记录下这次未写完的数据到写缓冲记录并退出,否则回收缓冲
  52.             if (buffer.hasRemaining()) {
  53.                 con.writeBuffer = buffer;
  54.                 con.writeAttempts++;                return false;
  55.             } else {
  56.                 con.recycle(buffer);
  57.             }
  58.         }        return true;
  59.     }    private void disableWrite() {        try {
  60.             SelectionKey key = this.processKey;
  61.             key.interestOps(key.interestOps() & OP_NOT_WRITE);
  62.         } catch (Exception e) {
  63.             AbstractConnection.LOGGER.warn("can't disable write " + e + " con "
  64.                     + con);
  65.         }
  66.  
  67.     }    private void enableWrite(boolean wakeup) {        boolean needWakeup = false;        try {
  68.             SelectionKey key = this.processKey;
  69.             key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
  70.             needWakeup = true;
  71.         } catch (Exception e) {
  72.             AbstractConnection.LOGGER.warn("can't enable write " + e);
  73.  
  74.         }        if (needWakeup && wakeup) {
  75.             processKey.selector().wakeup();
  76.         }
  77.     }

这个doNextWriteCheck方法之前也讲过,看调用关系:第一个调用关系没意义,WriteEventCheckRunner这个类从没被调用过。
第二个调用很。。。就是将这个方法简单封装,估计是为了好修改,之后会提两种写策略对比。
第三个调用是主要调用,所有往AbstractionConnection中写入都会调用Abstraction.write(ByteBuffer),这个方法先把要写的放入缓存队列,之后调用上面这个doNextWriteCheck方法。
第四个和第五个都是定时检查任务,为了检查是否有AbstractionConnection的写缓存没有写完的情况

  1. @Override
  2.     public void asynRead() throws IOException {
  3.         ByteBuffer theBuffer = con.readBuffer;        //如果buffer为空,证明被回收或者是第一次读,新分配一个buffer给AbstractConnection作为readBuffer
  4.         if (theBuffer == null) {
  5.             theBuffer = con.processor.getBufferPool().allocate();
  6.             con.readBuffer = theBuffer;
  7.         }        //从channel中读取数据,并且保存到对应AbstractConnection的readBuffer中,readBuffer处于write mode,返回读取了多少字节
  8.         int got = channel.read(theBuffer);        //调用处理读取到的数据的方法
  9.         con.onReadData(got);
  10.     }

这个方法之前也讲过,异步将channel中的数据读取到readBuffer中,之后调用对应AbstractConnection的处理方法。
调用关系:按理说,应该只有在RW线程检测到读事件之后,才会调用这个异步读方法。但是在FrontendConnection的register()方法和BackendAIOConnection的register()方法都调用了。这是因为这两个方法在正常工作情况下为了注册一个会先主动发一个握手包,另一个会先读取一个握手包。所以都会执行异步读方法。

免费体验云安全(易盾)内容安全、验证码等服务

更多网易技术、产品、运营经验分享请点击

相关文章:
【推荐】 Android输入法弹出时覆盖输入框问题
【推荐】 Android TV 开发(4)

数据库路由中间件MyCat - 源代码篇(6)的更多相关文章

  1. 数据库路由中间件MyCat - 源代码篇(1)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式.然后 ...

  2. 数据库路由中间件MyCat - 源代码篇(13)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 4.配置模块 4.2 schema.xml 接上一篇,接下来载入每个schema的配置(也就是每个MyCat ...

  3. 数据库路由中间件MyCat - 源代码篇(7)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.4 FrontendConnection前端连接 构造方法: public Fronte ...

  4. 数据库路由中间件MyCat - 源代码篇(15)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. public static void handle(String stmt, ServerConnectio ...

  5. 数据库路由中间件MyCat - 源代码篇(17)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 调用processInsert(sc,schema,sqlType,origSQL,tableName,pr ...

  6. 数据库路由中间件MyCat - 源代码篇(14)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 对于表的dataNode对应关系,有个特殊配置即类似dataNode="distributed(d ...

  7. 数据库路由中间件MyCat - 源代码篇(4)

    此文已由作者易国强授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...

  8. 数据库路由中间件MyCat - 源代码篇(2)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 2. 前端连接建立与认证 Title:MySql连接建立以及认证过程client->MySql:1.T ...

  9. 数据库路由中间件MyCat - 源代码篇(16)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 5. 路由模块 真正取得RouteResultset的步骤:AbstractRouteStrategy的ro ...

  10. 数据库路由中间件MyCat - 源代码篇(10)

    此文已由作者张镐薪授权网易云社区发布. 欢迎访问网易云社区,了解更多网易技术产品运营经验. 3. 连接模块 3.5 后端连接 3.5.2 后端连接获取与维护管理 还是那之前的流程, st=>st ...

随机推荐

  1. exception_action

    for i in range(3, -2, -1): try: print(4 / i) except Exception as e: print(Exception) print(e)

  2. Django项目高频使用文件

    数据库配置: MySQL数据库 DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'HOST': 'localhost' ...

  3. win7计划任务定时执行PHP脚本设置图解

    做php开发的朋友有时候会希望自己的电脑能每天定时的运行一下某个脚本,但定时执行php脚本这种概念似乎多半是在linux中才提到,下面这篇文章主要和大家分享一下在win7下如何设置计划任务,以实现定时 ...

  4. 创建spring管理的自定义注解

    转自: http://blog.csdn.net/wuqiqing_1/article/details/52763372 Annotation其实是一种接口.通过Java的反射机制相关的API来访问A ...

  5. 【python】使用python写windows服务

    背景 运维windows服务器的同学都知道,windows服务器进行批量管理的时候非常麻烦,没有比较顺手的工具,虽然saltstack和ansible都能够支持windows操作,但是使用起来总感觉不 ...

  6. python -- redis连接与使用

    前面我们简单介绍了redis nosql数据库,现在我们在python里面来使用redis. 一.python连接redis 在python中,要操作redis,目前主要是通过一个python-red ...

  7. 7-3 堆栈模拟队列(25 point(s)) 【数据结构】

    7-3 堆栈模拟队列(25 point(s)) 设已知有两个堆栈S1和S2,请用这两个堆栈模拟出一个队列Q. 所谓用堆栈模拟队列,实际上就是通过调用堆栈的下列操作函数: int IsFull(Stac ...

  8. 【Leetcode-easy】Remove Element

    思路:遍历数组,count保存下一个元素的位置,如果不与该元素相同,那么将该数保存在count位置,并且count++,否则,继续遍历. public int removeElement(int[] ...

  9. 更新TP-LINK路由器的外网IP到花生壳动态IP解析

    ------------------------------------------------------------------------------- 以下内容可能还是存在问题,等之后有时间再 ...

  10. CSS3咖啡制作全过程动画

    CSS3咖啡制作全过程动画是一款利用纯CSS3实现的咖啡制作全过程动画特效,从把咖啡豆导入杯子,到把咖啡煮好,整个动画还比较流畅. 源码:http://www.huiyi8.com/sc/8788.h ...