序
本文次要钻研一下arthas的HttpTermServer
TermServer
com/taobao/arthas/core/shell/term/TermServer.java
public abstract class TermServer { /** * Create a term server for the Telnet protocol. * * @param configure * @return the term server */ public static TermServer createTelnetTermServer(Configure configure, ShellServerOptions options) { int port = configure.getTelnetPort() != null ? configure.getTelnetPort() : ArthasConstants.TELNET_PORT; return new TelnetTermServer(configure.getIp(), port, options.getConnectionTimeout()); } /** * Create a term server for the HTTP protocol, using an existing router. * * @return the term server */ public static TermServer createHttpTermServer() { // TODO return null; } /** * Set the term handler that will receive incoming client connections. When a remote terminal connects * the {@code handler} will be called with the {@link Term} which can be used to interact with the remote * terminal. * * @param handler the term handler * @return this object */ public abstract TermServer termHandler(Handler<Term> handler); /** * Bind the term server, the {@link #termHandler(Handler)} must be set before. * * @return this object */ public TermServer listen() { return listen(null); } /** * Bind the term server, the {@link #termHandler(Handler)} must be set before. * * @param listenHandler the listen handler * @return this object */ public abstract TermServer listen(Handler<Future<TermServer>> listenHandler); /** * The actual port the server is listening on. This is useful if you bound the server specifying 0 as port number * signifying an ephemeral port * * @return the actual port the server is listening on. */ public abstract int actualPort(); /** * Close the server. This will close any currently open connections. The close may not complete until after this * method has returned. */ public abstract void close(); /** * Like {@link #close} but supplying a handler that will be notified when close is complete. * * @param completionHandler the handler to be notified when the term server is closed */ public abstract void close(Handler<Future<Void>> completionHandler);}
TermServer是一个抽象类,它定义了termHandler、listen、actualPort、close形象办法
HttpTermServer
com/taobao/arthas/core/shell/term/impl/HttpTermServer.java
public class HttpTermServer extends TermServer { private static final Logger logger = LoggerFactory.getLogger(HttpTermServer.class); private Handler<Term> termHandler; private NettyWebsocketTtyBootstrap bootstrap; private String hostIp; private int port; private long connectionTimeout; private EventExecutorGroup workerGroup; private HttpSessionManager httpSessionManager; public HttpTermServer(String hostIp, int port, long connectionTimeout, EventExecutorGroup workerGroup, HttpSessionManager httpSessionManager) { this.hostIp = hostIp; this.port = port; this.connectionTimeout = connectionTimeout; this.workerGroup = workerGroup; this.httpSessionManager = httpSessionManager; } @Override public TermServer termHandler(Handler<Term> handler) { this.termHandler = handler; return this; } @Override public TermServer listen(Handler<Future<TermServer>> listenHandler) { // TODO: charset and inputrc from options bootstrap = new NettyWebsocketTtyBootstrap(workerGroup, httpSessionManager).setHost(hostIp).setPort(port); try { bootstrap.start(new Consumer<TtyConnection>() { @Override public void accept(final TtyConnection conn) { termHandler.handle(new TermImpl(Helper.loadKeymap(), conn)); } }).get(connectionTimeout, TimeUnit.MILLISECONDS); listenHandler.handle(Future.<TermServer>succeededFuture()); } catch (Throwable t) { logger.error("Error listening to port " + port, t); listenHandler.handle(Future.<TermServer>failedFuture(t)); } return this; } @Override public int actualPort() { return bootstrap.getPort(); } @Override public void close() { close(null); } @Override public void close(Handler<Future<Void>> completionHandler) { if (bootstrap != null) { bootstrap.stop(); if (completionHandler != null) { completionHandler.handle(Future.<Void>succeededFuture()); } } else { if (completionHandler != null) { completionHandler.handle(Future.<Void>failedFuture("telnet term server not started")); } } }}
HttpTermServer继承了TermServer,其listen办法创立NettyWebsocketTtyBootstrap,而后执行其start办法,其accept办法执行的是termHandler.handle(new TermImpl(Helper.loadKeymap(), conn)),最初执行listenHandler.handle(Future.<TermServer>succeededFuture());其close办法执行的是bootstrap.stop()
Handler
com/taobao/arthas/core/shell/handlers/Handler.java
public interface Handler<E> { /** * Something has happened, so handle it. * * @param event the event to handle */ void handle(E event);}
Handler定义了handle办法
TermServerTermHandler
com/taobao/arthas/core/shell/handlers/server/TermServerTermHandler.java
public class TermServerTermHandler implements Handler<Term> { private ShellServerImpl shellServer; public TermServerTermHandler(ShellServerImpl shellServer) { this.shellServer = shellServer; } @Override public void handle(Term term) { shellServer.handleTerm(term); }}
TermServerTermHandler实现了Handler接口,其handle办法执行的是shellServer.handleTerm(term)
ShellServerImpl
com/taobao/arthas/core/shell/impl/ShellServerImpl.java
public void handleTerm(Term term) { synchronized (this) { // That might happen with multiple ser if (closed) { term.close(); return; } } ShellImpl session = createShell(term); tryUpdateWelcomeMessage(); session.setWelcome(welcomeMessage); session.closedFuture.setHandler(new SessionClosedHandler(this, session)); session.init(); sessions.put(session.id, session); // Put after init so the close handler on the connection is set session.readline(); // Now readline }
ShellServerImpl的handleTerm办法创立ShellImpl,而后执行init,最初执行readline
readline
com/taobao/arthas/core/shell/impl/ShellImpl.java
public void readline() { term.readline(prompt, new ShellLineHandler(this), new CommandManagerCompletionHandler(commandManager)); }
ShellImpl的readline委托给了Term,传入ShellLineHandler
ShellLineHandler
com/taobao/arthas/core/shell/handlers/shell/ShellLineHandler.java
public void handle(String line) { if (line == null) { // EOF handleExit(); return; } List<CliToken> tokens = CliTokens.tokenize(line); CliToken first = TokenUtils.findFirstTextToken(tokens); if (first == null) { // For now do like this shell.readline(); return; } String name = first.value(); if (name.equals("exit") || name.equals("logout") || name.equals("q") || name.equals("quit")) { handleExit(); return; } else if (name.equals("jobs")) { handleJobs(); return; } else if (name.equals("fg")) { handleForeground(tokens); return; } else if (name.equals("bg")) { handleBackground(tokens); return; } else if (name.equals("kill")) { handleKill(tokens); return; } Job job = createJob(tokens); if (job != null) { job.run(); } }
ShellLineHandler的handle办法才是真正解决命令的中央,它反对了exit、logout、q、quit、jobs、fg、bg、kill命令
小结
HttpTermServer继承了TermServer,其listen办法创立NettyWebsocketTtyBootstrap,而后执行其start办法,其accept办法执行的是termHandler.handle(new TermImpl(Helper.loadKeymap(), conn)),最初执行listenHandler.handle(Future.<TermServer>succeededFuture());其close办法执行的是bootstrap.stop()。