# 加载外部命令
Arthas 支持在启动时加载外部 command jar,用于把团队内部常用的诊断动作封装成新的 Arthas 命令。外部命令加载后可以像内置命令一样通过 help 查看和执行。
提示
外部命令只在 Arthas 服务端启动时加载。Arthas 已经 attach 到目标 JVM 后,再把 jar 放到目录里不会自动生效,需要重新启动 Arthas 服务端。
# 加载方式
# 通过命令行指定
arthas-boot.jar 支持 --command-locations 参数,参数值可以是 jar 文件路径,也可以是目录路径,多个路径用英文逗号分隔:
java -jar arthas-boot.jar --command-locations '/opt/arthas/ext-command.jar,/opt/arthas/ext-commands' <pid>
如果使用完整包里的 as.sh,也可以传入相同参数:
./as.sh --command-locations '/opt/arthas/ext-command.jar,/opt/arthas/ext-commands' <pid>
# 通过 arthas.properties 指定
在 arthas.properties 中配置:
arthas.commandLocations=/opt/arthas/ext-command.jar,/opt/arthas/ext-commands
如果通过 arthas-spring-boot-starter 启动,Spring Boot 配置文件中推荐使用 arthas.command-locations:
arthas.command-locations=/opt/arthas/ext-command.jar,/opt/arthas/ext-commands
# 默认 commands 目录
如果 ${arthas.home}/commands 目录存在,Arthas 启动时也会自动加载该目录下的 *.jar:
mkdir -p ${arthas.home}/commands
cp arthas-demo-external-command.jar ${arthas.home}/commands/
显式配置的 arthas.commandLocations 会先加载,随后再加载默认的 ${arthas.home}/commands 目录。
# 目录扫描规则
- 单个路径可以指向 jar 文件,也可以指向目录。
- 目录只扫描当前目录下的
*.jar,不会递归扫描子目录。 - 同一个 jar 路径会按规范化后的绝对路径去重。
- 如果外部命令依赖第三方 jar,可以把依赖打包进命令 jar,或把依赖 jar 一起放到被扫描的目录中。
# 编写外部命令
外部命令 jar 通过 Java SPI 暴露 CommandResolver。一个最小命令通常包含三个部分:
- 实现一个命令类,继承
AnnotatedCommand。 - 实现
CommandResolver,返回命令列表。 - 在
META-INF/services/com.taobao.arthas.core.shell.command.CommandResolver中写入CommandResolver实现类名。
示例命令:
package demo.command;
import com.taobao.arthas.core.shell.command.AnnotatedCommand;
import com.taobao.arthas.core.shell.command.CommandProcess;
import com.taobao.middleware.cli.annotations.Argument;
import com.taobao.middleware.cli.annotations.Description;
import com.taobao.middleware.cli.annotations.Name;
import com.taobao.middleware.cli.annotations.Summary;
@Name("demo-external")
@Summary("Demo external command loaded from arthas.home/commands")
@Description("Examples:\n"
+ " demo-external\n"
+ " demo-external Codex\n")
public class DemoExternalCommand extends AnnotatedCommand {
private String message;
@Argument(index = 0, argName = "message", required = false)
@Description("message printed by the demo external command")
public void setMessage(String message) {
this.message = message;
}
@Override
public void process(CommandProcess process) {
String value = message;
if (value == null || value.trim().isEmpty()) {
value = "hello";
}
process.write("demo external command loaded: " + value + "\n");
process.end();
}
}
示例 CommandResolver:
package demo.command;
import java.util.Collections;
import java.util.List;
import com.taobao.arthas.core.shell.command.Command;
import com.taobao.arthas.core.shell.command.CommandResolver;
public class DemoExternalCommandResolver implements CommandResolver {
@Override
public List<Command> commands() {
return Collections.singletonList(Command.create(DemoExternalCommand.class));
}
}
SPI 文件路径:
src/main/resources/META-INF/services/com.taobao.arthas.core.shell.command.CommandResolver
SPI 文件内容:
demo.command.DemoExternalCommandResolver
# Maven 依赖
外部命令需要依赖 Arthas 的命令接口和 CLI 注解。建议将这些依赖声明为 provided,避免把 Arthas 自身类重复打包进外部命令 jar:
<dependencies>
<dependency>
<groupId>com.taobao.arthas</groupId>
<artifactId>arthas-core</artifactId>
<version>${arthas.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.alibaba.middleware</groupId>
<artifactId>cli</artifactId>
<scope>provided</scope>
</dependency>
</dependencies>
Arthas 仓库中提供了完整示例:arthas-demo-external-command在新窗口打开。
# 构建和验证
以示例模块为例,构建命令 jar:
./mvnw -pl arthas-demo-external-command -DskipTests package
将生成的 jar 放到 ${arthas.home}/commands/ 后启动 Arthas:
mkdir -p ${arthas.home}/commands
cp arthas-demo-external-command/target/arthas-demo-external-command-*.jar ${arthas.home}/commands/
java -jar arthas-boot.jar <pid>
进入 Arthas 后执行:
help demo-external
demo-external Codex
预期可以看到类似输出:
demo external command loaded: Codex
# 加载行为和冲突处理
Arthas 启动时会把外部 command jar 加入 Arthas ClassLoader,然后通过 ServiceLoader<CommandResolver> 发现命令。命令注册时遵循下面规则:
- 外部命令不能覆盖 Arthas 内置命令;如果和内置命令重名,外部命令会被跳过并记录日志。
- 多个外部命令重名时,只保留第一个命令,后续重名命令会被跳过并记录日志。
CommandResolver加载失败不会中断其他 resolver 的加载,错误会写入 Arthas 日志。- 如果没有发现有效的外部命令,Arthas 会继续按正常流程启动。