Arthas Command处理流程

系列

开篇

  • 这篇文章主要是为了分析Arthas的命令的执行过程,整体过程包括任务的创建和任务的执行。
  • arthas的命令都是实现统一的接口,对外通过process方法进行调用。
public abstract class AnnotatedCommand {
    // 不同的命令又不同的process实现
    public abstract void process(CommandProcess process);
}

├── AbstractTraceAdviceListener.java
├── DashboardCommand.java
├── DashboardInterruptHandler.java
├── EnhancerCommand.java
├── GroovyAdviceListener.java
├── GroovyScriptCommand.java
├── HeapDumpCommand.java
├── JvmCommand.java
├── MBeanCommand.java
├── MonitorAdviceListener.java
├── MonitorCommand.java
├── MonitorData.java
├── PathTraceAdviceListener.java
├── PerfCounterCommand.java
├── ProfilerCommand.java
├── StackAdviceListener.java
├── StackCommand.java
├── ThreadCommand.java
├── ThreadSampler.java
├── TimeFragment.java
├── TimeTunnelAdviceListener.java
├── TimeTunnelCommand.java
├── TimeTunnelTable.java
├── TraceAdviceListener.java
├── TraceCommand.java
├── TraceEntity.java
├── WatchAdviceListener.java
└── WatchCommand.java


任务创建和执行

public class ShellLineHandler implements Handler<String> {

    private ShellImpl shell;
    private Term term;

    @Override
    public void handle(String line) {

        List<CliToken> tokens = CliTokens.tokenize(line);

        // 解析参数生成tokens,根据tokens来创建Job
        Job job = createJob(tokens);
        if (job != null) {
            // 执行任务
            job.run();
        }
    }

    // 根据请求参数创建任务
    private Job createJob(List<CliToken> tokens) {
        Job job;
        try {
            job = shell.createJob(tokens);
        } catch (Exception e) {
            // 省略无关代码
        }
        return job;
    }
}
  • createJob根据传入参数创建Job对象
  • 通过job.run()来执行Job任务
  • sc *命令,那么根据sc关键字创建SearchClassCommand的任务并执行


任务创建流程

public class ShellImpl implements Shell {

    private JobControllerImpl jobController;

    public synchronized Job createJob(List<CliToken> args) {
        Job job = jobController.createJob(commandManager, args, session, new ShellJobHandler(this), term, null);
        return job;
    }
}

public class GlobalJobControllerImpl extends JobControllerImpl {

    public Job createJob(InternalCommandManager commandManager, List<CliToken> tokens, Session session, JobListener jobHandler, Term term, ResultDistributor resultDistributor) {

        final Job job = super.createJob(commandManager, tokens, session, jobHandler, term, resultDistributor);

        JobTimeoutTask jobTimeoutTask = new JobTimeoutTask(job);
        long jobTimeoutInSecond = getJobTimeoutInSecond();
        Date timeoutDate = new Date(System.currentTimeMillis() + (jobTimeoutInSecond * 1000));
        ArthasBootstrap.getInstance().getScheduledExecutorService().schedule(jobTimeoutTask, jobTimeoutInSecond, TimeUnit.SECONDS);
        jobTimeoutTaskMap.put(job.id(), jobTimeoutTask);
        job.setTimeoutDate(timeoutDate);

        return job;
    }
}

public class JobControllerImpl implements JobController {

    public Job createJob(InternalCommandManager commandManager, List<CliToken> tokens, Session session, JobListener jobHandler, Term term, ResultDistributor resultDistributor) {
        checkPermission(session, tokens.get(0));
        int jobId = idGenerator.incrementAndGet();
        StringBuilder line = new StringBuilder();
        for (CliToken arg : tokens) {
            line.append(arg.raw());
        }
        boolean runInBackground = runInBackground(tokens);
        // 根据tokens创建Process对象
        Process process = createProcess(session, tokens, commandManager, jobId, term, resultDistributor);
        process.setJobId(jobId);
        // 创建JobImpl包含Process对象
        JobImpl job = new JobImpl(jobId, this, process, line.toString(), runInBackground, session, jobHandler);
        jobs.put(jobId, job);
        return job;
    }
}
  • createProcess创建tokens对应的Process对象
  • JobImpl将创建的Process对象进行封装,Job的执行会调用Process的方法


public class JobControllerImpl implements JobController {

