Arthas 之根据输入查找命令执行类

本文介绍 Arthas 命令路由的整个过程,包括如何注册命令、如何读取客户端的输入、如何根据输入找到对应的命令执行类。最后以一个基础命令 help 为例,简要介绍命令执行类是如何执行,如何接收参数,以及如何向客户端返回数据的。

在 ShellServer 中注册命令

ShellServer 是与命令行交互的服务端的核心类,启动 Arthas 服务端的整个过程(com.taobao.arthas.core.server.ArthasBootstrap 类的 bind () 方法)都是围绕着 ShellServer 进行的。

ShellServer 是开源项目 vert.x 提供的,Arthas 仅在此之上进行了少量的二次开发。

ArthasBootstrap 类的 bind () 方法中,首先通过一个自建的类 BuiltinCommandPack 来读取所有的命令类:

1597663881528

而这个 BuiltinCommandPack 是怎样运转的呢?非常简单,仅仅是将一个个类添加到一个 list 里而已。

BuiltinCommandPack.java

1597663968735

接着将所有的 CommandResolver 都调用 ShellServer 的 registerCommandResolver () 方法注册进 ShellServer,整个命令注册的过程就完成了:

1597664133197

读取客户端的输入

真正接收客户端输入的是 ShellServer 中注册的 TermServer,Arthas 默认注册了两个 TermServer:

  • 提供 Telnet 客户端的 TelnetTermServer
  • 提供 WebSocket 客户端的 HttpTermServer

ArthasBootstrap 类的 bind () 方法:

1597664402161

这两个 TermServer 的实现在读取客户端输入的部分是一样的:

1597664489423

客户端的输入到来时,通过 TermServer 来 handle,而这个 TermServer 是哪儿来的呢?是从 ShellServerImpl 中传来的 TermServerTermHandler。

而这个 TermServerTermHandler 是怎样 handle 的呢?调用 ShellServer 的 handleTerm () 方法。

1597665051394

在这里,一个 session 就是一个客户端的连接(这很容易理解),而 ShellImpl 的 readline () 方法,就是读取客户端输入的方法。见:ShellImpl

1597665260376

查找对应的命令执行类

ShellImpl 的 readline () 方法,最终将客户端的输入转换为 String,交给 ShellLineHandler 的 handle () 方法来处理。

ShellLineHandler 的 handle () 方法中,首先把 String 类型的输入转换为第三方组件 CLI 中的 CliToken 的 List:

1597665436179

在 List 中,first 是输入的命令,而剩余部分则是命令的参数。在拿到输入的命令后,先做一些判断,处理几个内建命令:

1597665536086

  • 输入的是 exit、logout 或 quit,将会直接关闭客户端。
  • 输入的是 jobs,将会返回当前正在后台执行的异步任务。
  • 输入的是 fg,将会将暂停的异步任务拉到前台执行。
  • 输入的是 bg,将会将暂停的异步任务放到后台执行。
  • 输入的是 kill,将会强制终止所有异步任务

如果输入的不是这些关键词,则开始创建 Job。

创建 Job 又是一个非常绕的过程,从 ShellLineHandler.createJob () 走到 ShellImpl.createJob () 再走到 JobControllerImpl.createJob ()。

见:JobControllerImpl.java

1597665729558

主要是调用 createProcess () 方法创建 Process,判断是在前台执行还是在后台执行,然后用 Process 生成 Job。

核心的 createProcess () 方法:

1597665851789

然后到了将输入路由到命令执行类的地方了!

开始看 InternalCommandManager 的 getCommand () 方法:

1597665933730

到这里,根据客户端的输入寻找对应的命令执行类的整个流程终于结束了,接下来就是各个命令执行类自己发挥的时刻了。

help 命令的执行过程

下面我以基础命令 help 为例,简要介绍命令执行类是如何执行,如何接收参数,以及如何向客户端返回数据的。

help 命令对应的命令执行类是 HelpCommand,其中 process () 方法是命令执行类的核心方法:

1597666134279

可以看到,help 命令的 process () 方法非常简单,就是获取所有的命令,生成 String 类型的文本。

不过这里有一点特殊之处,help 指令可以输入参数。根据参数中传来的命令生成的命令帮助内容。

  • 如果参数是空,返回 mainHelp () 方法生成的全局帮助内容
  • 如果参数不为空,则返回 commandHelp () 方法

那么,help 指令是如何接收参数的呢?奥妙就在下面的这个方法里了:

1597666365077

使用 @Argument 注解的方法用来接收参数,在这里就是把输入的内容放到 cmd 这个变量里,然后 findCommand () 方法去判断 cmd 变量的内容是否有命令与之相匹配:

1597666394962

另外,输出到客户端的内容并不是简单的文本,而是通过淘宝的 text-ui 包进行了排版。

HelpView.java

1597666484624

参考资料

  • 参考