在日常工作中我们经常会遇到一些线上异常的情况,而且有些问题只有在线上才会出现,由于环境和数据不一样在本地和测试环境根本没办法复现,而且线上也没有输出日志,那么遇到这种情况我们往往要怎么去解决呢?
常规做法
如果实在遇到上面的情况,在本地和测试都无法复现,那最常规的做法就是拉个线上分支的版本,增加一些调试日志,然后再重新发布版本进行调试。运气好加一次日志就可以找到问题,运气不好的话可能还要发布好几次才能定位到问题。
高级做法
下载安装 arthas
Arthas 是阿里开源的一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
上面的 Arthas 这款工具的官方介绍,从中我们可以看到这个工具可以查看方法的出入参,异常以及监测方法的耗时,我们排查问题的时候最重要的就是想知道一些方法的入参和返回,有了入参和返回的数据我们就可以模拟出具体的场景从而解决线上的问题。
注意这里说的方法不单单是外层的接口方法,任何 Service 层或者 RPC 层的方法都是可以的。之所以提出这一点是因为很多时候线上的接口都会有请求和结果的日志记录,但是并没有内部某个具体方法的入参和返回,而通过 Arthas 我们可以监控任何方法的入参和返回值。
我们可以在https://github.com/alibaba/arthas/releases 这个地址上下载具体的版本,下载下来就可以用,没什么难度。接下来阿粉通过一个案例来带大家使用一下 Arthas。
测试
首先我们启动一个 Spring Boot 应用,并且对外一个 http 的接口,具体的 Controller 和 Service 代码实现如下。
package com.example.demo.controller;
import com.example.demo.service.HelloService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private HelloService helloService;
@GetMapping(value = "/hello")
public String hello(@RequestParam("name") String name) {
return helloService.sayHello(name);
}
}
package com.example.demo.service;
import org.springframework.stereotype.Service;
@Service
public class HelloService {
public String sayHello(String name) {
String result = doSomething(name);
return "hello: " + result;
}
private String doSomething(String name) {
return name + " 欢迎关注 Java 极客技术";
}
}
上面的代码非常简单,对外暴露了一个 hello 接口,接收一个 name 参数,Service 中通过 sayHello 方法,调用了一个内部的 doSomething 方法。其中过后我们访问 http://127.0.0.1:8080/hello?name=ziyou 可以看到如下的内容。
到这里都没什么问题,但是我们想象一下,如果在某些 case doSomething 的方法比较复杂,我们想知道在执行 doSomething 方法的时候具体的入参和返回值是什么,这个时候我们要怎么办呢?
当然如果采用上面的常规做法,增加一些日志,当然是可以,不过我们这里就来演示一下如何使用 Arthas 来实现这个效果。前面我们已经下载了 Arthas 了,下载完成后是一个压缩包,解压过后,可以通过命令java -jar arthas-boot.jar 来启动 Arthas。效果如下
可以看到启动过后 Arthas 会找到目前系统当中在运行的 Java 进程,我这里有四个进程,然后输入我们要监控的进程编号,这里是 4,然后回车,接下来我们就进入了 Arthas 的界面。
接下来我们使用 Arthas 的 watch 命令来监控入参和返回值,完整的命令如下:watch com.example.demo.service.HelloService doSomething '{params,returnObj}
接下来我们再去访问一下刚刚的接口,可以看到在终端上面有对应的数据输出。
简单解释一下对应的命令和输出的信息,watch 是 Arthas 提供的命令,后面根据第一个参数是完整的类路径,第二个参数是我们要监控的方法名,第三个参数是一个表达是这里我们可以获取入参以及返回值或者异常等信息,对应的取值有 params 数组形式,可以通过 params[0] 来获取对应的属性,returnObj 表示返回值,并且这两个都是可以通过 .的形式,获取下面属性的值的。
watch 命令后面的第三个参数其实是一个 ognl 表达式,如下所示我们是可以做一些计算和逻辑处理的,这就带来了很多高级的用法。Arthas的一些特殊用法文档说明 https://github.com/alibaba/arthas/issues/71
总结
Arthas 除了 watch 之外还有很多其他的命令可以使用,比如 trace 可以统计每个方法执行的耗时,对于我们做一些性能优化有很大的帮助,还有比如支持热部署的功能等。完整的命令和使用方式大家可以在这个网址找到 https://arthas.aliyun.com/doc/,并且这里还提供了一套在线演示的功能,可以在在线教程中通过终端依次亲手执行每个命令来学习。官方提供了一个应用程序 math-game.jar 可以让我们在线调试,感兴趣的小伙伴可以去试一试。