网络层主要的就是ServerSocketChannel和SocketChannel(请参照NIO2看此篇blog),讲Myca通讯层前首先得说几个概念:
FrontEnd/BackEnd:mycat将网络通讯分成了两部分,第一部分叫FrontEnd即负责接收业务方用户请求的部分;第二部分叫BackEnd即负责和后端数据库通讯的部分。
AIO/NIO/BIO:BIO是jdk1.4以前就提供的同步阻塞IO方式,NIO是JDK1.4以后开始提供的新的同步非阻塞IO方式,AIO是NIO2中提供的异步非阻塞IO方式。性能依次递增。
Connector/Acceptor:前者负责客户端发起连接,后者负责服务端接收请求。
Connection:表示连接的对象。
FrontendConnection:前端连接对象,在Reactor中被引用,负责接收前端数据,将数据导向可以处理该信息的Proccessor,并将数据库查询结果返回给客户端。
BackendConnection:后端连接对象,负责连接数据库,将查询sql发送给数据库并将查询结果返回。
FrontEnd部分mycat提供三种不同IO方式,BackEnd部分提供AIO的IO方式和数据库连接。FrontEnd的源码主要在net包下,BackEnd的源码主要在backend包下。
MycatServer在调用start()方法启动时会创建一个AIOReactor(当然也可能是NIOReactor),AIOReactor会打开Socket并进行监听,一旦客户端有数据出入则调用回调函数accept处理数据。这时它会用FrontendConnectionFactory(FrontEnd中负责将请求包装成FrontendConnection的是ServerConnectionFactory,它继承自FrontendConnectionFactory)的make方法包装socket。包装完成后会注册一个NIOProcessor到connection上,并调用register方法发送一个HandshakePacket包(此处的握手包不同于TCP连接是的握手概念)给客户端异步等待客户端回应。这样客户端就和mycat建立起了连接,后续的处理流程比如sql解析,sql路由到对应的流程会在后面的章节中讲到。
通讯层还是比较简单的,使用了BIO/NIO/AIO等socket读写方式,下面以NIOAcceptor和AIOAcceptor的源码比较下两者的不同:
NIOAcceptor.java
@Override
public void run() {
final Selector tSelector = this.selector;
//死循环
for (;;) {
++acceptCount;
try {
//NIO中selector的select方法,不断轮询注册在其上的channel,一旦有数据进来就返回,最多等待1秒
tSelector.select(1000L);
//获取返回selectedKeys
Set<SelectionKey> keys = tSelector.selectedKeys();
try {
for (SelectionKey key : keys) {
//一旦数据是可处理的就调用accept()方法处理
if (key.isValid() && key.isAcceptable()) {
accept();
} else {
key.cancel();
}
}
} finally {
keys.clear();
}
} catch (Exception e) {
LOGGER.warn(getName(), e);
}
}
}
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());
//注册processor发送命令进行通讯
NIOProcessor processor = (NIOProcessor) MycatServer.getInstance()
.nextProcessor();
c.setProcessor(processor);
//reactor线程池处理
NIOReactor reactor = reactorPool.getNextReactor();
reactor.postRegister(c);
} catch (Exception e) {
LOGGER.warn(getName(), e);
closeChannel(channel);
}
}
AIOAcceptor.java
private void accept(NetworkChannel channel, Long id) {
try {
FrontendConnection c = factory.make(channel);
c.setAccepted(true);
c.setId(id);
NIOProcessor processor = MycatServer.getInstance().nextProcessor();
c.setProcessor(processor);
c.register();
} catch (Exception e) {
LOGGER.error("AioAcceptorError", e);
closeChannel(channel);
}
}