    private Process createProcess(Session session, List<CliToken> line, InternalCommandManager commandManager, int jobId, Term term, ResultDistributor resultDistributor) {
        try {
            ListIterator<CliToken> tokens = line.listIterator();
            while (tokens.hasNext()) {
                CliToken token = tokens.next();
                if (token.isText()) {
                    // check before create process
                    checkPermission(session, token);
                    // 1、获取命令对应的处理函数对象Command
                    Command command = commandManager.getCommand(token.value());
                    if (command != null) {
                        // 2、创建Process对象
                        return createCommandProcess(command, tokens, jobId, term, resultDistributor);
                    } else {
                        throw new IllegalArgumentException(token.value() + ": command not found");
                    }
                }
            }
            throw new IllegalArgumentException();
        } catch (Exception e) {
        }
    }


    private Process createCommandProcess(Command command, ListIterator<CliToken> tokens, int jobId, Term term, ResultDistributor resultDistributor) throws IOException {
        // 省略相关代码

        ProcessImpl process = new ProcessImpl(command, remaining, command.processHandler(), ProcessOutput, resultDistributor);
        process.setTty(term);
        return process;
    }
}

public class InternalCommandManager {

    private final List<CommandResolver> resolvers;

    public InternalCommandManager(List<CommandResolver> resolvers) {
        this.resolvers = resolvers;
    }

    public Command getCommand(String commandName) {
        Command command = null;
        for (CommandResolver resolver : resolvers) {
            // 内建命令在ShellLineHandler里提前处理了,所以这里不需要再查找内建命令
            if (resolver instanceof BuiltinCommandPack) {
                command = getCommand(resolver, commandName);
                if (command != null) {
                    break;
                }
            }
        }
        return command;
    }

    private static Command getCommand(CommandResolver commandResolver, String name) {
        List<Command> commands = commandResolver.commands();
        for (Command command : commands) {
            if (name.equals(command.name())) {
                return command;
            }
        }
    }
}
  • 根据命令的tokens去查询commands获取对应的command创建Process对象。
  • 查询返回的command是AnnotatedCommandImpl对象。


public class BuiltinCommandPack implements CommandResolver {

    private static List<Command> commands = new ArrayList<Command>();

    static {
        initCommands();
    }

    @Override
    public List<Command> commands() {
        return commands;
    }

    private static void initCommands() {
        commands.add(Command.create(HelpCommand.class));
        commands.add(Command.create(AuthCommand.class));
        commands.add(Command.create(KeymapCommand.class));
        commands.add(Command.create(SearchClassCommand.class));
        commands.add(Command.create(SearchMethodCommand.class));
        commands.add(Command.create(ClassLoaderCommand.class));
        commands.add(Command.create(JadCommand.class));
        commands.add(Command.create(GetStaticCommand.class));
        commands.add(Command.create(MonitorCommand.class));
        commands.add(Command.create(StackCommand.class));
        commands.add(Command.create(ThreadCommand.class));
        commands.add(Command.create(TraceCommand.class));
        commands.add(Command.create(WatchCommand.class));
        commands.add(Command.create(TimeTunnelCommand.class));
        commands.add(Command.create(JvmCommand.class));
        commands.add(Command.create(PerfCounterCommand.class));
        // commands.add(Command.create(GroovyScriptCommand.class));
        commands.add(Command.create(OgnlCommand.class));
        commands.add(Command.create(MemoryCompilerCommand.class));
        commands.add(Command.create(RedefineCommand.class));
        commands.add(Command.create(RetransformCommand.class));
        commands.add(Command.create(DashboardCommand.class));
        commands.add(Command.create(DumpClassCommand.class));
        commands.add(Command.create(HeapDumpCommand.class));
        commands.add(Command.create(JulyCommand.class));
        commands.add(Command.create(ThanksCommand.class));
        commands.add(Command.create(OptionsCommand.class));
        commands.add(Command.create(ClsCommand.class));
        commands.add(Command.create(ResetCommand.class));
        commands.add(Command.create(VersionCommand.class));
        commands.add(Command.create(SessionCommand.class));
        commands.add(Command.create(SystemPropertyCommand.class));
        commands.add(Command.create(SystemEnvCommand.class));
        commands.add(Command.create(VMOptionCommand.class));
        commands.add(Command.create(LoggerCommand.class));
        commands.add(Command.create(HistoryCommand.class));
        commands.add(Command.create(CatCommand.class));
        commands.add(Command.create(Base64Command.class));
        commands.add(Command.create(EchoCommand.class));
        commands.add(Command.create(PwdCommand.class));
        commands.add(Command.create(MBeanCommand.class));
        commands.add(Command.create(GrepCommand.class));
        commands.add(Command.create(TeeCommand.class));
        commands.add(Command.create(ProfilerCommand.class));
        commands.add(Command.create(ShutdownCommand.class));
        commands.add(Command.create(StopCommand.class));
    }
}

public abstract class Command {

