vertx run -conf '{"telnetOptions":{"port":5000}}' maven:io.vertx:vertx-shell:5.0.1
Vert.x Shell
Vert.x Shell 是一个命令行界面,可通过各种协议从常规终端访问 Vert.x 运行时。
Vert.x Shell 提供了多种命令,用于与 Vert.x 服务进行实时交互。
Vert.x Shell 可以通过 Vert.x 支持的任何语言的自定义命令进行扩展
使用 Vert.x Shell
Vert.x Shell 是一个 Vert.x 服务,可以通过 ShellService
以编程方式启动,或作为服务部署。
Shell 服务
Shell 可以直接从命令行或作为 Vert.x 部署服务启动。
或
# create a key pair for the SSH server
keytool -genkey -keyalg RSA -keystore ssh.jks -keysize 2048 -validity 1095 -dname CN=localhost -keypass secret -storepass secret
# create the auth config
echo user.admin=password > auth.properties
# start the shell
vertx run -conf '{"sshOptions":{"port":4000,"keyPairOptions":{"path":"ssh.jks","password":"secret"},"authOptions":{"provider":"properties","config":{"file":"file:auth.properties"}}}}' maven:io.vertx:vertx-shell:5.0.1
或
# create a certificate for the HTTP server
keytool -genkey -keyalg RSA -keystore keystore.jks -keysize 2048 -validity 1095 -dname CN=localhost -keypass secret -storepass secret
# create the auth config
echo user.admin=password > auth.properties
vertx run -conf '{"httpOptions":{"port":8080,"ssl":true,"keyStoreOptions":{"path":"keystore.jks","password":"secret"},"authOptions":{"provider":""properties,"config":{"file":"file:auth.properties"}}}}' maven:io.vertx:vertx-shell:5.0.1
您也可以在自己的 verticle 中部署此服务
vertx
.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
new DeploymentOptions().setConfig(
new JsonObject().put("telnetOptions",
new JsonObject().
put("host", "localhost").
put("port", 4000))
)
);
或
vertx
.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
new DeploymentOptions().setConfig(new JsonObject().
put("sshOptions", new JsonObject().
put("host", "localhost").
put("port", 5000).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/ssh.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().
put("provider", "properties").
put("config", new JsonObject().
put("file", "file:/path/to/my/auth.properties"))))
)
);
或
vertx
.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
new DeploymentOptions().setConfig(new JsonObject().
put("httpOptions", new JsonObject().
put("host", "localhost").
put("port", 8080).
put("ssl", true).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/server-keystore.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().
put("provider", "properties").
put("config", new JsonObject().
put("file", "file:/path/to/my/auth.properties"))))
)
);
当 Vert.x Shell 已在您的 classpath 中时,您可以使用 service:io.vertx.ext.shell 而不是 maven:io.vertx:vertx-shell:5.0.1 |
编程服务
ShellService
负责启动 Vert.x Shell 实例。
通过 SSH 启动可用的 Shell 服务
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setSSHOptions(
new SSHTermOptions().
setHost("localhost").
setPort(5000).
setKeyPairOptions(new JksOptions().
setPath("server-keystore.jks").
setPassword("wibble")
).
setAuthOptions(
new JsonObject()
.put("provider", "properties")
.put("type", "PROPERTIES")
.put("config", new JsonObject().
put("properties_path", "file:/path/to/my/auth.properties"))
)
)
);
service.start();
通过 Telnet 启动可用的 Shell 服务
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setTelnetOptions(
new TelnetTermOptions().
setHost("localhost").
setPort(4000)
)
);
service.start();
TelnetTermOptions
扩展了 Vert.x Core 的 NetServerOptions
,因为 Telnet 服务器实现基于 NetServer
。
Telnet 不提供任何身份验证或加密。 |
通过 HTTP 启动可用的 Shell 服务
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setHttpOptions(
new HttpTermOptions().
setHost("localhost").
setPort(8080)
)
);
service.start();
认证
SSH 和 HTTP 连接器都提供基于 vertx-auth 构建的身份验证,支持以下提供商:
-
properties: 提供
.properties
后端 -
ldap: 提供
LDAP
后端 -
mongo : MongoDB 后端
这些选项可以直接使用相应的类创建
-
LdapAuthenticationOptions
用于 LDAP -
MongoAuthenticationOptions
用于 Mongo
至于 Json 中的外部服务配置,authOptions
使用 provider
属性进行区分
{
...
"authOptions": {
"provider":"properties",
"config": {
"file":"file:auth.properties"
}
}
...
}
Telnet 终端配置
Telnet 终端通过 setTelnetOptions
进行配置,TelnetTermOptions
扩展了 NetServerOptions
,因此它们具有完全相同的配置。
SSH 终端配置
SSH 终端通过 setSSHOptions
进行配置
目前只支持用户名/密码认证,可以使用属性文件或 LDAP 进行配置,更多信息请参阅 Vert.x Auth
-
setAuthOptions
: 配置用户认证
服务器密钥配置重用了 Vert.x Core 提供的密钥对存储配置方案
-
setKeyPairOptions
: 设置.jks
密钥对存储 -
setPfxKeyPairOptions
: 设置.pfx
密钥对存储 -
setPemKeyPairOptions
: 设置.pem
密钥对存储
vertx
.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
new DeploymentOptions().setConfig(new JsonObject().
put("sshOptions", new JsonObject().
put("host", "localhost").
put("port", 5000).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/ssh.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().
put("provider", "mongo").
put("config", new JsonObject().
put("connection_string", "mongodb://:27018"))))
)
);
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setSSHOptions(
new SSHTermOptions().
setHost("localhost").
setPort(5000).
setKeyPairOptions(new JksOptions().
setPath("server-keystore.jks").
setPassword("wibble")
).
setAuthOptions(new JsonObject()
.put("provider", "mongo")
.put("config", new JsonObject()
.put("connection_string", "mongodb://:27018"))
)
)
);
service.start();
HTTP 终端配置
HTTP 终端通过 setHttpOptions
进行配置,http 选项扩展了 HttpServerOptions
,因此它们公开了完全相同的配置。
此外,还有用于配置 HTTP 终端的额外选项
-
setAuthOptions
: 配置用户认证 -
setSockJSHandlerOptions
: 配置 SockJS -
setSockJSPath
: 路由器中的 SockJS 路径
vertx
.deployVerticle("maven:{maven-groupId}:{maven-artifactId}:{maven-version}",
new DeploymentOptions().setConfig(new JsonObject().
put("httpOptions", new JsonObject().
put("host", "localhost").
put("port", 8080).
put("ssl", true).
put("keyPairOptions", new JsonObject().
put("path", "src/test/resources/server-keystore.jks").
put("password", "wibble")).
put("authOptions", new JsonObject().
put("provider", "mongo").
put("config", new JsonObject().
put("connection_string", "mongodb://:27018"))))
)
);
ShellService service = ShellService.create(vertx,
new ShellServiceOptions().setHttpOptions(
new HttpTermOptions().
setHost("localhost").
setPort(8080).
setAuthOptions(new JsonObject()
.put("provider", "mongo")
.put("config", new JsonObject()
.put("connection_string", "mongodb://:27018"))
)
)
);
service.start();
按键映射配置
Shell 使用默认的按键映射配置,可以通过各种终端配置对象的 inputrc
属性来覆盖
inputrc
必须指向通过类加载器或文件系统可用的文件。
inputrc
仅用于函数绑定,可用的函数有:
-
backward-char
-
forward-char
-
next-history
-
previous-history
-
backward-delete-char
-
backward-delete-char
-
backward-word
-
end-of-line
-
beginning-of-line
-
delete-char
-
delete-char
-
complete
-
accept-line
-
accept-line
-
kill-line
-
backward-word
-
forward-word
-
backward-kill-word
可以添加额外函数,但这是通过实现 Vert.x Shell 所基于的 Term.d 项目的函数来完成的,例如,reverse function 可以实现,然后声明在 META-INF/services/io.termd.core.readline.Function 中,以便由 shell 加载。 |
基本命令
要查找可用命令,可以使用内置的 help 命令
-
Verticle 命令
-
verticle-ls: 列出所有已部署的 verticle
-
verticle-undeploy: 卸载一个 verticle
-
verticle-deploy: 部署一个 verticle,部署选项为 JSON 字符串
-
verticle-factories: 列出所有已知的 verticle 工厂
-
-
文件系统命令
-
ls
-
cd
-
pwd
-
-
总线命令
-
bus-tail: 显示事件总线地址上的所有传入消息
-
bus-send: 在事件总线上发送消息
-
-
网络命令
-
net-ls: 列出所有可用的网络服务器,包括 HTTP 服务器
-
-
共享数据命令
-
local-map-put
-
local-map-get
-
local-map-rm
-
-
各种命令
-
echo
-
sleep
-
help
-
exit
-
logout
-
-
作业控制
-
fg
-
bg
-
jobs
-
此命令列表将在 Vert.x Shell 的后续版本中不断演进。其他 Vert.x 项目可能会提供命令来扩展 Vert.x Shell,例如 Dropwizard Metrics。 |
扩展 Vert.x Shell
Vert.x Shell 可以使用支持代码生成的任何语言中的自定义命令进行扩展。
命令通过 CommandBuilder.command
方法创建:当命令执行时,shell 会调用命令进程处理器,此处理器可以通过 processHandler
方法设置
CommandBuilder builder = CommandBuilder.command("my-command");
builder.processHandler(process -> {
// Write a message to the console
process.write("Hello World");
// End the process
process.end();
});
// Register the command
CommandRegistry registry = CommandRegistry.getShared(vertx);
registry.registerCommand(builder.build(vertx));
命令创建后,需要注册到 CommandRegistry
。命令注册表保存了 Vert.x 实例的所有命令。
命令会一直注册,直到通过 unregisterCommand
取消注册。当命令从 Verticle 注册时,该 Verticle 卸载时此命令也会被取消注册。
当命令在注册表中注册时,命令回调会在 Context 中被调用。如果您在命令中维护状态,请记住这一点。 |
CommandProcess
对象可用于与 shell 交互。
命令参数
args
返回命令参数
command.processHandler(process -> {
for (String arg : process.args()) {
// Print each argument on the console
process.write("Argument " + arg);
}
process.end();
});
此外,也可以使用 Vert.x CLI
创建命令:这使得编写命令行参数解析变得更容易
-
选项和参数解析
-
参数验证
-
生成命令用法
CLI cli = CLI.create("my-command").
addArgument(new Argument().setArgName("my-arg")).
addOption(new Option().setShortName("m").setLongName("my-option"));
CommandBuilder command = CommandBuilder.command(cli);
command.processHandler(process -> {
CommandLine commandLine = process.commandLine();
String argValue = commandLine.getArgumentValue(0);
String optValue = commandLine.getOptionValue("my-option");
process
.write("The argument is " + argValue + " and the option is " + optValue);
process.end();
});
当名为 help 的选项添加到 CLI 对象时,shell 会在选项激活时负责生成命令用法
CLI cli = CLI.create("my-command").
addArgument(new Argument().setArgName("my-arg")).
addOption(new Option()
.setArgName("help")
.setShortName("h")
.setLongName("help"));
CommandBuilder command = CommandBuilder.command(cli);
command.processHandler(process -> {
// ...
});
当命令执行时,会提供 process
用于与 shell 交互。CommandProcess
扩展了 Tty
,Tty
用于与终端交互。
终端用法
终端 I/O
stdinHandler
处理器用于在终端接收到数据(例如用户使用键盘)时接收通知
tty.stdinHandler(data -> System.out.println("Received " + data));
命令可以使用 write
写入标准输出。
tty.write("Hello World");
终端大小
tty.write("Current terminal size: (" + tty.width() + ", " + tty.height() + ")");
调整大小事件
当终端大小改变时,会调用 resizehandler
,新的终端大小可以通过 width
和 height
获取。
tty.resizehandler(v ->
System.out.println("terminal resized : " + tty.width() + " " + tty.height()));
Shell 会话
shell 是一个连接服务,自然地与客户端维护一个会话,此会话可以在命令中用于范围数据。命令可以使用 session
获取会话
command.processHandler(process -> {
Session session = process.session();
if (session.get("my_key") == null) {
session.put("my key", "my value");
}
process.end();
});
进程终止
调用 end
结束当前进程。它可以在命令处理器的调用中直接调用,也可以在任何时候之后调用
command.processHandler(process -> {
Vertx vertx = process.vertx();
// Set a timer
vertx.setTimer(1000, id -> {
// End the command when the timer is fired
process.end();
});
});
进程事件
命令可以订阅一些进程事件。
中断事件
当进程被中断时,会调用 interruptHandler
,此事件在用户在命令执行期间按下 Ctrl+C 时触发。此处理器可用于中断阻塞 CLI 的命令并优雅地结束命令进程
command.processHandler(process -> {
Vertx vertx = process.vertx();
// Every second print a message on the console
long periodicId = vertx.setPeriodic(1000, id -> process.write("tick\n"));
// When user press Ctrl+C: cancel the timer and end the process
process.interruptHandler(v -> {
vertx.cancelTimer(periodicId);
process.end();
});
});
如果没有注册中断处理器,按下 Ctrl+C 对当前进程将没有影响,事件将被延迟,并可能由 shell 处理,例如在控制台上打印新行。
暂停/恢复事件
当进程正在运行且用户按下 Ctrl+Z 时,会调用 suspendHandler
,命令将*暂停*
-
当命令为此事件注册了处理器时,它可以接收暂停事件
-
命令将不再从标准输入接收数据
-
shell 提示用户输入
-
命令可以接收中断事件或结束事件
当进程恢复时,会调用 resumeHandler
,通常是当用户输入 fg 时
-
当命令为此事件注册了处理器时,它可以接收恢复事件
-
当命令注册了 stdin 处理器时,它将再次从标准输入接收数据
command.processHandler(process -> {
// Command is suspended
process.suspendHandler(v -> System.out.println("Suspended"));
// Command is resumed
process.resumeHandler(v -> System.out.println("Resumed"));
});
结束事件
当进程正在运行或暂停且命令终止时,例如 shell 会话关闭时,会调用 endHandler
(io.vertx.core.Handler)},命令*终止*。
command.processHandler(process -> {
// Command terminates
process.endHandler(v -> System.out.println("Terminated"));
});
即使命令调用 end
,也会调用结束处理器。
此处理器对于在命令终止时清理资源很有用,例如关闭客户端或计时器。
命令补全
当命令希望提供上下文命令行界面补全时,可以提供一个补全处理器。
与进程处理器一样,completion handler
是非阻塞的,因为实现可能会使用 Vert.x 服务,例如文件系统。
lineTokens
返回从行首到光标位置的 tokens
列表。如果光标在行首,则列表可能为空。
rawLine
返回当前从行首到光标位置的已完成内容,采用原始格式,即未执行任何字符转义。
补全以调用 complete
结束。
Shell 服务器
Shell 服务是一个方便的门面,用于以编程方式或作为 Vert.x 服务启动预配置的 shell。当需要更大的灵活性时,可以使用 ShellServer
代替该服务。
例如,shell http 终端可以配置为使用现有路由器,而不是启动自己的 http 服务器。
使用 shell 服务器需要显式配置,但提供了完全的灵活性,shell 服务器的设置分几个步骤完成
ShellServer server = ShellServer.create(vertx); (1)
Router shellRouter = Router.router(vertx); (2)
router.route("/shell/*").subRouter(shellRouter);
TermServer httpTermServer = TermServer.createHttpTermServer(vertx, router);
TermServer sshTermServer = TermServer.createSSHTermServer(vertx); (3)
server.registerTermServer(httpTermServer); (4)
server.registerTermServer(sshTermServer);
server.registerCommandResolver(CommandResolver.baseCommands(vertx)); (5)
server.listen(); (6)
1 | 创建 shell 服务器 |
2 | 创建挂载在现有路由器上的 HTTP 终端服务器 |
3 | 创建 SSH 终端服务器 |
4 | 注册终端服务器 |
5 | 注册所有基本命令 |
6 | 最后启动 shell 服务器 |
此外,shell 服务器还可以用于创建进程内 shell 会话:它提供了一个可编程的交互式 shell。
进程内 shell 会话可以通过 createShell
创建
Shell shell = shellServer.createShell();
主要用例是运行或测试命令
Shell shell = shellServer.createShell();
// Create a job fo the command
Job job = shell.createJob("my-command 1234");
// Create a pseudo terminal
Pty pty = Pty.create();
pty.stdoutHandler(data -> System.out.println("Command wrote " + data));
// Run the command
job.setTty(pty.slave());
job.statusUpdateHandler(status ->
System.out.println("Command terminated with status " + status));
Pty
伪终端是命令运行时与命令交互的主要接口
-
使用标准输入/输出写入或读取字符串
-
调整终端大小
close
关闭 shell,它将终止当前 shell 会话中的所有作业。
终端服务器
Vert.x Shell 还为需要编写纯终端应用程序的用户提供了裸终端服务器。
在启动终端服务器之前,必须在其上设置一个 Term
处理器。当用户连接时,此处理器将处理每个终端。
Auth*Options
可以在 SSHTermOptions
和 HttpTermOptions
上设置。或者,可以在启动终端服务器之前,直接在终端服务器上 set
AuthenticationProvider
。
SSH 终端
TermServer server = TermServer
.createSSHTermServer(vertx,
new SSHTermOptions().setPort(5000).setHost("localhost"));
server.termHandler(term -> term.stdinHandler(term::write));
server.listen();
Telnet 终端
TermServer server = TermServer
.createTelnetTermServer(vertx,
new TelnetTermOptions().setPort(5000).setHost("localhost"));
server.termHandler(term -> term.stdinHandler(term::write));
server.listen();
HTTP 终端
TermServer.createHttpTermServer
方法创建一个 HTTP 终端服务器,它基于 Vert.x Web 并使用 SockJS 协议构建。
TermServer server = TermServer
.createHttpTermServer(vertx,
new HttpTermOptions().setPort(5000).setHost("localhost"));
server.termHandler(term -> term.stdinHandler(term::write));
server.listen();
HTTP 终端可以启动自己的 HTTP 服务器,也可以重用现有的 Vert.x Web Router
。
shell 可以在 /shell.html
找到。
TermServer server = TermServer
.createHttpTermServer(vertx,
router,
new HttpTermOptions().setPort(5000).setHost("localhost"));
server.termHandler(term -> term.stdinHandler(term::write));
server.listen();
当 HTTP shell 集成到现有 HTTP 服务器中时,后一种选项很方便。
HTTP 终端服务器默认配置为提供
-
shell.html
页面 -
term.js
客户端库 -
vertxshell.js
客户端库
vertxshell.js
集成了 term.js
,是 HTTP 终端的客户端部分。
它将 term.js
与 SockJS 集成,需要 HTTP 终端服务器端点的 URL
window.addEventListener('load', function () {
var url = 'https:///shell';
new VertxTerm(url, {
cols: 80,
rows: 24
});
});
也可以直接使用 websockets,如果是这样,远程终端 URL 应以 /websocket
为后缀
window.addEventListener('load', function () {
var url = 'ws:///shell/websocket';
new VertxTerm(url, {
cols: 80,
rows: 24
});
});
出于定制目的,这些资源可以复制和定制,它们在 Vert.x Shell jar 的 io.vertx.ext.shell
包下可用。
命令发现
当需要向 Vert.x 添加新命令而无需显式注册时,可以使用命令发现。
例如,*Dropwizard* 指标服务会即时向 shell 服务添加特定的指标命令。
这可以通过 CommandResolverFactory
的 java.util.ServiceLoader
实现。
public class CustomCommands implements CommandResolverFactory {
public void resolver(Vertx vertx, Handler<AsyncResult<CommandResolver>> resolverHandler) {
resolverHandler.handler(() -> Arrays.asList(myCommand1, myCommand2));
}
}
resolver
方法是异步的,因为解析器在命令解析之前可能需要等待某些条件。
使用服务加载器机制的 shell 服务发现
META-INF/services/io.vertx.ext.shell.spi.CommandResolverFactory
my.CustomCommands
这仅对 ShellService
有效。ShellServer
不使用此机制。
命令包
命令包是一个提供新的 Vert.x Shell 命令的 jar。
这样的 jar 只需存在于 classpath 中,即可被 Vertx. Shell 发现。
public class CommandPackExample implements CommandResolverFactory {
@Override
public void resolver(Vertx vertx, Handler<AsyncResult<CommandResolver>> resolveHandler) {
List<Command> commands = new ArrayList<>();
// Add commands
commands.add(Command.create(vertx, JavaCommandExample.class));
// Add another command
commands.add(CommandBuilder.command("another-command").processHandler(process -> {
// Handle process
}).build(vertx));
// Resolve with the commands
resolveHandler.handle(Future.succeededFuture(() -> commands));
}
}
命令包使用命令发现机制,因此它需要描述符
META-INF/services/io.vertx.ext.shell.spi.CommandResolverFactory
描述符examples.pack.CommandPackExample