【
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
是一个强大的工具,但是,在线上检测的时候考虑时效性和安全性,必须有一个经过检验的脚本库才可以安全及时的定位系统问题.
分享到:
相关推荐
**BTrace监控远程服务器使用实例** BTrace 是一个强大的、安全的、动态的Java应用程序诊断工具,它允许开发者在运行时对Java应用进行细粒度的监控和性能分析。BTrace利用了Java的动态代理机制(Java Agent)和ASM...
5. **应用实例**:BTrace常用于诊断性能瓶颈、检测线程死锁、监控内存使用、分析SQL查询等。例如,可以创建一个脚本来记录所有数据库访问,以找出慢查询。 6. **与其他工具集成**:BTrace也可以与持续集成系统、...
BTrace使用这个API获取`Instrumentation`实例,进而利用ASM生成的字节码对目标类进行改造。例如,BTrace可以通过`retransformClasses`方法在运行时对已经加载的类进行重新转换,添加监控代码。 4. **Java Compiler ...
当使用`OnMethod`注解时,如果目标方法在父类中定义,BTrace可能无法正确地在子类的相应实例上调用该方法的探针。这个问题可能导致跟踪丢失或者错误的结果。 为了更深入地理解这个问题,我们需要查看BTrace的源码。...
在本文的实例中,当`CookieDecoder.requestDecode`方法的执行时间超过500ms时,Btrace会打印出执行时间和堆栈信息,这对于定位性能瓶颈非常有帮助。 2. Greys 是阿里巴巴开源的一款Java线上诊断工具,它的功能与...
- `samples`:示例目录,提供了使用Btrace的实例,有助于用户快速上手。 综上所述,Btrace是一个强大的工具,它允许开发者在不中断Java应用程序运行的情况下进行实时调试,通过其提供的丰富的API和脚本语言,可以...
1. `addTransformer(ClassFileTransformer transformer, boolean canRetransform)`:添加一个 `ClassFileTransformer` 实例,该实例会在类加载时被调用,用来修改类的字节码。`canRetransform` 参数表示是否允许对已...
Arthas使用的核心变量包括类加载器、类引用、方法引用、实例、参数列表、返回值、抛出的异常等,这些变量帮助用户更好地理解和诊断问题。 总结来说,Java在线问题诊断工具提供了强大的监控和分析能力,使得开发者在...
针对这些问题,文中提供了一些解决思路,例如针对物理内存耗光的问题,可以通过分析线程分配的堆栈情况找到问题所在,并修复无限制使用DirectByteBuffer的代码段,避免创建过多的DirectByteBuffer实例,从而缓解内存...
堆用于存储对象实例,而栈则保存方法调用的局部变量。 运行时常量池是方法区的一部分,在JDK 8之前,方法区又称为永久代,但在JDK 8后,被元空间(Metaspace)取代。运行时常量池主要存放编译期间生成的各种字面量...
- **堆**:用于存储所有类的实例和数组,由所有线程共享。 - **栈**:用于存储局部变量、操作数栈、动态链接等,每个线程拥有独立的栈。 - **垃圾回收**:自动回收不再使用的对象所占用的内存空间。 **1.1.2 JVM...