MyCat 学习笔记 第二十篇 . mycat 源代码分析 上

学了那么久的mycat使用,也该对 mycat 内部的架构与原理做一番了解。
简单理解起来,mycat就是数据库的中间件,就是做了数据报文的透传功能(数据库路由是另外一块内容),主要还使用NIO Reactor模式,先来看下mycat启动的时候做了哪些事情。

MyCat 学习笔记 第二十篇 . mycat 源代码分析 上_第1张图片
看了一下mycat 1.6版本的源码作理初步理解,不正确的地方还要大家指出。

io.mycat.MycatServer 初始化系统运行环境与服务配置参数

public void startup() throws IOException {

        SystemConfig system = config.getSystem();
        int processorCount = system.getProcessors();

        // server startup
        LOGGER.info("===============================================");
        LOGGER.info(NAME + " is ready to startup ...");
        String inf = "Startup processors ...,total processors:"
                + system.getProcessors() + ",aio thread pool size:"
                + system.getProcessorExecutor()
                + "    \r\n each process allocated socket buffer pool "
                + " bytes ,buffer chunk size:"
                + system.getProcessorBufferChunk()
                + "  buffer pool's capacity(buferPool/bufferChunk) is:"
                + system.getProcessorBufferPool()
                / system.getProcessorBufferChunk();
        LOGGER.info(inf);
        LOGGER.info("sysconfig params:" + system.toString());

        // startup manager
        ManagerConnectionFactory mf = new ManagerConnectionFactory();
        ServerConnectionFactory sf = new ServerConnectionFactory();
        SocketAcceptor manager = null;
        SocketAcceptor server = null;
        aio = (system.getUsingAIO() == 1);

        // startup processors
        int threadPoolSize = system.getProcessorExecutor();
        processors = new NIOProcessor[processorCount];
        long processBuferPool = system.getProcessorBufferPool();
        int processBufferChunk = system.getProcessorBufferChunk();
        int socketBufferLocalPercent = system.getProcessorBufferLocalPercent();
        bufferPool = new BufferPool(processBuferPool, processBufferChunk, system.getFrontSocketSoRcvbuf(),
                socketBufferLocalPercent / processorCount);
        businessExecutor = ExecutorUtil.create("BusinessExecutor",
                threadPoolSize);
        timerExecutor = ExecutorUtil.create("Timer", system.getTimerExecutor());
        listeningExecutorService = MoreExecutors.listeningDecorator(businessExecutor);

        for (int i = 0; i < processors.length; i++) {
            processors[i] = new NIOProcessor("Processor" + i, bufferPool,
                    businessExecutor);
        }

AIO与NIO的选择,目前AIO还没有在Linux上得到底层实现,觉得这块AIO是否有实现或是选用的必要? 更多BIO AIO NIO 的东西看这里 戳

if (aio) {
            LOGGER.info("using aio network handler ");
            asyncChannelGroups = new AsynchronousChannelGroup[processorCount];
            // startup connector
            connector = new AIOConnector();
            for (int i = 0; i < processors.length; i++) {
                asyncChannelGroups[i] = AsynchronousChannelGroup
                        .withFixedThreadPool(processorCount,
                                new ThreadFactory() {
                                    private int inx = 1;

                                    @Override
                                    public Thread newThread(Runnable r) {
                                        Thread th = new Thread(r);
                                        th.setName(BufferPool.LOCAL_BUF_THREAD_PREX
                                                + "AIO" + (inx++));
                                        LOGGER.info("created new AIO thread "
                                                + th.getName());
                                        return th;
                                    }
                                });

            }
            manager = new AIOAcceptor(NAME + "Manager", system.getBindIp(),
                    system.getManagerPort(), mf, this.asyncChannelGroups[0]);

            // startup server

            server = new AIOAcceptor(NAME + "Server", system.getBindIp(),
                    system.getServerPort(), sf, this.asyncChannelGroups[0]);

        } else {
            LOGGER.info("using nio network handler ");
            NIOReactorPool reactorPool = new NIOReactorPool(
                    BufferPool.LOCAL_BUF_THREAD_PREX + "NIOREACTOR",
                    processors.length);
            connector = new NIOConnector(BufferPool.LOCAL_BUF_THREAD_PREX
                    + "NIOConnector", reactorPool);
            ((NIOConnector) connector).start();

            manager = new NIOAcceptor(BufferPool.LOCAL_BUF_THREAD_PREX + NAME
                    + "Manager", system.getBindIp(), system.getManagerPort(),
                    mf, reactorPool);

            server = new NIOAcceptor(BufferPool.LOCAL_BUF_THREAD_PREX + NAME
                    + "Server", system.getBindIp(), system.getServerPort(), sf,
                    reactorPool);
        }

上面两段是
MycatServer启动的主要内容,更多细节可以直接看源码。

另外再来看下 mycat 如何执行SQL操作 ,上面的源代码中有明显的两个变量 manager (默认9066端口) 和 server (默认8066端口)。

io.mycat.net.NIOAcceptor 是个独立的线程,run方法里面从socketServerChannel 拿取外部链接。

@Override
    public void run() {
        final Selector tSelector = this.selector;
        for (;;) {
            ++acceptCount;
            try {
                tSelector.select(1000L);
                Set 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);
            }
        }
    }

