当DUBBO遇上Arthas - 排查问题的实践
Apache Dubbo是Alibaba开源的高性能RPC框架,在国内有非常多的用户。
- Github: https://github.com/apache/incubator-dubbo
- 文档:http://dubbo.incubator.apache.org/zh-cn/
Arthas是Alibaba开源的应用诊断利器,9月份开源以来,Github Star数三个月超过6000。
- Github: https://github.com/alibaba/arthas
- 文档:https://alibaba.github.io/arthas/
- Arthas开源交流QQ群: 916328269
- Arthas开源交流钉钉群: 21965291
当Dubbo遇上Arthas,会碰撞出什么样的火花呢?下面来分享Arthas排查Dubbo问题的一些经验。
dubbo-arthas-demo
下面的排查分享基于这个dubbo-arthas-demo
,非常简单的一个应用,浏览器请求从Spring MVC到Dubbo Client,再发送到Dubbo Server。
Demo里有两个spring boot应用,可以先启动server-demo
,再启动client-demo
。
- https://github.com/hengyunabc/dubbo-arthas-demo
/user/{id} -> UserService -> UserServiceImpl Browser Dubbo Client Dubbo Server
Client端:
@RestController
public class UserController {@Reference(version = "1.0.0")private UserService userService;@GetMapping("/user/{id}")public User findUserById(@PathVariable Integer id) {return userService.findUser(id);}
Server端:
@Service(version = "1.0.0")
public class UserServiceImpl implements UserService {@Overridepublic User findUser(int id) {if (id < 1) {throw new IllegalArgumentException("user id < 1, id: " + id);}for (User user : users) {if (user.getId() == id) {return user;}}throw new RuntimeException("Can not find user, id: " + id);}
Arthas快速开始
- https://alibaba.github.io/arthas/install-detail.html
$ wget https://alibaba.github.io/arthas/arthas-boot.jar
$ java -jar arthas-boot.jar
启动后,会列出所有的java进程,选择1,然后回车,就会连接上ServerDemoApplication
$ java -jar arthas-boot.jar
* [1]: 43523 ServerDemoApplication[2]: 22342[3]: 44108 ClientDemoApplication
1
[INFO] arthas home: /Users/hengyunabc/.arthas/lib/3.0.5/arthas
[INFO] Try to attach process 43523
[INFO] Attach process 43523 success.
[INFO] arthas-client connect 127.0.0.1 3658,---. ,------. ,--------.,--. ,--. ,---. ,---./ O \ | .--. ''--. .--'| '--' | / O \ ' .-'
| .-. || '--'.' | | | .--. || .-. |`. `-.
| | | || |\ \ | | | | | || | | |.-' |
`--' `--'`--' '--' `--' `--' `--'`--' `--'`-----'wiki: https://alibaba.github.io/arthas
version: 3.0.5
pid: 43523
time: 2018-12-05 16:23:52$
Dubbo线上服务抛出异常,怎么获取调用参数?
- https://alibaba.github.io/arthas/watch.html
当线上服务抛出异常时,最着急的是什么参数导致了抛异常?
在demo里,访问 http://localhost:8080/user/0 ,UserServiceImpl
就会抛出一个异常,因为user id不合法。
在Arthas里执行 watch com.example.UserService * -e -x 2 '{params,throwExp}'
,然后再次访问,就可以看到watch命令把参数和异常都打印出来了。
$ watch com.example.UserService * -e -x 2 '{params,throwExp}'
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:4) cost in 230 ms.
ts=2018-12-05 16:26:44; [cost=3.905523ms] result=@ArrayList[@Object[][@Integer[0],],java.lang.IllegalArgumentException: user id < 1, id: 0at com.example.UserServiceImpl.findUser(UserServiceImpl.java:24)at com.alibaba.dubbo.common.bytecode.Wrapper1.invokeMethod(Wrapper1.java)at com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory$1.doInvoke(JavassistProxyFactory.java:45)at com.alibaba.dubbo.rpc.proxy.AbstractProxyInvoker.invoke(AbstractProxyInvoker.java:71)at com.alibaba.dubbo.config.invoker.DelegateProviderMetaDataInvoker.invoke(DelegateProviderMetaDataInvoker.java:48)at com.alibaba.dubbo.rpc.protocol.InvokerWrapper.invoke(InvokerWrapper.java:52)at com.alibaba.dubbo.rpc.filter.ExceptionFilter.invoke(ExceptionFilter.java:61)
怎样线上调试Dubbo服务代码?
- https://alibaba.github.io/arthas/redefine.html
在本地开发时,可能会用到热部署工具,直接改代码,不需要重启应用。但是在线上环境,有没有办法直接动态调试代码?比如增加日志。
在Arthas里,可以通过redefine
命令来达到线上不重启,动态更新代码的效果。
比如我们修改下UserServiceImpl
,用System.out
打印出具体的User
对象来:
public User findUser(int id) {if (id < 1) {throw new IllegalArgumentException("user id < 1, id: " + id);}for (User user : users) {if (user.getId() == id) {System.out.println(user);return user;}}throw new RuntimeException("Can not find user, id: " + id);}
本地编绎后,把server-demo/target/classes/com/example/UserServiceImpl.class
传到线上服务器,然后用redefine
命令来更新代码:
$ redefine -p /tmp/UserServiceImpl.class
redefine success, size: 1
这样子更新成功之后,访问 http://localhost:8080/user/1 ,在ServerDemoApplication
的控制台里就可以看到打印出了user信息。
怎样动态修改Dubbo的logger级别?
- https://alibaba.github.io/arthas/ognl.html
- https://alibaba.github.io/arthas/sc.html
- https://commons.apache.org/proper/commons-ognl/language-guide.html
在排查问题时,需要查看到更多的信息,如果可以把logger级别修改为DEBUG
,就非常有帮助。
ognl
是apache开源的一个轻量级表达式引擎。下面通过Arthas里的ognl
命令来动态修改logger级别。
首先获取Dubbo里TraceFilter
的一个logger对象,看下它的实现类,可以发现是log4j。
$ ognl '@com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter@logger.logger'
@Log4jLogger[FQCN=@String[com.alibaba.dubbo.common.logger.support.FailsafeLogger],logger=@Logger[org.apache.log4j.Logger@2f19bdcf],
]
再用sc
命令来查看具体从哪个jar包里加载的:
$ sc -d org.apache.log4j.Loggerclass-info org.apache.log4j.Loggercode-source /Users/hengyunabc/.m2/repository/org/slf4j/log4j-over-slf4j/1.7.25/log4j-over-slf4j-1.7.25.jarname org.apache.log4j.LoggerisInterface falseisAnnotation falseisEnum falseisAnonymousClass falseisArray falseisLocalClass falseisMemberClass falseisPrimitive falseisSynthetic falsesimple-name Loggermodifier publicannotationinterfacessuper-class +-org.apache.log4j.Category+-java.lang.Objectclass-loader +-sun.misc.Launcher$AppClassLoader@5c647e05+-sun.misc.Launcher$ExtClassLoader@59878d35classLoaderHash 5c647e05Affect(row-cnt:1) cost in 126 ms.
可以看到log4j是通过slf4j代理的。
那么通过org.slf4j.LoggerFactory
获取root
logger,再修改它的level:
$ ognl '@org.slf4j.LoggerFactory@getLogger("root").setLevel(@ch.qos.logback.classic.Level@DEBUG)'
null
$ ognl '@org.slf4j.LoggerFactory@getLogger("root").getLevel().toString()'
@String[DEBUG]
可以看到修改之后,root
logger的level变为DEBUG
。
怎样减少测试小姐姐重复发请求的麻烦?
- https://alibaba.github.io/arthas/tt.html
在平时开发时,可能需要测试小姐姐发请求过来联调,但是我们在debug时,可能不小心直接跳过去了。这样子就尴尬了,需要测试小姐姐再发请求过来。
Arthas里提供了tt
命令,可以减少这种麻烦,可以直接重放请求。
$ tt -t com.example.UserServiceImpl findUser
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 145 ms.INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
----------------------------------------------------------------------------------------------------------------1000 2018-12-05 17:47:52 1.56523 true false 0x3233483 UserServiceImpl findUser1001 2018-12-05 17:48:03 0.286176 false true 0x3233483 UserServiceImpl findUser1002 2018-12-05 17:48:11 90.324335 true false 0x3233483 UserServiceImpl findUser
上面的tt -t
命令捕获到了3个请求。然后通过tt --play
可以重放请求:
$ tt --play -i 1000RE-INDEX 1000GMT-REPLAY 2018-12-05 17:55:50OBJECT 0x3233483CLASS com.example.UserServiceImplMETHOD findUserPARAMETERS[0] @Integer[1]IS-RETURN trueIS-EXCEPTION falseRETURN-OBJ @User[id=@Integer[1],name=@String[Deanna Borer],]
Time fragment[1000] successfully replayed.
Affect(row-cnt:1) cost in 4 ms.
Dubbo运行时有哪些Filter? 耗时是多少?
- https://alibaba.github.io/arthas/trace.html
Dubbo运行时会加载很多的Filter,那么一个请求会经过哪些Filter处理,Filter里的耗时又是多少呢?
通过Arthas的trace
命令,可以很方便地知道Filter的信息,可以看到详细的调用栈和耗时。
$ trace com.alibaba.dubbo.rpc.Filter *
Press Ctrl+C to abort.
Affect(class-cnt:19 , method-cnt:59) cost in 1441 ms.
`---ts=2018-12-05 19:07:26;thread_name=DubboServerHandler-30.5.125.152:20880-thread-10;id=3e;is_daemon=true;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@5c647e05`---[8.435844ms] com.alibaba.dubbo.rpc.filter.EchoFilter:invoke()+---[0.124572ms] com.alibaba.dubbo.rpc.Invocation:getMethodName()+---[0.065123ms] java.lang.String:equals()`---[7.762928ms] com.alibaba.dubbo.rpc.Invoker:invoke()`---[7.494124ms] com.alibaba.dubbo.rpc.filter.ClassLoaderFilter:invoke()+---[min=0.00355ms,max=0.049922ms,total=0.057637ms,count=3] java.lang.Thread:currentThread()+---[0.0126ms] java.lang.Thread:getContextClassLoader()+---[0.02188ms] com.alibaba.dubbo.rpc.Invoker:getInterface()+---[0.004115ms] java.lang.Class:getClassLoader()+---[min=0.003906ms,max=0.014058ms,total=0.017964ms,count=2] java.lang.Thread:setContextClassLoader()`---[7.033486ms] com.alibaba.dubbo.rpc.Invoker:invoke()`---[6.869488ms] com.alibaba.dubbo.rpc.filter.GenericFilter:invoke()+---[0.01481ms] com.alibaba.dubbo.rpc.Invocation:getMethodName()
Dubbo动态代理是怎样实现的?
- https://alibaba.github.io/arthas/jad.html
- com.alibaba.dubbo.common.bytecode.Wrapper
通过Arthas的jad
命令,可以看到Dubbo通过javaassist动态生成的Wrappr类的代码:
$ jad com.alibaba.dubbo.common.bytecode.Wrapper1ClassLoader:
+-sun.misc.Launcher$AppClassLoader@5c647e05+-sun.misc.Launcher$ExtClassLoader@59878d35Location:
/Users/hengyunabc/.m2/repository/com/alibaba/dubbo/2.5.10/dubbo-2.5.10.jarpackage com.alibaba.dubbo.common.bytecode;public class Wrapper1
extends Wrapper
implements ClassGenerator.DC {public Object invokeMethod(Object object, String string, Class[] arrclass, Object[] arrobject) throws InvocationTargetException {UserServiceImpl userServiceImpl;try {userServiceImpl = (UserServiceImpl)object;}catch (Throwable throwable) {throw new IllegalArgumentException(throwable);}try {if ("findUser".equals(string) && arrclass.length == 1) {return userServiceImpl.findUser(((Number)arrobject[0]).intValue());}if ("listUsers".equals(string) && arrclass.length == 0) {return userServiceImpl.listUsers();}if ("findUserByName".equals(string) && arrclass.length == 1) {return userServiceImpl.findUserByName((String)arrobject[0]);}}
获取Spring context
除了上面介绍的一些排查技巧,下面分享一个获取Spring Context,然后“为所欲为”的例子。
在Dubbo里有一个扩展com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory
,把Spring Context保存到了里面。
因此,我们可以通过ognl
命令获取到。
$ ognl '#context=@com.alibaba.dubbo.config.spring.extension.SpringExtensionFactory@contexts.iterator.next, #context.getBean("userServiceImpl").findUser(1)'
@User[id=@Integer[1],name=@String[Deanna Borer],
]
SpringExtensionFactory@contexts.iterator.next
获取到SpringExtensionFactory
里保存的spring context对象#context.getBean("userServiceImpl").findUser(1)
获取到userServiceImpl
再执行一次调用
只要充分发挥想像力,组合Arthas里的各种命令,可以发挥出神奇的效果。
总结
本篇文章来自杭州Dubbo Meetup的分享《当DUBBO遇上Arthas - 排查问题的实践》,希望对大家线上排查Dubbo问题有帮助。
分享的PDF可以在 https://github.com/alibaba/arthas/issues/327 里下载。
公众号
欢迎关注公众号:横云断岭的专栏,专注分享Java,Spring Boot,Arthas,Dubbo。
当DUBBO遇上Arthas - 排查问题的实践相关推荐
- Java 线上问题排查神器 Arthas 快速上手与原理浅谈
[Arthas 官方社区正在举行征文活动,参加即有奖品拿哦~点击投稿] 作者 | 杨桢栋,笔名叫蛮三刀把刀,是一名一线互联网码农,留美访学一年,主要关注后端开发,数据安全,爬虫,物联网,边缘计算等方向 ...
- php线上问题排查,线上问题排查神器 Arthas
摘要: rController|kite.springcloud.jxm.service.MonitorDashboardServiceoverviewstack输出当前方法被调用的调用路径stack ...
- curl命令java_让 Bug 无处藏身,Java 线上问题排查思路、常用工具
本文总结了一些常见的线上应急现象和对应排查步骤和工具.分享的主要目的是想让对线上问题接触少的同学有个预先认知,免得在遇到实际问题时手忙脚乱. 只不过这里先提示一下.在线上应急过程中要记住,只有一个总体 ...
- 使用 Arthas 排查 SpringBoot 诡异耗时的 Bug
作者 | 空无 来源 | 阿里巴巴云原生公众号 背景 公司有个渠道系统,专门对接三方渠道使用,没有什么业务逻辑,主要是转换报文和参数校验之类的工作,起着一个承上启下的作用. 最近在优化接口的响应时间, ...
- 利用Arthas排查NoSuchMethodError
1.前言 有时spring boot应用会遇到java.lang.NoSuchMethodError的问题,下面以具体的demo来说明怎样利用arthas来排查. Demo: https://gith ...
- 深入Spring Boot:利用Arthas排查NoSuchMethodError
前言 有时spring boot应用会遇到java.lang.NoSuchMethodError的问题,下面以具体的demo来说明怎样利用arthas来排查. Demo: https://github ...
- [当人工智能遇上安全] 3.安全领域中的机器学习及机器学习恶意请求识别案例分享
您或许知道,作者后续分享网络安全的文章会越来越少.但如果您想学习人工智能和安全结合的应用,您就有福利了,作者将重新打造一个<当人工智能遇上安全>系列博客,详细介绍人工智能与安全相关的论文. ...
- 理想汽车×OceanBase:当造车新势力遇上数据库新势力
6 月 21 日晚 20:00,理想发布了 2022 年的旗舰车型 L9.三维空间交互.超强计算平台.五屏视听系统.AD Max 旗舰级智能驾驶......作为一款大型家庭智能旗舰全尺寸 SUV,不论 ...
- Java线上问题排查系列--后端接口响应慢的排查方法及解决方案
原文网址:Java线上问题排查系列--后端接口响应慢的排查方法及解决方案_IT利刃出鞘的博客-CSDN博客 简介 说明 本文介绍Java后端接口响应慢的排查的方法以及如何解决. 如何发现接口响应慢了? ...
最新文章
- IQueryable和IQueryProvider初尝
- vb 用代码添加控件
- 树莓派3 有线静态路由设置_clash在树莓派
- 微信小程序开发 使用高德地图(精准一些)
- SAP ABAP SICF事务码和SAP Hybris的链式过滤器filter chain
- GPU下train 模型出现nan
- 杨国勋:云计算颠覆现有市场和产业格局
- python文件写入_python读写不同编码txt文件
- go语言ATM小案例
- mysql 判断指定条件数据存不存在,不存在则插入
- java 观察者模式类图_设计模式——观察者模式
- 消消乐android 源代码,【消消乐】源代码
- installshield mysql_InstallShield 调用批处理部署MySql数据库 | 学步园
- ForkJoinPool线程池
- jQuery UI 拖拽功能
- 【沐风老师】3dMax Gyroid Minimal Surface 极小曲面建模详细教程
- 法国“毁容”女教师请求安乐死
- 将字符串中的中文转为拼音(java)
- 创建fate账户密码
- 加密算法之PKCS填充