7点微信公众号:请点这里。7点测试网QQ总群:277957570。

7点测试网

 找回密码
 注册7点

QQ登录

只需一步,快速开始

查看: 939|回复: 0

btrace使用实例

[复制链接]
发表于 2013-12-3 14:53:04 | 显示全部楼层 |阅读模式
【 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点

本版积分规则

QQ|Archiver|手机版|小黑屋|7点测试网 ( 京ICP备09084002号

GMT+8, 2018-8-19 16:10 , Processed in 0.113623 second(s), 22 queries .

Powered by Discuz! X3.1

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表