Mycat源码阅读(一)jdbc是如何和mycat创建连接,如何通信数据的

jdbc是如何和mycat创建连接,如何通信数据的

源码部分阅读,可从MycatStartup类开始读起。

public final class MycatStartup {
    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private static final Logger LOGGER = LoggerFactory.getLogger(MycatStartup.class);
    public static void main(String[] args) {
        //use zk ?
        ZkConfig.getInstance().initZk();
        try {
            String home = SystemConfig.getHomePath();
            if (home == null) {
                System.out.println(SystemConfig.SYS_HOME + "  is not set.");
                System.exit(-1);
            }
            // init,这里做了一些初始化工作,比如配置信息xml的读取
            MycatServer server = MycatServer.getInstance();
            //这个方法执行的代码,上面SystemConfig.getHomePath()已经执行过了,建议注释掉。
            //server.beforeStart();
            
            // startup,这里是一些主要线程的启动
            server.startup();
            System.out.println("Isolation Server startup successfully. see logs in logs/isolation.log");

        } catch (Exception e) {
            SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
            LOGGER.error(sdf.format(new Date()) + " startup error", e);
            System.exit(-1);
        }
    }
}

Startup()方法中的ManagerConnectionFactory和ServerConnectionFactory分别是ManagerConnection和ServerConnection的工厂类,所谓ManagerConnection是mycat对外提供的监控连接,ServerConnection是真正的sql查询的连接。

// startup manager
ManagerConnectionFactory mf = new ManagerConnectionFactory();   //监控模块
ServerConnectionFactory sf = new ServerConnectionFactory();     //服务模块
SocketAcceptor manager = null;
SocketAcceptor server = null;

继续往下读,发现创建了一个NIOProcessor的数组,这个数组在后面代码中做了初始化。

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

NIOProcessor主要作用是MyCat定时检查前端和后端空闲连接,并清理和回收资源的,每个连接都会从NIOProcessor池(数组)提取一个放入类中。NIOProcessor主要用于性能提升方面,我们可以跳过去。
再往下读,有一个AIO和NIO的区分,由于Linux系统并没有实现AIO,windows有实现AIO,所以做了区分,用户可根据实际情况,选择技术方式,官网上说,目前来看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);
                        //TODO
                        th.setName(DirectByteBufferPool.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(
            DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOREACTOR",
            processors.length);
    connector = new NIOConnector(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOConnector", reactorPool);
    ((NIOConnector) connector).start();

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

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

在NIO部分,主要初始化了一下三个重要的线程。

LOGGER.info("using nio network handler ");

NIOReactorPool reactorPool = new NIOReactorPool(
        DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOREACTOR",
        processors.length);
connector = new NIOConnector(DirectByteBufferPool.LOCAL_BUF_THREAD_PREX + "NIOConnector", reactorPool);
((NIOConnector) connector).start();

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

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

1)NIOReactorPool是NIOReactor池,其实就是数组,NIOReactor是网络事件反应器,主要负责连接的读写事件。类中有一个WR的私有类,WR继承Thread类,并有registerQueue一个连接注册队列,NIOReactor启动时,会初始化这个队列。Run方法主要如下

@Override
public void run() {
	int invalidSelectCount = 0;
	Set<SelectionKey> keys = null;
	for (;;) {
		++reactCount;
		try {
			final Selector tSelector = this.selector;
			long start = System.nanoTime();
			tSelector.select(500L);
			long end = System.nanoTime();
			register(tSelector);
			keys = tSelector.selectedKeys();
			if (keys.size() == 0 && (end - start) < SelectorUtil.MIN_SELECT_TIME_IN_NANO_SECONDS )
			{
				invalidSelectCount++;
			}
			else
			{
				invalidSelectCount = 0;
				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();
						}
					} catch (CancelledKeyException e) {
						if (LOGGER.isDebugEnabled()) {
							LOGGER.debug(con + " socket key canceled");
						}
					} catch (Exception e) {
						LOGGER.warn(con + " " + e);
					} catch (final Throwable e) {
						// Catch exceptions such as OOM and close connection if exists
						//so that the reactor can keep running!
						// @author Uncle-pan
						// @since 2016-03-30
						if (con != null) {
							con.close("Bad: " + e);
						}
						LOGGER.error("caught err: ", e);
						continue;
					}
				}
			}
			if (invalidSelectCount > SelectorUtil.REBUILD_COUNT_THRESHOLD)
			{
				final Selector rebuildSelector = SelectorUtil.rebuildSelector(this.selector);
				if (rebuildSelector != null)
				{
					this.selector = rebuildSelector;
				}
				invalidSelectCount = 0;
			}
		} catch (Exception e) {
			LOGGER.warn(name, e);
		} catch (final Throwable e){
			// Catch exceptions such as OOM so that the reactor can keep running!
              	// @author Uncle-pan
              	// @since 2016-03-30
			LOGGER.error("caught err: ", e);
		} finally {
			if (keys != null) {
				keys.clear();
			}

		}
	}
}

无限循环,register方法主要对新放入队列的链接,注册读事件,并执行连接的register方法。当Selector监听到连接有数据传来,调用AbstractConnection的asynRead()方法,及通过AbstractConnection中的NIOSocketWR类读取数据到AbstractConnection的readBuffer属性中,并触发onReadData()方法,详细在后面描述。当通道空闲时,触发写事件,调用AbstractConnection的doNextWriteCheck()方法,还是通过NIOSocketWR将数据通过channel输出。

