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

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

进入了源代码篇,我们先从整体入手,之后拿一个简单流程前端连接建立与认证作为例子,理清代码思路和设计模式。然后,针对每一个重点模块进行分析。

1. 整体通信与业务框架:

前端与后端通信框架都为NIO/AIO,因为目前生产上用的linux发行版内核都没有真正实现网络上的AIO,如果应用用AIO的话可能比NIO还要慢一些,所以,我们这里只分析NIO相关的通信模块。

  1. NIOAcceptor:作为服务器接受客户端连接(前端NIO通信)

  2. NIOConnector:作为客户端去连接后台数据库(MySql,后端NIO通信)

  3. NIOReactor:Reactor模式的NIO,处理并转发请求到RW线程,其实就是把对应AbstractConnection(就是NIO的channel的封装)注册到RW线程的selector上,只注册读标记;原因之后细讲

  4. NIOReactorPool:一般高性能网络通信框架采用多Reactor(多dispatcher)模式,这里将NIOReactor池化;每次NIOConnector接受一个连接或者NIOAcceptor请求一个连接,都会封装成AbstractConnection,同时请求NIOReactorPool每次轮询出一个NIOReactor,之后AbstractConnection与这个NIOReactor绑定(就是3之中说的注册)。

  5. RW:RW线程,负责执行NIO的channel读写,这里channel封装成了AbstractConnection

  6. NIOSocketWR:每个前端和后端连接都有一个对应的缓冲区,对连接读写操作具体如何操作的方法和缓存方式,封装到了这个类里面。

通过上面的分析,我们大致知道了通信是由谁负责的了,但是为什么NIOReactor只注册读标记?还有网络通信channel(之后的文章我们就都用AbstractConnection代替了)读写有线程执行了,但是中间的业务步骤,比如SQL拦截,SQL解析还有结果合并是谁执行呢?然后,还有些定时的任务,比如检查心跳连接等,如何执行呢?
首先,Reactor不会主动驱动写请求,写请求只会由业务步骤和定时任务触发。首先看,Reactor与前端AbstractConnection还有后端AbstractConnection,接收到的请求有两种,前端的SQL请求,还有后端的结果。但是这两种都不能直接转发,前端的SQL请求需要经过SQL解析等业务步骤才能写到后端,后端的结果也需要经过业务处理才能写到前端。所以只要执行业务步骤的线程去注册写标记,Reactor只要在检查到写标记后去写之后取消标记即可。定时任务同理。
那么谁去执行业务请求呢?MyCat会初始化一个BusinessExecutor线程池去处理业务请求,这个BusinessExecutor接受Reactor调度,定时任务由一个Timer线程调度并由一个TimerExecutor线程池执行。
整体结构如下所示,所有椭圆形的图形是线程或者进程(省略了很多,比如缓冲、缓存、连接以及对应的管理,这些之后会细细介绍):

2. 前端连接建立与认证

mysql客户端连接mysql服务器抓包:流程是:

Title:MySql连接建立以及认证过程client->MySql:1.TCP连接请求 
MySql->client:2.接受TCP连接client->MySql:3.TCP连接建立MySql->client:4.握手包HandshakePacketclient->MySql:5.认证包AuthPacketMySql->client:6.如果验证成功,则返回OkPacketclient->MySql:7.默认会发送查询版本信息的包MySql->client:8.返回结果包

在之后的协议分析,我们会深入每一个包进行分析

2.1 (1~3)TCP连接请求->接受TCP连接->TCP连接建立

首先,接受TCP连接(为了三次握手,上面流程的前三个包)需要通过NIOAcceptor实现,NIOAcceptor主要完成绑定端口,注册OP_ACCEPT监听客户端连接事件,有客户连接,则放接受连接,将返回的channel封装成为FrontendConnection(AbstarctConnection的子类),从NIOReactorPool中拿出一个NIOReactor并将FrontendConnection交给它绑定。
到此,NIOAcceptor就处理完一个客户端的连接请求。

public NIOAcceptor(String name, String bindIp, int port,
                       FrontendConnectionFactory factory, NIOReactorPool reactorPool)
            throws IOException {        super.setName(name);        this.port = port;        this.selector = Selector.open();        this.serverChannel =  ServerSocketChannel.open();        this.serverChannel.configureBlocking(false);        //设置TCP属性 
        serverChannel.setOption(StandardSocketOptions.SO_REUSEADDR, true);
        serverChannel.setOption(StandardSocketOptions.SO_RCVBUF, 1024 * 16 * 2);        // backlog=100
        serverChannel.bind(new InetSocketAddress(bindIp, port), 100);        //注册OP_ACCEPT,监听客户端连接
        this.serverChannel.register(selector, SelectionKey.OP_ACCEPT);        //FrontendConnectionFactory,用来封装channel成为FrontendConnection
        this.factory = factory;        //NIOReactor池
        this.reactorPool = reactorPool;
    }

构造器读取ip,端口,前端连接工厂和NIOReactor池,初始化TCP参数,并bind,在selector上注册OP_ACCEPT。
在NIOAcceptor启动后:

