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()方法即可。