Arthas 之根据输入查找命令执行类
本文介绍 Arthas 命令路由的整个过程,包括如何注册命令、如何读取客户端的输入、如何根据输入找到对应的命令执行类。最后以一个基础命令 help 为例,简要介绍命令执行类是如何执行,如何接收参数,以及如何向客户端返回数据的。
在 ShellServer 中注册命令
ShellServer 是与命令行交互的服务端的核心类,启动 Arthas 服务端的整个过程(com.taobao.arthas.core.server.ArthasBootstrap 类的 bind () 方法)都是围绕着 ShellServer 进行的。
ShellServer 是开源项目 vert.x 提供的,Arthas 仅在此之上进行了少量的二次开发。
在 ArthasBootstrap 类的 bind () 方法中,首先通过一个自建的类 BuiltinCommandPack
来读取所有的命令类:
而这个 BuiltinCommandPack
是怎样运转的呢?非常简单,仅仅是将一个个类添加到一个 list 里而已。
见 BuiltinCommandPack.java:
接着将所有的 CommandResolver 都调用 ShellServer 的 registerCommandResolver () 方法注册进 ShellServer,整个命令注册的过程就完成了:
读取客户端的输入
真正接收客户端输入的是 ShellServer 中注册的 TermServer,Arthas 默认注册了两个 TermServer:
- 提供 Telnet 客户端的 TelnetTermServer
- 提供 WebSocket 客户端的 HttpTermServer
见 ArthasBootstrap 类的 bind () 方法:
这两个 TermServer 的实现在读取客户端输入的部分是一样的:
客户端的输入到来时,通过 TermServer 来 handle,而这个 TermServer 是哪儿来的呢?是从 ShellServerImpl 中传来的 TermServerTermHandler。
而这个 TermServerTermHandler 是怎样 handle 的呢?调用 ShellServer 的 handleTerm () 方法。
在这里,一个 session 就是一个客户端的连接(这很容易理解),而 ShellImpl 的 readline () 方法,就是读取客户端输入的方法。见:ShellImpl
查找对应的命令执行类
ShellImpl 的 readline () 方法,最终将客户端的输入转换为 String,交给 ShellLineHandler 的 handle () 方法来处理。
在 ShellLineHandler 的 handle () 方法中,首先把 String 类型的输入转换为第三方组件 CLI 中的 CliToken 的 List:
在 List
- 输入的是 exit、logout 或 quit,将会直接关闭客户端。
- 输入的是 jobs,将会返回当前正在后台执行的异步任务。
- 输入的是 fg,将会将暂停的异步任务拉到前台执行。
- 输入的是 bg,将会将暂停的异步任务放到后台执行。
- 输入的是 kill,将会强制终止所有异步任务。
如果输入的不是这些关键词,则开始创建 Job。
创建 Job 又是一个非常绕的过程,从 ShellLineHandler.createJob () 走到 ShellImpl.createJob () 再走到 JobControllerImpl.createJob ()。
见:JobControllerImpl.java
主要是调用 createProcess () 方法创建 Process,判断是在前台执行还是在后台执行,然后用 Process 生成 Job。
核心的 createProcess () 方法:
然后到了将输入路由到命令执行类的地方了!
开始看 InternalCommandManager 的 getCommand () 方法:
到这里,根据客户端的输入寻找对应的命令执行类的整个流程终于结束了,接下来就是各个命令执行类自己发挥的时刻了。
help 命令的执行过程
下面我以基础命令 help 为例,简要介绍命令执行类是如何执行,如何接收参数,以及如何向客户端返回数据的。
help 命令对应的命令执行类是 HelpCommand,其中 process () 方法是命令执行类的核心方法:
可以看到,help 命令的 process () 方法非常简单,就是获取所有的命令,生成 String 类型的文本。
不过这里有一点特殊之处,help 指令可以输入参数。根据参数中传来的命令生成的命令帮助内容。
- 如果参数是空,返回 mainHelp () 方法生成的全局帮助内容
- 如果参数不为空,则返回 commandHelp () 方法
那么,help 指令是如何接收参数的呢?奥妙就在下面的这个方法里了:
使用 @Argument 注解的方法用来接收参数,在这里就是把输入的内容放到 cmd 这个变量里,然后 findCommand () 方法去判断 cmd 变量的内容是否有命令与之相匹配:
另外,输出到客户端的内容并不是简单的文本,而是通过淘宝的 text-ui 包进行了排版。
见 HelpView.java:
参考资料
- 参考