在accept方法中make出一个抽像的前端链接,并设置具体的processer,同时指向对应的 reactor 进行选择处理

    private void accept() {
        SocketChannel channel = null;
        try {
            channel = serverChannel.accept();
            channel.configureBlocking(false);
            FrontendConnection c = factory.make(channel);
            c.setAccepted(true);
            c.setId(ID_GENERATOR.getId());
            NIOProcessor processor = (NIOProcessor) MycatServer.getInstance()
                    .nextProcessor();
            c.setProcessor(processor);

            NIOReactor reactor = reactorPool.getNextReactor();
            reactor.postRegister(c);

        } catch (Exception e) {
            LOGGER.warn(getName(), e);
            closeChannel(channel);
        }
    }

io.mycat.net.NIOReactor 中接受前后端过来 reactor 注册事件,并唤醒 reactor 的 nio selector

public final class NIOReactor {
    private static final Logger LOGGER = LoggerFactory.getLogger(NIOReactor.class);
    private final String name;
    private final RW reactorR;

    public NIOReactor(String name) throws IOException {
        this.name = name;
        this.reactorR = new RW();
    }

    final void startup() {
        new Thread(reactorR, name + "-RW").start();
    }

    final void postRegister(AbstractConnection c) {
        reactorR.registerQueue.offer(c);
        reactorR.selector.wakeup();
    }

io.mycat.net.NIOReactor 的内部类 RW 又是一个独立线程,在run方法里面调用 前后端connection的asyncRead() 和 doNextWriteCheck()方法

for (;;) {
                ++reactCount;
                try {
                    selector.select(500L);
                    register(selector);
                    keys = selector.selectedKeys();
                    for (SelectionKey key : keys) {
                        AbstractConnection con = null;
                        try {
                            Object att = key.attachment();
                            if (att != null) {
                                con = (AbstractConnection) att;
                                if (key.isValid() && key.isReadable()) {
                                    try {
                                        con.asynRead();
                                    } catch (IOException e) {
                                        con.close("program err:" + e.toString());
                                        continue;
                                    } catch (Exception e) {
                                        LOGGER.warn("caught err:", e);
                                        con.close("program err:" + e.toString());
                                        continue;
                                    }
                                }
                                if (key.isValid() && key.isWritable()) {
                                    con.doNextWriteCheck();
                                }
                            } else {
                                key.cancel();
                            }

来看下AbstractConnection 到底是什么鬼
MyCat 学习笔记 第二十篇 . mycat 源代码分析 上_第2张图片

io.mycat.net.NIOSocketWR 是NIOReactor.RW背后的asynRead方法,做两件事件:
1)申请 byte buffer
2)读取 byte 报文

@Override
    public void asynRead() throws IOException {
        ByteBuffer theBuffer = con.readBuffer;
        if (theBuffer == null) {
            theBuffer = con.processor.getBufferPool().allocate();
            con.readBuffer = theBuffer;
        }
        int got = channel.read(theBuffer);
        con.onReadData(got);
    }

在 connection.onReadData中会调用到 io.mycat.net.handler.FrontendCommandHandler 的 handle方法进行具体的前端命令操作

@Override
    public void handle(byte[] data)
    {
        if(source.getLoadDataInfileHandler()!=null&&source.getLoadDataInfileHandler().isStartLoadData())
        {
            MySQLMessage mm = new MySQLMessage(data);
            int  packetLength = mm.readUB3();
            if(packetLength+4==data.length)
            {
                source.loadDataInfileData(data);
            }
            return;
        }
        switch (data[4])
        {
            case MySQLPacket.COM_INIT_DB:
                commands.doInitDB();
                source.initDB(data);
                break;
            case MySQLPacket.COM_QUERY:
                commands.doQuery();
                source.query(data);
                break;
            case MySQLPacket.COM_PING:
                commands.doPing();
                source.ping();
                break;

再逐步往下层调用进入 io.mycat.server.ServerQueryHandler 的 query() 方法

@Override
    public void query(String sql) {

        ServerConnection c = this.source;
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug(new StringBuilder().append(c).append(sql).toString());
        }
        //
        int rs = ServerParse.parse(sql);
        int sqlType = rs & 0xff;

        switch (sqlType) {
        case ServerParse.EXPLAIN:
            ExplainHandler.handle(sql, c, rs >>> 8);
            break;
        case ServerParse.EXPLAIN2:
            Explain2Handler.handle(sql, c, rs >>> 8);
            break;
        case ServerParse.SET:
            SetHandler.handle(sql, c, rs >>> 8);
            break;
        case ServerParse.SHOW:
            ShowHandler.handle(sql, c, rs >>> 8);
            break;
        case ServerParse.SELECT:
            if(QuarantineHandler.handle(sql, c)){
                SelectHandler.handle(sql, c, rs >>> 8);
            }
            break;

由于这里是在看前端select操作行为,因此我们再来看下更具体的SelectHandler,里面区分了版本、数据库、用户、自增 以及最重要的默认操作形为——数据查询select.

public final class SelectHandler {

    public static void handle(String stmt, ServerConnection c, int offs) {
        int offset = offs;
        switch (ServerParseSelect.parse(stmt, offs)) {
        case ServerParseSelect.VERSION_COMMENT:
            SelectVersionComment.response(c);
            break;
        case ServerParseSelect.DATABASE:
            SelectDatabase.response(c);
            break;
        case ServerParseSelect.USER:
            SelectUser.response(c);
            break;
        case ServerParseSelect.VERSION:
            SelectVersion.response(c);
            break;
        case ServerParseSelect.SESSION_INCREMENT:
            SessionIncrement.response(c);
            break;
            case ServerParseSelect.SESSION_ISOLATION:
            SessionIsolation.response(c);
            break;
        case ServerParseSelect.LAST_INSERT_ID:
            // offset = ParseUtil.move(stmt, 0, "select".length());
            loop:for (int l=stmt.length(); offset < l; ++offset) {
                switch (stmt.charAt(offset)) {
                case ' ':
                    continue;
                case '/':
                case '#':
                    offset = ParseUtil.comment(stmt, offset);
                    continue;
                case 'L':
                case 'l':
                    break loop;
                }
            }
            offset = ServerParseSelect.indexAfterLastInsertIdFunc(stmt, offset);
            offset = ServerParseSelect.skipAs(stmt, offset);
            SelectLastInsertId.response(c, stmt, offset);
            break;
        ...
        ...
        ...
        default:
            c.execute(stmt, ServerParse.SELECT);

总结一下,整体上对mycat如何接收前端请求有一个了解,再整理一个时序图看得会更清晰一些
MyCat 学习笔记 第二十篇 . mycat 源代码分析 上_第3张图片

这篇先到这里,下篇接着讲重点哈~~

你可能感兴趣的:(Java,MySql,MyCat,架构)