    public static Command create(final Class<? extends AnnotatedCommand> clazz) {
        return new AnnotatedCommandImpl(clazz);
    }
}


public class AnnotatedCommandImpl extends Command {

    private CLI cli;
    private Class<? extends AnnotatedCommand> clazz;
    private Handler<CommandProcess> processHandler = new ProcessHandler();

    public AnnotatedCommandImpl(Class<? extends AnnotatedCommand> clazz) {
        this.clazz = clazz;
        cli = CLIConfigurator.define(clazz, true);
        cli.addOption(new Option().setArgName("help").setFlag(true).setShortName("h").setLongName("help")
                .setDescription("this help").setHelp(true));
    }

    public Handler<CommandProcess> processHandler() {
        return processHandler;
    }

    private void process(CommandProcess process) {
        AnnotatedCommand instance;
        try {
            instance = clazz.newInstance();
        } catch (Exception e) {
            process.end();
            return;
        }
        CLIConfigurator.inject(process.commandLine(), instance);
        instance.process(process);
        UserStatUtil.arthasUsageSuccess(name(), process.args());
    }

    private class ProcessHandler implements Handler<CommandProcess> {
        @Override
        public void handle(CommandProcess process) {
            process(process);
        }
    }
}
  • BuiltinCommandPack包含所有arthas支持的命令
  • 所有arthas支持的命令都用AnnotatedCommandImpl进行包装。


任务执行流程

public class JobImpl implements Job {
    // 保存Process对象
    final Process process;

    public Job run(boolean foreground) {

        actualStatus = ExecStatus.RUNNING;
        if (statusUpdateHandler != null) {
            statusUpdateHandler.handle(ExecStatus.RUNNING);
        }
        // 执行Process的run方法
        process.setSession(this.session);
        process.run(foreground);

        if (this.status() == ExecStatus.RUNNING) {
            if (foreground) {
                jobHandler.onForeground(this);
            } else {
                jobHandler.onBackground(this);
            }
        }
        return this;
    }
}
  • Job的执行是执行Process对象,执行Process的run方法


public class ProcessImpl implements Process {

    public synchronized void run(boolean fg) {

        // Make a local copy
        final Tty tty = this.tty;
        // 将Process封装成CommandProcessImpl对象
        process = new CommandProcessImpl(this, tty);

        // 省略代码

        // 封装成CommandProcessTask对象
        Runnable task = new CommandProcessTask(process);
        // 通过线程池执行任务
        ArthasBootstrap.getInstance().execute(task);
    }

    private class CommandProcessImpl implements CommandProcess {

        private final Process process;
        private final Tty tty;
        private List<String> args2;
        private CommandLine commandLine;
        private AtomicInteger times = new AtomicInteger();
        private AdviceListener listener = null;
        private ClassFileTransformer transformer;

        public CommandProcessImpl(Process process, Tty tty) {
            this.process = process;
            this.tty = tty;
        }
    }
}
  • ProcessImpl封装成CommandProcessImpl对象
  • CommandProcessImpl封装成CommandProcessTask对象
  • CommandProcessTask的run调用ProcessHandler的handle方法执行ProcessImpl


public class ProcessImpl implements Process {

    private Command commandContext;
    // handler是ProcessHandler对象
    private Handler<CommandProcess> handler;

    private class CommandProcessTask implements Runnable {

        private CommandProcess process;

        public CommandProcessTask(CommandProcess process) {
            this.process = process;
        }

        @Override
        public void run() {
            try {
                handler.handle(process);
            } catch (Throwable t) {
                logger.error("Error during processing the command:", t);
                process.end(1, "Error during processing the command: " + t.getClass().getName() + ", message:" + t.getMessage()
                        + ", please check $HOME/logs/arthas/arthas.log for more details." );
            }
        }
    }
}
  • 执行ProcessHandler的handle方法


public class AnnotatedCommandImpl extends Command {

    private CLI cli;
    private Class<? extends AnnotatedCommand> clazz;
    private Handler<CommandProcess> processHandler = new ProcessHandler();

    private class ProcessHandler implements Handler<CommandProcess> {
        @Override
        public void handle(CommandProcess process) {
            process(process);
        }
    }

    private void process(CommandProcess process) {
        AnnotatedCommand instance;
        try {
            // 创建命令的实例对象
            instance = clazz.newInstance();
        } catch (Exception e) {
            process.end();
            return;
        }
        CLIConfigurator.inject(process.commandLine(), instance);
        // 执行命令的process方法
        instance.process(process);
        UserStatUtil.arthasUsageSuccess(name(), process.args());
    }
}
  • 实例化命令的对象并执行对应的process方法完成对应命令的执行
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容