序
本文主要研究一下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()。