文档章节

MyCat - 源代码篇(1)

张哈希_
 张哈希_
发布于 2016/04/24 11:42
字数 1521
阅读 24
收藏 0

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

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

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

前端与后端通信框架都为NIO/AIO,因为目前生产上用的linux发行版内核都没有真正实现网络上的AIO,如果应用用AIO的话可能比NIO还要慢一些,所以,我们这里只分析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服务器抓包:
这里写图片描述
这里写图片描述
流程是:

Created with Raphaël 2.1.0 MySql连接建立以及认证过程 client client MySql MySql 1.TCP连接请求 2.接受TCP连接 3.TCP连接建立 4.握手包HandshakePacket 5.认证包AuthPacket 6.如果验证成功,则返回OkPacket 7.默认会发送查询版本信息的包 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;
            }
        }
    }

© 著作权归作者所有

张哈希_
粉丝 7
博文 42
码字总数 67907
作品 0
房山
程序员
私信 提问
MyCat 看了这篇什么都会了

1、什么是MyCat 1、一个彻底开源的,面向企业应用开发的大数据库集群 2、支持事务、ACID、可以替代MySQL的加强版数据库 3、一个可以视为MySQL集群的企业级数据库,用来替代昂贵的Oracle集群 ...

木迪_2a4e
2018/09/17
0
0
高性能Mysql中间组件Mycat安装配置

高性能Mysql中间组件Mycat安装配置 由于公司项目需要使用到读写分离,但是官方版本的Mysql-proxy读写分离组件太不稳定且有很多BUG,最终决定使用Mycat作为中间组件,其实读写分离只是Mycat中...

幸运券发放
2018/05/02
0
0
MyCAT全局序列号-数据库方式

1.MyCat中的全局序列号介绍 在实现分库分表的情况下,数据库自增主键已无法保证自增主键的全局唯一。为此,MyCat 提供了全局 sequence,并且提供了包含本地配置和数据库配置等多种实现方式。...

HilaryHe
2018/07/23
0
0
使用MyCAT实现MySQL的分库分表、读写分离、主从切换

MyCAT及MySQL实例部署情况: MyCAT: IP:10.20.8.57,Port:3310/3311 MySQL : db1-M1,IP:10.20.8.126,Port:3306 db1-M2,IP:10.20.8.126,Port:3307 db2-M1,IP:10.25.80.7,Port:......

橡皮高
2018/05/08
0
0
mycat-1:初试mycat

学习资料http://blog.51cto.com/ygqygq2/1973653 Mycat是一个彻底开源面向企业应用开发的大数据库集群软件。 环境:mysql5.6 ip192.168.56.101 root/123456 (密码必须为这个,mycat文件默认...

超级梦想家
02/18
0
0

没有更多内容

加载失败,请刷新页面

加载更多

C 语言 二级指针的使用

#include <stdio.h>#include <stdlib.h>typedef struct node Node;struct node {int data;struct node* next;struct node* prev;};Node head;Node* insert(Node......

小张525
29分钟前
2
0
【大数据技术】——Hadoop(1)

什么是大数据 基本概念 《数据处理》 在互联网技术发展到现今阶段,大量日常、工作等事务产生的数据都已经信息化,人类产生的数据量相比以前有了爆炸式的增长,以前的传统的数据处理技术已经...

须臾之余
43分钟前
6
0
比特币从地址逆向计算私钥

区块链 区块链简介 说到比特币,就不得不提区块链。那什么是区块链呢? 区块链本质是一个数据集,只不过数据的组织采用了比较特殊的方式,就是把数据拆分为一块一块的小数据集。 为什么要进行...

trayvon
57分钟前
1
0
TypeScript……真香

写前端或者用 node 写命令行小工具一直采用的 es6 的语法,对于 TypeScript 则是秉持敬而远之的态度,毕竟团队中多推广一门语言所需要花费的精力都是让人望而却步的。所以对于 JavaScript 的...

郁也风
今天
3
0
shell基本案例

1、自定义rm linux系统的rm命令太危险,一不小心就会删除掉系统文件。 写一个shell脚本来替换系统的rm命令,要求当删除一个文件或者目录时,都要做一个备份,然后再删除。下面分两种情况,做...

寰宇01
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部