`
zhuanggl
  • 浏览: 13946 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
最近访客 更多访客>>
社区版块
存档分类
最新评论

btrace使用实例

    博客分类:
  • java
阅读更多

BTrace 说明】

http://kenai.com/projects/btrace 是一个实时监控工具,使用了 java agent jvm attach 技术,可以在不停机的情况下实时监控线上程序的运行情况,另外,对 btrace 脚本(实际上就是 java 程序)做了非常严格的安全限制,安全性很高,对应用程序基本没有影响。在性能方面, cobar 进行过测试,对方法进行调用耗时统计的时候,基本消费在微秒级别,可以说微不足道。

【背景】

在中文站 napoli 上线过程后,发现了一个奇怪的现象,尽管“已知”的 offer 发送端都已经迁移到 napoli 系统中,但是老的 mq 系统仍然有新的 offer 消息进来,因为连接 mq 的服务器非常多,定位消息来源成了一个非常大的问题。这种情况,想到了使用 BTrace 在某一台服务器进行线上监控进而期望发现这个幽灵。

【过程】

首先,我们需要知道两个基本信息:消息类型和来源 ip ,这样才可以定位 offer 消息的来源。

要知道来源 ip ,需要找到服务器端 socket 管理的类,只有在建立 socket 的地方,才可以抓到具体 ip ,经过分析 amq 代码,发现 tcp 连接基本是有下面这个类来服务所有消息的接收的:

 public class TcpTransport extends TransportThreadSupport implements Transport, Service, Runnable {
    private static final Log LOG = LogFactory.getLog(TcpTransport.class);
    private static final ThreadPoolExecutor SOCKET_CLOSE;
    protected final URI remoteLocation;
    protected final URI localLocation;
    protected final WireFormat wireFormat;

    protected int connectionTimeout = 30000;
    protected int soTimeout;
    protected int socketBufferSize = 64 * 1024;
    protected int ioBufferSize = 8 * 1024;
    protected boolean closeAsync=true;
    protected Socket socket;

这个类中包含一个 socket 对象的成员变量,所有我们只要监控 readCommand 方法,这个方法的返回值实际上就是一个 ActivemqObjectMessage 对象,这样就可以在一个方法上加拦截器就可以同时捕获到 ip 和消息对象,两全其美!!!

protected Object readCommand() throws IOException {
        return wireFormat.unmarshal(dataIn);
    }

因为原有 ESB 消息通道都是一个队列 ESBQueue ,所以无法通过队列名称来确定消息类型,必须通过 ESBTransferObject 对象来取得消息类型: destType offer 的区间是 1000-1008

public class ESBTransferObject implements Serializable {

    private static final long serialVersionUID = -5975115234845303878L;
    /**
     * 消息体,原则上对象序列化后的XML数据(String) 注意使用XML1.1规范。
     */
    private Object            content;
    /**
     * 用户自定义数据
     */
    private Object            userDefineData;
    /**
     * 目的消息类型
     */
    private int               destType         = -1;

但是,在服务器端并没有 ESBTransferObject 对象,无法反序列化( BTrace 也不支持反序列化操作),所以没有方法简单取得消息类型信息!!!

OK ,我不反序列化,直接拿二进制 byte[] ,类型信息应该是在固定位置的吧?但是发现这个对象 content 变长字符串定义在类型之前,类型位置不确定了,晕倒啊

不死心,输出二进制数据,柳暗花明啊,原来对象序列化的时候, primitive field 都是紧接着类型信息写入的,所以,类型信息是在固定位置的 ,类型信息始终是 255 256 两个字节(实际上是 4 个字节,但是目前我们只占有 2 个)

Ok ,编写代码,测试环境运行一下,晕倒,竟然有数组溢出!

使用 BTrace ,把这个数组打印下来(这个需要点技巧, btrace for 都不允许),竟然发现位置偏移到 205 206 位置 ,这个真的不知道什么原因,估计是客户端发送的时候压缩了,简单修改偏移量,测试运行, ok ,所有的消息类型和 ip 的对照表打印出来了。

 

 

package com.alibaba.btrace.script;

import static com.sun.btrace.BTraceUtils.*;

import com.sun.btrace.annotations.*;

@BTrace
public class AMQQueue2IP {

    @OnMethod(clazz = "org.apache.activemq.transport.tcp.TcpTransport",        //需要拦截的类名
      method = "readCommand",                                         //需要拦截的方法名
      location = @Location(Kind.RETURN))              //拦截位置,方法返回时
    public static void onTransportCommandExit(@Self Object transport, @Return Object command) { //捕获调用对象和返回值
        String commandName = str(command);
        boolean isObjectMessage = (indexOf(commandName, "org.apache.activemq.command.ActiveMQObjectMessage") >= 0);
        if (isObjectMessage) {
            Object msg = command;
            Object content = get(field(getSuperclass(getSuperclass(classOf(msg))), "content", false), msg);//捕获消息内容byte[]
            byte[] bs = (byte[]) get(field(classOf(content), "data", false), content);
            if (bs.length >= 206) {
                int off = getInt(field(classOf(content), "offset", false), content);
                int code = (0xff00&bs[205]<<8)+(0xff&bs[206]);                                             //转换205,206字节为消息类型
                //println(str(code));
                Object socket = get(field(classOf(transport), "socket"), transport);                  
                String address = str(socket);                                                              //截取ip地址
                int s = indexOf(address, "/");
                int e = indexOf(address, ",");
                int len = e - s;
                String ip = substr(address, s + 1, e);
                print(strcat(timestamp(),"---"));
                println(strcat(strcat("ip: ", ip), strcat(" queueName: ", str(code))));                  
            }
        }
    }
}

打印结果:

 

2/3/10 12:38 PM---ip: 172.22.2.34 queueName: 2001

2/3/10 12:38 PM---ip: 172.22.2.41 queueName: 5001

2/3/10 12:38 PM---ip: 172.22.2.22 queueName: 5001

2/3/10 12:38 PM---ip: 172.22.2.47 queueName: 2001

2/3/10 12:38 PM---ip: 172.22.2.31 queueName: 2001

2/3/10 12:38 PM---ip: 172.22.2.13 queueName: 5001

2/3/10 12:38 PM---ip: 172.22.2.6 queueName: 5001

2/3/10 12:38 PM---ip: 172.22.2.48 queueName: 2001

2/3/10 12:38 PM---ip: 172.22.2.39 queueName: 2001

 

【补充】

BTrace 是一个强大的工具,但是,在线上检测的时候考虑时效性和安全性,必须有一个经过检验的脚本库才可以安全及时的定位系统问题.

分享到:
评论
7 楼 Willam2004 2012-11-25  
不能这么说吧,本身这并不是btrace的入门文档。你可以先将btrace的下载下来,看里面的例子试下,入手应该比较简单吧。
6 楼 fucktianya 2012-11-19  
SB。这也叫写的详细,连怎么配置都没说。
对新手没帮忙。至于你的业务逻辑,没人关心。
5 楼 Willam2004 2011-01-03  
老庄写的很详细,刚好正在学习btrace。
4 楼 sesame 2010-10-13  
刚好也在研究BTrace的使用,结果找到的都是自己公司人的。
3 楼 argan 2010-02-22  
btrace是个好东西,维护好自己定制的一套脚本就很好用了
2 楼 RednaxelaFX 2010-02-11  
hmm……这帖为什么不在Java区,差点漏掉了。最近我也在看BTrace相关,看看能不能用在这边的线上监控上。总有人担心字节码操纵不安全,诶。
1 楼 sdh5724 2010-02-11  
老庄也开始玩诊断了啊.

诊断是门艺术, 如何才能更科学呢~~ 我一直很烦恼.

相关推荐

    BTrace监控远程服务器使用实例

    **BTrace监控远程服务器使用实例** BTrace 是一个强大的、安全的、动态的Java应用程序诊断工具,它允许开发者在运行时对Java应用进行细粒度的监控和性能分析。BTrace利用了Java的动态代理机制(Java Agent)和ASM...

    btrace1.3.9

    5. **应用实例**:BTrace常用于诊断性能瓶颈、检测线程死锁、监控内存使用、分析SQL查询等。例如,可以创建一个脚本来记录所有数据库访问,以找出慢查询。 6. **与其他工具集成**:BTrace也可以与持续集成系统、...

    BTrace实现原理

    BTrace使用这个API获取`Instrumentation`实例,进而利用ASM生成的字节码对目标类进行改造。例如,BTrace可以通过`retransformClasses`方法在运行时对已经加载的类进行重新转换,添加监控代码。 4. **Java Compiler ...

    BTrace二三事之二:OnMethod子类匹配BUG(怀疑)

    当使用`OnMethod`注解时,如果目标方法在父类中定义,BTrace可能无法正确地在子类的相应实例上调用该方法的探针。这个问题可能导致跟踪丢失或者错误的结果。 为了更深入地理解这个问题,我们需要查看BTrace的源码。...

    java在线问题排查利器之Btrace&Greys1

    在本文的实例中,当`CookieDecoder.requestDecode`方法的执行时间超过500ms时,Btrace会打印出执行时间和堆栈信息,这对于定位性能瓶颈非常有帮助。 2. Greys 是阿里巴巴开源的一款Java线上诊断工具,它的功能与...

    Btrace非侵入式调试Java程序神奇linux版

    - `samples`:示例目录,提供了使用Btrace的实例,有助于用户快速上手。 综上所述,Btrace是一个强大的工具,它允许开发者在不中断Java应用程序运行的情况下进行实时调试,通过其提供的丰富的API和脚本语言,可以...

    javaagent使用指南-rickiyang1

    1. `addTransformer(ClassFileTransformer transformer, boolean canRetransform)`:添加一个 `ClassFileTransformer` 实例,该实例会在类加载时被调用,用来修改类的字节码。`canRetransform` 参数表示是否允许对已...

    Java在线问题诊断工具.pdf

    Arthas使用的核心变量包括类加载器、类引用、方法引用、实例、参数列表、返回值、抛出的异常等,这些变量帮助用户更好地理解和诊断问题。 总结来说,Java在线问题诊断工具提供了强大的监控和分析能力,使得开发者在...

    Java内存管理问题案例分享_技术大学.pdf

    针对这些问题,文中提供了一些解决思路,例如针对物理内存耗光的问题,可以通过分析线程分配的堆栈情况找到问题所在,并修复无限制使用DirectByteBuffer的代码段,避免创建过多的DirectByteBuffer实例,从而缓解内存...

    底层篇.pdf

    堆用于存储对象实例,而栈则保存方法调用的局部变量。 运行时常量池是方法区的一部分,在JDK 8之前,方法区又称为永久代,但在JDK 8后,被元空间(Metaspace)取代。运行时常量池主要存放编译期间生成的各种字面量...

    Java工程师应用技术汇总

    - **堆**:用于存储所有类的实例和数组,由所有线程共享。 - **栈**:用于存储局部变量、操作数栈、动态链接等,每个线程拥有独立的栈。 - **垃圾回收**:自动回收不再使用的对象所占用的内存空间。 **1.1.2 JVM...

Global site tag (gtag.js) - Google Analytics