https://www.cnblogs.com/geason/p/5781179.html
需要好幾張圖來理清這裏的連接,線程池,後端處理之間的關係。
關於 NIO
https://blog.csdn.net/justsomebody126/article/details/104545152
https://blog.csdn.net/justsomebody126/article/details/104545371
一個 NIO Server demo
Java 的 NIO 是多路複用的,一個線程負責多個連接。
public class NIOServer {
private int num;
private static final int BLOCK = 2048;
private static final ByteBuffer sendB = ByteBuffer.allocate(BLOCK);
private static final ByteBuffer receB = ByteBuffer.allocate(BLOCK);
private Selector selector;
public NIOServer(int port) throws IOException {
//開啓ServerSocketChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//設置爲非阻塞
serverSocketChannel.configureBlocking(false);
//獲取ServerSocket
ServerSocket serverSocket = serverSocketChannel.socket();
//綁定ServerSocket提供服務的端口
serverSocket.bind(new InetSocketAddress(port));
//開啓選擇器
selector = Selector.open();
//將ServerSocketChannel註冊到選擇器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
System.out.println("NOIServer start run in port " + port);
}
/**
* 監聽選擇器的數據
*
* @throws IOException
*/
private void listen() throws IOException {
//循環監聽,事件驅動模式
while (true) {
//select()阻塞,等待有事件發生時喚醒
selector.select();
Set<SelectionKey> selectionKeys = selector.selectedKeys();
Iterator<SelectionKey> iterator = selectionKeys.iterator();
while (iterator.hasNext()) {
SelectionKey selectionKey = iterator.next();
//處理完後移除該事件
iterator.remove();
//處理該事件
handleKey(selectionKey);
}
}
}
/**
* 處理選擇器的監聽事件
*
* @param selectionKey 選擇器的監聽事件key
* @throws IOException
*/
private void handleKey(SelectionKey selectionKey) throws IOException {
ServerSocketChannel serverSocketChannel = null;
SocketChannel socketChannel = null;
int count = 0;
//客戶端新連接
if (selectionKey.isAcceptable()) {
//開啓通道連接
serverSocketChannel = (ServerSocketChannel) selectionKey.channel();
socketChannel = serverSocketChannel.accept();
//設置爲非阻塞
socketChannel.configureBlocking(false);
//將通道註冊到選擇器
socketChannel.register(selector, SelectionKey.OP_READ);
} else if (selectionKey.isReadable()) {
//獲取讀事件通道
socketChannel = (SocketChannel) selectionKey.channel();
//清除原先讀緩存
receB.clear();
//讀取通道緩存
count = socketChannel.read(receB);
if (count > 0) {
//解析通道緩存數據
String receMsg = new String(receB.array(), 0, count);
System.out.println("receive from client " + receMsg);
//註冊切到寫事件
socketChannel.register(selector, SelectionKey.OP_WRITE);
}
} else if (selectionKey.isWritable()) {
//獲取寫事件通道
socketChannel = (SocketChannel) selectionKey.channel();
//清除發送緩存數據
sendB.clear();
String sendMsg = "num " + num++;
//設置待發送的數據
sendB.put(sendMsg.getBytes());
//準備寫
sendB.flip();
int write = socketChannel.write(sendB);
System.out.println("send to client " + sendMsg);
//註冊切到讀事件
socketChannel.register(selector, SelectionKey.OP_READ);
}
}
public static void main(String[] args) throws Exception {
new NIOServer(9999).listen();
}
}
Mycat 處理用戶連接的類
NIOAcceptor
ServerConnection (extends FrontendConnection)
ServerConnection
ServerConnection 繼承自 FrontendConnection 繼承自 AbstractConnection
public class ServerConnection extends FrontendConnection {
public class ServerConnection extends FrontendConnection {
public void execute(String sql, int type) {
//連接狀態檢查
if (this.isClosed()) {
LOGGER.warn("ignore execute ,server connection is closed " + this);
return;
}
// 事務狀態檢查
if (txInterrupted) {
writeErrMessage(ErrorCode.ER_YES,
"Transaction error, need to rollback." + txInterrputMsg);
return;
}
// 檢查當前使用的DB
String db = this.schema;
boolean isDefault = true;
if (db == null) {
db = SchemaUtil.detectDefaultDb(sql, type);
if (db == null) {
db = MycatServer.getInstance().getConfig().getUsers().get(user).getDefaultSchema();
if (db == null) {
writeErrMessage(ErrorCode.ERR_BAD_LOGICDB,
"No MyCAT Database selected");
return ;
}
}
isDefault = false;
}
// 兼容PhpAdmin's, 支持對MySQL元數據的模擬返回
//// TODO: 2016/5/20 支持更多information_schema特性
// if (ServerParse.SELECT == type
// && db.equalsIgnoreCase("information_schema") ) {
// MysqlInformationSchemaHandler.handle(sql, this);
// return;
// }
if (ServerParse.SELECT == type
&& sql.contains("mysql")
&& sql.contains("proc")) {
SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);
if (schemaInfo != null
&& "mysql".equalsIgnoreCase(schemaInfo.schema)
&& "proc".equalsIgnoreCase(schemaInfo.table)) {
// 兼容MySQLWorkbench
MysqlProcHandler.handle(sql, this);
return;
}
}
SchemaConfig schema = MycatServer.getInstance().getConfig().getSchemas().get(db);
if (schema == null) {
writeErrMessage(ErrorCode.ERR_BAD_LOGICDB,
"Unknown MyCAT Database '" + db + "'");
return;
}
//fix navicat SELECT STATE AS `State`, ROUND(SUM(DURATION),7) AS `Duration`, CONCAT(ROUND(SUM(DURATION)/*100,3), '%') AS `Percentage` FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID= GROUP BY STATE ORDER BY SEQ
if(ServerParse.SELECT == type &&sql.contains(" INFORMATION_SCHEMA.PROFILING ")&&sql.contains("CONCAT(ROUND(SUM(DURATION)/"))
{
InformationSchemaProfiling.response(this);
return;
}
/* 當已經設置默認schema時,可以通過在sql中指定其它schema的方式執行
* 相關sql,已經在mysql客戶端中驗證。
* 所以在此處增加關於sql中指定Schema方式的支持。
*/
if (isDefault && schema.isCheckSQLSchema() && isNormalSql(type)) {
SchemaUtil.SchemaInfo schemaInfo = SchemaUtil.parseSchema(sql);
if (schemaInfo != null && schemaInfo.schema != null && !schemaInfo.schema.equals(db)) {
SchemaConfig schemaConfig = MycatServer.getInstance().getConfig().getSchemas().get(schemaInfo.schema);
if (schemaConfig != null)
schema = schemaConfig;
}
}
routeEndExecuteSQL(sql, type, schema);
}
}