/**
 * [简要描述]</br>
 * [详细描述]:对新放入队列的链接,注册读事件,并执行连接的register方法</br>
 * @param selector
 *
 */
private void register(Selector selector) {
	AbstractConnection c = null;
	if (registerQueue.isEmpty()) {
		return;
	}
	while ((c = registerQueue.poll()) != null) {
		try {
			((NIOSocketWR) c.getSocketWR()).register(selector);//注册读事件
			c.register();//类似于连接的初始化方法
		} catch (Exception e) {
			c.close("register err" + e.toString());
		}
	}
}

2)NIOConnector继承了Thread,主要负责MyCat连接 MySQL 的主动连接事件,启动后,主要执行下面代码:

@Override
public void run() {
	int invalidSelectCount = 0;
	for (;;) {
		final Selector tSelector = this.selector;
		++connectCount;
		try {
			long start = System.nanoTime();
			tSelector.select(1000L);
			long end = System.nanoTime();
			connect(tSelector);
			Set<SelectionKey> keys = tSelector.selectedKeys();
			if (keys.size() == 0 && (end - start) < SelectorUtil.MIN_SELECT_TIME_IN_NANO_SECONDS )
			{
				invalidSelectCount++;
			}
			else
			{
				try {
					for (SelectionKey key : keys)
					{
						Object att = key.attachment();
						if (att != null && key.isValid() && key.isConnectable())
						{
							finishConnect(key, att);
						} else
						{
							key.cancel();
						}
					}
				} finally
				{
					invalidSelectCount = 0;
					keys.clear();
				}
			}
			if (invalidSelectCount > SelectorUtil.REBUILD_COUNT_THRESHOLD)
			{
				final Selector rebuildSelector = SelectorUtil.rebuildSelector(this.selector);
				if (rebuildSelector != null)
				{
					this.selector = rebuildSelector;
				}
				invalidSelectCount = 0;
			}
		} catch (Exception e) {
			LOGGER.warn(name, e);
		}
	}
}

无限循环,每次循环都会执行connect方法,此方法主要是判断 connectQueue 中是否新的连接请求,如有则在 selector 中进行注册连接监听事件。
代码继续执行,如果selectkey中有连接事件,则执行finishConnect方法,此方法主要用于创建连接,从 reactorPool 中获得一个 NIOReactor,然后把连接传递到 NIOReactor,然后后续的 Read 和 Write 事件就交给 NIOReactor 处理了。
3)NIOAcceptor类继承Thread类,主要是创建一个selector事件选择器,处理jdbc向mycat的连接。在类构造器中,创建ServerSocketChannel,并绑定端口号,初始化selector,并将chanel注册到selector上,监听OP_ACCEPT事件,run方法无限循环,查看是否有连接事件,如果有则建立FrontendConnection连接,从reactorPool 中获得一个 NIOReactor,然后把连接传递到 NIOReactor,然后后续的 Read 和 Write 事件就交给NIOReactor 处理了。

/**
 * [简要描述]</br>
 * [详细描述]:当连接建立完毕后,从reactorPool 中获得一个 NIOReactor,然后把连接传递到 NIOReactor,然后后续的 Read  Write 事件就交给NIOReactor 处理了。</br>
 */
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) IsolationServer.getInstance()
				.nextProcessor();
		c.setProcessor(processor);
		
		NIOReactor reactor = reactorPool.getNextReactor();
		reactor.postRegister(c);

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

4) NIOSocketWR类,每一个connection都会初始化一个NIOSocketWR,用于该来连接的读写操作,

public AbstractConnection(NetworkChannel channel) {
	this.channel = channel;
	boolean isAIO = (channel instanceof AsynchronousChannel);
	if (isAIO) {
		socketWR = new AIOSocketWR(this);        //访问者模式
	} else {
		socketWR = new NIOSocketWR(this);        //访问者模式
	}
	this.isClosed = new AtomicBoolean(false);
	this.startupTime = TimeUtil.currentTimeMillis();
	this.lastReadTime = startupTime;
	this.lastWriteTime = startupTime;
}

类中主要方法有三个:
register(Selector selector)将channel注册到Selector上,做读事件监听。
doNextWriteCheck()将connection的writeQueue写队列里的数据通过channel输出。
asynRead()将channel中的数据读取到connection的readBuffer中。并通过onReadData方法通知connection。

结论:jdbc连接mycat,mycat通过NIOAcceptor监听到连接事件,new 一个FrontendConnection对象,从NIOReactor池中获取一个NIOReactor,将FrontendConnection添加到NIOReactor的registerQueue注册队列中,NIOReactor将新添加到队列的连接注册到selector上,监听读事件,当jdbc发送数据过来,NIOReactor通过asynRead方法通知connection,connection通过调用自身的NIOSocketWR的asynRead()方法将channel中的数据读取到readBuffer属性上,读取完毕后通过connection的onReadData方法告知connection。
Connection写数据则只需要把要写的数据放入writeQueue队列,而后调用NIOSocketWR的doNextWriteCheck()方法即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值