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

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

3. 连接模块

如之前所述,MyCat的连接分为前端和后端,下面是连接基本相关类图:

3.1 ClosableConnection:

public interface ClosableConnection {    String getCharset();    //关闭连接
    void close(String reason);    boolean isClosed();    public void idleCheck();    long getStartupTime();    String getHost();    int getPort();    int getLocalPort();    long getNetInBytes();    long getNetOutBytes();
}

根据字面意思,一个可以关闭的连接需要实现关闭方法-_-,并且需要原因判断是否是正常关闭。MySQL的通信都需要指定字符集。MyCat服务器建立ServerSocket时输入的端口为服务器在其上面监听客户的连接,当有客户连接时,在随机选择一个没用的端口与客户端通信;建立客户socket时输入的为服务端的监听端口,在本地选择一个未用端口与服务器通信,至于服务器怎么知道和客户端的哪个端口通信,和客户端怎么知道和服务端的哪个端口通信(因为这两个端口都是随机生成的),tcp是采用"三次握手"建立连接,而udp则是每次发送信息时将端口号放在ip报文的数据段里面。所以,连接里面需要提供获得监听端口和服务端口的方法。此外,还需要检查连接是否为空闲状态(idle)。最后,需要一些统计数据。

3.2 NIOConnection:

public interface NIOConnection extends ClosableConnection {    //connected
    void register() throws IOException;    //处理数据
    void handle(byte[] data);    // 写出一块缓冲数据
    void write(ByteBuffer buffer); }

所有NIO的通信需要在多路复用选择器上注册channel,这里有个对应的register()方法需要实现。然后,读取和写入数据都需要通过缓冲。缓冲区(Buffer)就是在内存中预留指定大小的存储空间用来对输入/输出(I/O)的数据作临时存储,这部分预留的内存空间就叫做缓冲区,使用缓冲区有这么两个好处:

  1. 减少实际的物理读写次数

  2. 缓冲区在创建时就被分配内存,这块内存区域一直被重用,可以减少动态分配和回收内存的次数 读取到的数据需要经过处理,这里对应的就是handle(byte[])方法。

    3.3 AbstractConnection:

从上面的实体图,我们发现,AbstractConnection其实就是把Java的NetworkChannel进行封装,同时需要依赖其他几个类来完成他所需要的操作,如下:  其中,NIOProcessor是对AbstractConnection实现NIO读写的方法类,NIOHandler是处理AbstractConnection读取的数据的处理方法类,NIOSocketWR是执行以上方法的线程类。

  1. 3.3.1 NIOProcessor:

NIOProcessor的构建方法:

public NIOProcessor(String name, BufferPool bufferPool,
            NameableExecutor executor) throws IOException {        this.name = name;        this.bufferPool = bufferPool;        this.executor = executor;        this.frontends = new ConcurrentHashMap<Long, FrontendConnection>();        this.backends = new ConcurrentHashMap<Long, BackendConnection>();        this.commands = new CommandCount();
    }

调用位置:  MyCatServer.java

...
bufferPool = new BufferPool(processBuferPool, processBufferChunk,
                socketBufferLocalPercent / processorCount);
        businessExecutor = ExecutorUtil.create("BusinessExecutor",
                threadPoolSize);
...for (int i = 0; i < processors.length; i++) {
            processors[i] = new NIOProcessor("Processor" + i, bufferPool,
                    businessExecutor);
        }
...

每个MyCat实例会初始化processors个NIOProcessor,每个NIOProcessor公用同一个bufferPool和businessExecutor。 bufferPool是缓冲池,BufferPool这个类负责缓冲统一管理 businessExecutor如之前所述,是业务线程池。 NIOProcessor被池化,很简单,就是保存到数组中,通过MyCatServer的nextProcessor()方法轮询获取一个NIOProcessor,之后每个AbstractConnection通过setNIOProcessor方法,设置NIOProcessor。

public NIOProcessor nextProcessor() {        int i = ++nextProcessor;        if (i >= processors.length) {
            i = nextProcessor = 0;
        }        return processors[i];
    }

可以看出,每个AbstractConnection依赖于一个NIOProcessor,每个NIOProcessor保存着多个AbstractConnection。AbstractConnection分为FrontendConnection和BackendConnection被分别保存在NIOProcessor的frontends和backends这两个ConcurrentHashMap中。 用ConcurrentHashMap是因为NIOAcceptor和NIOConnector线程以及RW线程池都会访问这两个变量。 NIOProcessor其实主要负责连接资源的管理:  MyCat会定时检查前端和后端空闲连接,并清理和回收资源: MyCatServer.java:

// 处理器定时检查任务
    private TimerTask processorCheck() {        return new TimerTask() {            @Override
            public void run() {
                timerExecutor.execute(new Runnable() {                    @Override
                    public void run() {                        try {                            for (NIOProcessor p : processors) {
                                p.checkBackendCons();
                            }
                        } catch (Exception e) {
                            LOGGER.warn("checkBackendCons caught err:" + e);
                        }                     }
                });
                timerExecutor.execute(new Runnable() {                    @Override
                    public void run() {                        try {                            for (NIOProcessor p : processors) {
                                p.checkFrontCons();
                            }
                        } catch (Exception e) {
                            LOGGER.warn("checkFrontCons caught err:" + e);
                        }
                    }
                });
            }
        };
    }

