The code snippet is as follows:
// startup server ServerConnectionFactory sf = new ServerConnectionFactory(); sf.setCharset(system.getCharset()); sf.setIdleTimeout(system.getIdleTimeout()); server = new NIOAcceptor(NAME + "Server", system.getServerPort(), sf); server.setProcessors(processors); server.start();
See how to do this, the processing code is
@Override public void run() { final Selector selector = this.selector; for (;;) { ++acceptCount; try { selector.select(1000L); Set<SelectionKey> keys = selector.selectedKeys(); try { for (SelectionKey key : keys) { if (key.isValid() && key.isAcceptable()) { accept(); } else { key.cancel(); } } } finally { keys.clear(); } } catch (Throwable e) { LOGGER.warn(getName(), e); } } }
Break the point
stop in com.baidu.hsb.net.NIOAcceptor.run
You can see
> VM Started: hsb.home-->/Users/baidu/tmp/hsb hsb.log.home-->/Users/baidu/tmp/hsb/logs hsb.conf-->/root/heisenberg/ loading properties file[/root/heisenberg/hsb.properties] backend NIO mode actived.. Set deferred breakpoint com.baidu.hsb.net.NIOAcceptor.run start hsb time:2401ms Breakpoint hit: "thread=HeisenbergManager", com.baidu.hsb.net.NIOAcceptor.run(), line=63 bci=0 63 final Selector selector = this.selector; HeisenbergManager[1]
View Stack
HeisenbergManager[1] where [1] com.baidu.hsb.net.NIOAcceptor.run (NIOAcceptor.java:63)
The key code is accept
@Override public void run() { // Start execution final Selector selector = this.selector; for (;;) { // Infinite loop ++acceptCount; try { selector.select(1000L); Set<SelectionKey> keys = selector.selectedKeys(); try { for (SelectionKey key : keys) { if (key.isValid() && key.isAcceptable()) { //Execute accept accept(); } else { key.cancel(); } } } finally { keys.clear(); } } catch (Throwable e) { LOGGER.warn(getName(), e); } } }
At accept breakpoint
stop in com.baidu.hsb.net.NIOAcceptor.accept
private void accept() { // There's a connection coming in SocketChannel channel = null; try { // Accept this connection channel = serverChannel.accept(); channel.configureBlocking(false);// Set to non-blocking // Establish Front End Connection FrontendConnection c = factory.make(channel); c.setAccepted(true); c.setId(ID_GENERATOR.getId()); NIOProcessor processor = nextProcessor(); c.setProcessor(processor); // Handle to a processor processor.postRegister(c); } catch (Throwable e) { closeChannel(channel); LOGGER.warn(getName(), e); } }
Track the code until you finally deliver the connection to
final void postRegister(NIOConnection c) { reactorR.registerQueue.offer(c); reactorR.selector.wakeup(); }
So, the question arises, who gets this connection from reactorR.registerQueue?
It suddenly occurred to me that this was the same as what netty and thrift had done before.
--Find clues in the code!There is such a function
private void register(Selector selector) { NIOConnection c = null; while ((c = registerQueue.poll()) != null) { try { c.register(selector); } catch (Throwable e) { c.error(ErrorCode.ERR_REGISTER, e); } } }
This function gets the connection from registerQueue, so interrupt
stop in com.baidu.hsb.net.NIOReactor$R.register
Success comes in, the key point is
c.register(selector);
This means registering the readable and writable state of the current connection with the selector
Breakpoint is
stop in com.baidu.hsb.net.FrontendConnection.register
hsb.conf-->/root/heisenberg/ loading properties file[/root/heisenberg/hsb.properties] backend NIO mode actived.. start hsb time:2164ms Set deferred breakpoint com.baidu.hsb.net.FrontendConnection.register Breakpoint hit: "thread=Processor1-R", com.baidu.hsb.net.FrontendConnection.register(), line=330 bci=0 330 super.register(selector); Processor1-R[1] step > Step completed: "thread=Processor1-R", com.baidu.hsb.net.AbstractConnection.register(), line=145 bci=0 145 processKey = channel.register(selector, SelectionKey.OP_READ, this); Processor1-R[1] step > Step completed: "thread=Processor1-R", com.baidu.hsb.net.AbstractConnection.register(), line=146 bci=14 146 isRegistered = true; Processor1-R[1] step > Step completed: "thread=Processor1-R", com.baidu.hsb.net.AbstractConnection.register(), line=148 bci=19 148 if (isClosed.get()) { Processor1-R[1] next > Step completed: "thread=Processor1-R", com.baidu.hsb.net.AbstractConnection.register(), line=152 bci=53 152 } Processor1-R[1] step > Step completed: "thread=Processor1-R", com.baidu.hsb.net.FrontendConnection.register(), line=331 bci=5 331 if (!isClosed.get()) { Processor1-R[1] next > Step completed: "thread=Processor1-R", com.baidu.hsb.net.FrontendConnection.register(), line=333 bci=15 333 byte[] rand1 = RandomUtil.randomBytes(8);
And then we're ready to shake hands
if (!isClosed.get()) { // Generate authentication data byte[] rand1 = RandomUtil.randomBytes(8); byte[] rand2 = RandomUtil.randomBytes(12); // Save authentication data byte[] seed = new byte[rand1.length + rand2.length]; System.arraycopy(rand1, 0, seed, 0, rand1.length); System.arraycopy(rand2, 0, seed, rand1.length, rand2.length); this.seed = seed; // Send handshake packet HandshakePacket hs = new HandshakePacket(); hs.packetId = 0; hs.protocolVersion = Versions.PROTOCOL_VERSION; hs.serverVersion = Versions.SERVER_VERSION; hs.threadId = id; hs.seed = rand1; hs.serverCapabilities = getServerCapabilities(); hs.serverCharsetIndex = (byte) (charsetIndex & 0xff); hs.serverStatus = 2; hs.restOfScrambleBuff = rand2; hs.write(this); }
Grab a bag and see the results
Explains that the handshake package was returned to navicat correctly, and then navicat logged in
So, the next step is to work with the read function, Breakpoint
stop in com.baidu.hsb.net.NIOReactor$R.read
@Override public void read() throws IOException { //Read function ByteBuffer buffer = this.readBuffer; int got = channel.read(buffer); lastReadTime = TimeUtil.currentTimeMillis(); if (got < 0) { throw new EOFException(); } netInBytes += got; processor.addNetInBytes(got); // Processing data int offset = readBufferOffset, length = 0, position = buffer.position(); for (;;) { length = getPacketLength(buffer, offset); if (length == -1) {// Data not reaching calculable packet length if (!buffer.hasRemaining()) { checkReadBuffer(buffer, offset, position); } break; } if (position >= offset + length) { // Extract data from a packet for processing buffer.position(offset); byte[] data = new byte[length]; buffer.get(data, 0, length); handle(data); // Set Offset offset += length; if (position == offset) {// The data just finished processing if (readBufferOffset != 0) { readBufferOffset = 0; } buffer.clear(); break; } else {// Remaining data unprocessed readBufferOffset = offset; buffer.position(position); continue; } } else {// Data not reaching a packet if (!buffer.hasRemaining()) { checkReadBuffer(buffer, offset, position); } break; } } }
You don't need to look at all the code, just focus!!!How much time to read line by line!!!
Instead of explaining the sticky stuff here, go directly to handle(data); function, start breaking points
stop in com.baidu.hsb.net.FrontendConnection.handle
The code is as follows:
@Override public void handle(final byte[] data) { // Asynchronous processing of front-end data processor.getHandler().execute(new Runnable() { @Override public void run() { try { handler.handle(data); } catch (Throwable t) { error(ErrorCode.ERR_HANDLE_DATA, t); } } }); }
Breakpoint hit: "thread=Processor1-R", com.baidu.hsb.net.FrontendConnection.handle(), line=360 bci=0 360 processor.getHandler().execute(new Runnable() { Processor1-R[1] step > Step completed: "thread=Processor1-R", com.baidu.hsb.net.NIOProcessor.getHandler(), line=76 bci=0 76 return handler; Processor1-R[1] print handler handler = "com.baidu.hsb.util.NameableExecutor@f604253[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]" Processor1-R[1] print handler.getClass() handler.getClass() = "class com.baidu.hsb.util.NameableExecutor"
You can see that a Runnable is executed by a thread pool, so the focus is this handler.handle(data);
Processor1-R[1] print handler handler = "com.baidu.hsb.net.handler.FrontendAuthenticator@1c0beb9f"
Breakpoint,
stop in com.baidu.hsb.net.handler.FrontendAuthenticator.handle
debug knows the validation passed
@Override public void handle(byte[] data) { // check quit packet if (data.length == QuitPacket.QUIT.length && data[4] == MySQLPacket.COM_QUIT) { source.close(); return; } AuthPacket auth = new AuthPacket(); auth.read(data); // check user if (!checkUser(auth.user, source.getHost())) { failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'"); return; } // check password if (!checkPassword(auth.password, auth.user)) { failure(ErrorCode.ER_ACCESS_DENIED_ERROR, "Access denied for user '" + auth.user + "'"); return; } // check schema switch (checkSchema(auth.database, auth.user)) { case ErrorCode.ER_BAD_DB_ERROR: failure(ErrorCode.ER_BAD_DB_ERROR, "Unknown database '" + auth.database + "'"); break; case ErrorCode.ER_DBACCESS_DENIED_ERROR: String s = "Access denied for user '" + auth.user + "' to database '" + auth.database + "'"; failure(ErrorCode.ER_DBACCESS_DENIED_ERROR, s); break; default: success(auth); } }
Let's see what information was sent back!Grab the bag directly!
?... 5.1.48-heisenberg.....qbYIgi1Y.O.!...............ROOgLwOSLuLE.:..........@!.......................root.....mn..'.t.(T..9.=.t................SET NAMES utf8
As you can see, navicat issued another command for set names utf8
Processor1-H0[1] stop in com.baidu.hsb.net.FrontendConnection.handle Set breakpoint com.baidu.hsb.net.FrontendConnection.handle Processor1-H0[1] cont > Breakpoint hit: "thread=Processor1-R", com.baidu.hsb.net.FrontendConnection.handle(), line=360 bci=0 360 processor.getHandler().execute(new Runnable() { Processor1-R[1] print handler handler = "com.baidu.hsb.net.handler.FrontendCommandHandler@3791625d"
As you can see, the processing handle now becomes FrontendCommandHandler
The breakpoints are as follows:
stop in com.baidu.hsb.net.handler.FrontendCommandHandler.handle
@Override public void handle(byte[] data) { switch (data[4]) { case MySQLPacket.COM_INIT_DB: commands.doInitDB(); source.initDB(data); break; case MySQLPacket.COM_QUERY: commands.doQuery(); source.query(data); break; case MySQLPacket.COM_PING: commands.doPing(); source.ping(); break; case MySQLPacket.COM_QUIT: commands.doQuit(); source.close(); break; case MySQLPacket.COM_PROCESS_KILL: commands.doKill(); source.kill(data); break; case MySQLPacket.COM_STMT_PREPARE: commands.doStmtPrepare(); source.stmtPrepare(data); break; case MySQLPacket.COM_STMT_EXECUTE: commands.doStmtExecute(); source.stmtExecute(data); break; case MySQLPacket.COM_STMT_CLOSE: commands.doStmtClose(); source.stmtClose(data); break; case MySQLPacket.COM_HEARTBEAT: commands.doHeartbeat(); source.heartbeat(data); break; default: commands.doOther(); source.writeErrMessage(ErrorCode.ER_UNKNOWN_COM_ERROR, "Unknown command"); } }
Drop down the details and remember that the command set names utf8 has not been processed yet!