@Override
    public void run() {        final Selector tSelector = this.selector;        for (; ; ) {
            ++acceptCount;            try {                //轮询发现新连接请求
                tSelector.select(1000L);
                Set<SelectionKey> keys = tSelector.selectedKeys();                try {                    for (SelectionKey key : keys) {                        if (key.isValid() && key.isAcceptable()) {                            //接受连接操作
                            accept();
                        } else {
                            key.cancel();
                        }
                    }
                } finally {
                    keys.clear();
                }
            } catch (Exception e) {
                LOGGER.warn(getName(), e);
            }
        }
    }

NIOAcceptor这个线程不断轮询接受新的客户端连接请求,接受连接操作:

private void accept() {
        SocketChannel channel = null;        try {            //得到通信channel并设置为非阻塞
            channel = serverChannel.accept();
            channel.configureBlocking(false);            //封装channel为FrontendConnection
            FrontendConnection c = factory.make(channel);
            c.setAccepted(true);
            c.setId(ID_GENERATOR.getId());            //利用NIOProcessor管理前端链接,定期清除空闲连接,同时做写队列检查
            NIOProcessor processor = (NIOProcessor) MycatServer.getInstance()
                    .nextProcessor();
            c.setProcessor(processor);            //和具体执行selector响应感兴趣事件的NIOReactor绑定
            NIOReactor reactor = reactorPool.getNextReactor();
            reactor.postRegister(c);         } catch (Exception e) {
            LOGGER.warn(getName(), e);
            closeChannel(channel);
        }
    }

NIOProcessor持有所有的前后端连接,里面有空闲检查和写队列检查。RW线程,TimerExecutor线程池会执行里面的方法来实现空闲写入和定时连接检查等等。之后我还会详细介绍。
关闭Channel方法以及生成连接id方法很简单,就不加注释和赘述了。下面是他们的源代码:

private static void closeChannel(SocketChannel channel) {        if (channel == null) {            return;
        }
        Socket socket = channel.socket();        if (socket != null) {            try {
                socket.close();
            } catch (IOException e) {
                LOGGER.error("closeChannelError", e);
            }
        }        try {
            channel.close();
        } catch (IOException e) {
            LOGGER.error("closeChannelError", e);
        }
    }    /**
     * 前端连接ID生成器
     *
     * @author mycat
     */
    private static class AcceptIdGenerator {        private static final long MAX_VALUE = 0xffffffffL;        private long acceptId = 0L;        private final Object lock = new Object();        private long getId() {            synchronized (lock) {                if (acceptId >= MAX_VALUE) {
                    acceptId = 0L;
                }                return ++acceptId;
            }
        }
    }

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

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

相关文章:
【推荐】 一个小白的测试环境docker化之路
【推荐】 网易七鱼 Android 高性能日志写入方案
【推荐】 浅谈由管理者角色引出的B端产品设计思考点

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

随机推荐

  1. HDFS relaxes a few POSIX requirements to enable streaming access to file system data

    https://hadoop.apache.org/docs/r2.7.2/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html Introduction [ ...

  2. [证书服务器 第二篇] 基于OpenSSL 在 CentOS6 系统上 搭建自签证书服务,并应用于Web容器

    第一部分:概述 .. 第二部分:环境准备 1 操作系统 CentOS 6.x 2 安装openssl yum install -y openssl 3 安装jdk 从官网下载JDK http://ww ...

  3. fastjson(转)

    博客地址 :http://blog.csdn.NET/shulianghan/article/details/41011605 fastjson 源码地址 : -- GitHub : https:// ...

  4. nginx语法之location详解

    Location语法优先级排列 匹配符 匹配规则 优先级 = 精确匹配 ^~ 以某个字符串开头 ~ 区分大小写的正则匹配 ~* 不区分大小写的正则匹配 !~ 区分大小写不匹配的正则 !~* 不区分大小 ...

  5. 7-4 汉密尔顿回路(25 分) 【STL】

    7-4 汉密尔顿回路(25 分) 著名的"汉密尔顿(Hamilton)回路问题"是要找一个能遍历图中所有顶点的简单回路(即每个顶点只访问 1 次).本题就要求你判断任一给定的回路是 ...

  6. mini2440移植uboot 2011.03(下)

    参考博文: <u-boot-2011.03在mini2440/micro2440上的移植> 移植(五)添加nand支持: host@debian:~/soft/mini2440/u-boo ...

  7. 在WAMPSERVER下增加多版本的PHP(PHP5.3,PHP5.4,PHP5.5 ,PHP5.6)支持。

    本人预装了一个wamp的集成环境(Apache 2.4.9 + PHP 5.5.12 + mysql 5.6.17),今天在wamp环境下 添加PHP多版本 (PHP 5.5.30).中间两个过程,1 ...

  8. ASP.NET 4.0 页面 ValidateRequest="false" 失效不起作用

    当ASP.NET 2.0升级到 ASP.NET 4.0后,页面的 ValidateRequest="false" 不起作用. 因为 ASP.NET 4.0 请求验证被提前到IHtt ...

  9. Contiki Process概述

    本文涉及到的Protothread机制知识,在http://www.cnblogs.com/songdechiu/p/5793717.html 一.进程类型 进程类型主要有协同式(cooperativ ...

  10. Hadoop- MapReduce分布式计算框架原理

    分布式计算: 原则:移动计算而尽可能减少移动数据(减少网络开销) 分布式计算其实就是将单台机器上的计算拓展到多台机器上并行计算. MapReduce是一种编程模型.Hadoop MapReduce采用 ...