检查前端连接,回收空闲资源:

    /**
     * 定时执行该方法,回收部分资源。
     */
    public void checkFrontCons() {
        frontendCheck();
    }    private void frontendCheck() {
        Iterator<Entry<Long, FrontendConnection>> it = frontends.entrySet()
                .iterator();        while (it.hasNext()) {
            FrontendConnection c = it.next().getValue();            // 删除空连接
            if (c == null) {
                it.remove();                this.frontendsLength.decrementAndGet();                continue;
            }            // 清理已关闭连接,否则空闲检查。
            if (c.isClosed()) {
                c.cleanup();
                it.remove();                this.frontendsLength.decrementAndGet();
            } else {                // very important ,for some data maybe not sent
                checkConSendQueue(c);
                c.idleCheck();
            }
        }
    }

在关闭前端连接时,会清理连接占用的缓存资源: FrontendConnection.java:

protected void cleanup() {        //回收读缓冲
        if (readBuffer != null) {
            recycle(readBuffer);            this.readBuffer = null;            this.readBufferOffset = 0;
        }        //回收写缓冲
        if (writeBuffer != null) {
            recycle(writeBuffer);            this.writeBuffer = null;
        }        //回收压缩协议栈编码解码队列
        if(!decompressUnfinishedDataQueue.isEmpty())
        {
            decompressUnfinishedDataQueue.clear();
        }        if(!compressUnfinishedDataQueue.isEmpty())
        {
            compressUnfinishedDataQueue.clear();
        }        //回收写队列
        ByteBuffer buffer = null;        while ((buffer = writeQueue.poll()) != null) {
            recycle(buffer);
        }
    }

后端连接检查,除了要清理已关闭的连接,还有要检查SQL执行时间是否超时:

    /**
     * 定时执行该方法,回收部分资源。
     */
    public void checkBackendCons() {
        backendCheck();
    }    // 后端连接检查
    private void backendCheck() {        long sqlTimeout = MycatServer.getInstance().getConfig().getSystem().getSqlExecuteTimeout() * 1000L;
        Iterator<Entry<Long, BackendConnection>> it = backends.entrySet().iterator();        while (it.hasNext()) {
            BackendConnection c = it.next().getValue();            // 删除空连接
            if (c == null) {
                it.remove();                continue;
            }            // SQL执行超时的连接关闭
            if (c.isBorrowed()
                    && c.getLastTime() < TimeUtil.currentTimeMillis()
                            - sqlTimeout) {
                LOGGER.warn("found backend connection SQL timeout ,close it "
                        + c);
                c.close("sql timeout");
            }            // 清理已关闭连接,否则空闲检查。
            if (c.isClosed()) {
                it.remove();             } else {                // very important ,for some data maybe not sent
                if (c instanceof AbstractConnection) {
                    checkConSendQueue((AbstractConnection) c);
                }
                c.idleCheck();
            }
        }
    }

同时,在检查连接是否关闭时,需要检查写队列是否为空。写队列不为空,证明还有请求没有响应。需要将写队列的剩余请求异步写出,通过NIOSocketWR。

    private void checkConSendQueue(AbstractConnection c) {        // very important ,for some data maybe not sent
        if (!c.writeQueue.isEmpty()) {
            c.getSocketWR().doNextWriteCheck();
        }
    }

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

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

相关文章:
【推荐】 类似gitlab代码提交的热力图怎么做?
【推荐】 JAVA虚拟机的类加载机制

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

  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. Mac下终端常用命令

    一.删除文件 1 打开终端应用程序 2 输入命令:sudo (空格) rm (空格)-r (空格)-f (空格)(注意-f后面还有空格),还要注意,全部小写. 3 把你要删的文件或者文件夹用mouse ...

  2. JAVA中equals()与==的区别详解

    在进行判断操作时,常常会用到==或者equals()进行等价判断,那么两者究竟有什么区别呢,下面整理一下个人理解. 简单介绍: ==是一种引用相等性比较符,判断引用到堆上同一个对象的两个引用是相等的. ...

  3. 我的Android进阶之旅------>真正在公司看几天代码的感触

    仅以此文来回顾这一周我的工作情况,以及由此而触发的感想. ============================================================= 来到新公司5天了, ...

  4. Django创建项目及app

    主要环境为python3.5,编译环境为pycharm 首先已经安装好Django相关的组件 1.首先创建Django程序: windows系统下pycharm创建步骤: File->New P ...

  5. BCH分叉是一次站队博弈

    BCH分叉在即,很多人说BCH本次分叉实质是大佬间的斗争,主要是本次BCH分叉主要分为两大派别: 一派以BCH用户量最大的客户端Bitcoin ABC开发组为主,要在11月15日展开硬分叉升级,主要升 ...

  6. main方法的参数

    敲例子的时候无意中把主方法的参数给落下了,当时没有发现,保存之后就去编译,运行了,通常情况下编译没有错误那胜利就在掌握之中了,没想到这次我竟然在"不一般"的行列中,编译无误,运行出 ...

  7. testng testcase失败重试

    简单介绍 需求场景:测试移动端应用,常会因为点击失效.网络延迟大等原因导致测试脚本失败.这时,需要自动重新运行失败的脚本,直到脚本成功通过或者到达限定重试次数. 解决方案:实现testng的IRetr ...

  8. 如何浏览github上所有的公开的项目?

    github 上面项目多如牛毛,没有维护的.没有意义的或太过偏门的项目也是数不胜数,所以直接按照字母或者更新顺序浏览实在没什么意义. 有一个做法是去 github 搜 awesome list,比如通 ...

  9. make和rest用法

    位置(position):下一个要读取或写入的数据的索引.缓冲区的位置不能为负,并且不能大于其限制(limit). 标记(mark)与重置(reset):标记是一个索引,通过Buffer中的mark( ...

  10. 使用.net实现ZooKeeper客户端

    最近在项目中用到ZooKeeper, 通过Java连接比较容易,.net项目就没那么容易,尤其对于不熟悉Linux的开发人员,这里写点搭建ZooKeeper测试环境的经验,供参考. 背景知识: Zoo ...