Mycat 的 NIO

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);
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章