Heisenberg Source Analysis 5: server Listening Port & Processing Logins

Keywords: Database Navicat Java Netty

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!

Posted by chen2424 on Wed, 19 Jun 2019 10:15:25 -0700