﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-往事如风-随笔分类-tools</title><link>http://www.blogjava.net/zhuanggl/category/45590.html</link><description>记录工作中的点点滴滴
留住那些淡淡的回忆</description><language>zh-cn</language><lastBuildDate>Wed, 14 Jul 2010 18:20:44 GMT</lastBuildDate><pubDate>Wed, 14 Jul 2010 18:20:44 GMT</pubDate><ttl>60</ttl><item><title>btrace使用实例</title><link>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326120.html</link><dc:creator>井底青蛙,常望天空</dc:creator><author>井底青蛙,常望天空</author><pubDate>Wed, 14 Jul 2010 11:18:00 GMT</pubDate><guid>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326120.html</guid><wfw:comment>http://www.blogjava.net/zhuanggl/comments/326120.html</wfw:comment><comments>http://www.blogjava.net/zhuanggl/archive/2010/07/14/326120.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/zhuanggl/comments/commentRss/326120.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/zhuanggl/services/trackbacks/326120.html</trackback:ping><description><![CDATA[<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
【
<span lang="EN-US">
<span style="font-family: calibri">BTrace</span>
</span>
说明】
</span>
<span lang="EN-US">
<a href="http://kenai.com/projects/btrace">
<span style="font-family: calibri; font-size: small">http://kenai.com/projects/btrace</span>
</a>
</span>
<br />
<span style="font-size: small">
是一个实时监控工具，使用了
<span lang="EN-US">
<span style="font-family: calibri">java agent</span>
</span> 和
<span lang="EN-US">
<span style="font-family: calibri">jvm attach</span>
</span>
技术，可以在不停机的情况下实时监控线上程序的运行情况，另外，对
<span lang="EN-US">
<span style="font-family: calibri">btrace</span>
</span>
脚本（实际上就是
<span lang="EN-US">
<span style="font-family: calibri">java</span>
</span>
程序）做了非常严格的安全限制，安全性很高，对应用程序基本没有影响。在性能方面，
<span lang="EN-US">
<span style="font-family: calibri">cobar</span>
</span>
<span>进行过测试，对方法进行调用耗时统计的时候，基本消费在微秒级别，可以说微不足道。
<br />
</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span>
<span style="font-size: small">【背景】</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
在中文站
<span lang="EN-US">
<span style="font-family: calibri">napoli</span>
</span>
上线过程后，发现了一个奇怪的现象，尽管"已知"的
<span lang="EN-US">
<span style="font-family: calibri">offer</span>
</span>
发送端都已经迁移到
<br />
<span lang="EN-US">
<span style="font-family: calibri">napoli</span>
</span>
系统中，但是老的
<span lang="EN-US">
<span style="font-family: calibri">mq</span>
</span>
系统仍然有新的
<span lang="EN-US">
<span style="font-family: calibri">offer</span>
</span>
消息进来，因为连接
<span lang="EN-US">
<span style="font-family: calibri">mq</span>
</span>
的服务器非常多，定位消息来源成了一个非常大的问题。这种情况，想到了使用
<span lang="EN-US">
<span style="font-family: calibri">BTrace</span>
</span>
<span>在某一台服务器进行线上监控进而期望发现这个幽灵。
<br />
</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span>
<span style="font-size: small">【过程】</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
首先，我们需要知道两个基本信息：消息类型和来源
<span lang="EN-US">
<span style="font-family: calibri">ip</span>
</span>
，这样才可以定位
<span lang="EN-US">
<span style="font-family: calibri">offer</span>
</span>
消息的来源。
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
要知道来源
<span lang="EN-US">
<span style="font-family: calibri">ip</span>
</span>
，需要找到服务器端
管理的类，只有在建立
<span lang="EN-US">
<span style="font-family: calibri">socket</span>
</span>
的地方，才可以抓到具体
<span lang="EN-US">
<span style="font-family: calibri">ip</span>
</span>
，经过分析
<span lang="EN-US">
<span style="font-family: calibri">amq</span>
</span>
代码，发现
<span lang="EN-US">
<span style="font-family: calibri">tcp</span>
</span>
连接基本是由下面这个类来服务所有消息的接收的：
<br />
</span>
</p>
<table cellpadding="0" cellspacing="0" style="border-collapse: collapse; " border="0">
    <tbody>
        <tr>
            <td width="590" style="padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 442.8pt; padding-right: 5.4pt; padding-top: 0cm; " valign="top">
            <p style="margin: 0cm 0cm 0pt">
            <span lang="EN-US">
            public class TcpTransport extends TransportThreadSupport implements Transport, Service, Runnable {
            </span>
            </p>
            <p>private static final Log LOG = LogFactory.getLog(TcpTransport.class);</p>
            <p>private static final ThreadPoolExecutor SOCKET_CLOSE;</p>
            <p>protected final URI remoteLocation;</p>
            <p>protected final URI localLocation;</p>
            <p>protected final WireFormat wireFormat;</p>
            <br />
            <p style="margin: 0cm 0cm 0pt">
            <span lang="EN-US">
            protected int connectionTimeout = 30000;
            </span>
            </p>
            <p>protected int soTimeout;</p>
            <p>protected int socketBufferSize = 64 * 1024;</p>
            <p>protected int ioBufferSize = 8 * 1024;</p>
            <p>protected boolean closeAsync=true;</p>
            <p>
            <span style="color: #ff0000">protected Socket socket;</span>
            <br />
            </p>
            <br />
            </td>
        </tr>
    </tbody>
</table>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
这个类中包含一个
<span lang="EN-US">
<span style="font-family: calibri">socket</span>
</span>
对象的成员变量，所有我们只要监控
<span lang="EN-US">
<span style="font-family: calibri">readCommand</span>
</span>
方法，这个方法的返回值实际上就是一个
<span lang="EN-US">
<span style="font-family: calibri">ActivemqObjectMessage</span>
</span>
对象，这样就可以在一个方法上加拦截器就可以同时捕获到
<span lang="EN-US">
<span style="font-family: calibri">ip</span>
</span>
和消息对象，两全其美！！！
<br />
</span>
</p>
<table cellpadding="0" cellspacing="0" style="border-collapse: collapse; " border="0">
    <tbody>
        <tr>
            <td width="590" style="padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 442.8pt; padding-right: 5.4pt; padding-top: 0cm; " valign="top">
            <p style="margin: 0cm 0cm 0pt">
            <span lang="EN-US">protected Object readCommand() throws IOException {</span>
            </p>
            <p>return wireFormat.unmarshal(dataIn);</p>
            <p>}</p>
            </td>
        </tr>
    </tbody>
</table>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
因为原有
<span lang="EN-US">
<span style="font-family: calibri">ESB</span>
</span>
消息通道都是一个队列
<span lang="EN-US">
<span style="font-family: calibri">ESBQueue</span>
</span>
，所以无法通过队列名称来确定消息类型，必须通过
<br />
<span lang="EN-US">
<span style="font-family: calibri">ESBTransferObject</span>
</span>
对象来取得消息类型：
<span lang="EN-US">
<span style="font-family: calibri">destType</span>
</span>
，
<span lang="EN-US">
<span style="font-family: calibri">offer</span>
</span>
的区间是
<span lang="EN-US">
<span style="font-family: calibri">1000-1008</span>
<br />
</span>
<br />
</span>
</p>
<table cellpadding="0" cellspacing="0" style="border-collapse: collapse; " border="0">
    <tbody>
        <tr>
            <td width="590" style="padding-bottom: 0cm; background-color: transparent; padding-left: 5.4pt; width: 442.8pt; padding-right: 5.4pt; padding-top: 0cm; " valign="top">
            <p style="margin: 0cm 0cm 0pt">
            <span lang="EN-US">
            <span style="font-family: calibri; font-size: small">public class ESBTransferObject implements Serializable {</span>
            <br />
            </span>
            </p>
            <p style="margin: 0cm 0cm 0pt">
            <span lang="EN-US">
            <span style="font-family: calibri; font-size: small">    private static final long serialVersionUID = -5975115234845303878L;</span>
            </span>
            </p>
            <p>/**</p>
            <p>* 消息体，原则上对象序列化后的XML数据(String) 注意使用XML1.1规范。</p>
            <p>*/</p>
            <p>private Object            content;</p>
            <p>/**</p>
            <p>* 用户自定义数据</p>
            <p>*/</p>
            <p>private Object            userDefineData;</p>
            <p>/**</p>
            <p>* 目的消息类型</p>
            <p>*/</p>
            <p>
            <span style="color: #ff0000">private int               destType         = -1;</span>
            <br />
            </p>
            <br />
            </td>
        </tr>
    </tbody>
</table>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
但是，在服务器端并没有
<span lang="EN-US">
<span style="font-family: calibri">ESBTransferObject</span>
</span>
对象，无法反序列化（
<span lang="EN-US">
<span style="font-family: calibri">BTrace</span>
</span>
也不支持反序列化操作），所以没有方法简单取得消息类型信息！！！
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
<span lang="EN-US">
<span style="font-family: calibri">OK</span>
</span>
，我不反序列化，直接拿二进制
<span lang="EN-US">
<span style="font-family: calibri">byte[]</span>
</span>
，类型信息应该是在固定位置的吧？但是发现这个对象
<br />
<span lang="EN-US">
<span style="font-family: calibri">content</span>
</span>
变长字符串定义在类型之前，类型位置不确定了，晕倒啊
</span>
<span style="font-size: small">
不死心，输出二进制数据，柳暗花明啊，原来对象序列化的时候，
<strong>
<span style="color: red">
<span style="font-family: calibri">primitive</span>
</span>
</strong>
<strong>
的
<span style="color: red">
<span style="font-family: calibri">field</span>
</span>
</strong>
<strong>
都是紧接着类型信息写入的，所以，类型信息是在固定位置的
</strong>
，类型信息始终是
<span lang="EN-US">
<span style="font-family: calibri">255</span>
</span>
，
<span lang="EN-US">
<span style="font-family: calibri">256</span>
</span>
两个字节（实际上是
<span lang="EN-US">
<span style="font-family: calibri">4</span>
</span>
个字节，但是目前我们只占有
<span lang="EN-US">
<span style="font-family: calibri">2</span>
</span>
个）
</span>
<span style="font-size: small">
<span lang="EN-US">
<span style="font-family: calibri">Ok</span>
</span>
，编写代码，测试环境运行一下，晕倒，竟然有数组溢出！
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
使用
<span lang="EN-US">
<span style="font-family: calibri">BTrace</span>
</span>
，把这个数组打印下来（这个需要点技巧，
<span lang="EN-US">
<span style="font-family: calibri">btrace</span>
</span>
连
<span lang="EN-US">
<span style="font-family: calibri">for</span>
</span>
<span>都不允许），竟然发现<strong>
<span style="color: red">位置偏移到</span>
</strong>
</span>
<strong>
<span style="color: red">
<span style="font-family: calibri">205</span>
</span>
</strong>
<strong>
，
<span style="color: red">
<span style="font-family: calibri">206</span>
</span>
</strong>
<strong>
位置
</strong>
，这个真的不知道什么原因，估计是客户端发送的时候压缩了，简单修改偏移量，测试运行，
<span lang="EN-US">
<span style="font-family: calibri">ok</span>
</span>
，所有的消息类型和
<span lang="EN-US">
<span style="font-family: calibri">ip</span>
</span>
的对照表打印出来了。
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt"> </p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small"> </span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">package com.alibaba.btrace.script;</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">import static com.sun.btrace.BTraceUtils.*;</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">import com.sun.btrace.annotations.*;</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">@BTrace</span>
</span>
</p>
<p>public class AMQQueue2IP {</p>
<p>
<br />
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">    @OnMethod(clazz = "org.apache.activemq.transport.tcp.TcpTransport",        //需要拦截的类名</span>
</span>
</p>
<p>method = "readCommand",                                         //需要拦截的方法名</p>
<p>location = @Location(Kind.RETURN))              //拦截位置，方法返回时</p>
<p>public static void onTransportCommandExit(@Self Object transport, @Return Object command) { //捕获调用对象和返回值</p>
<p>String commandName = str(command);</p>
<p>boolean isObjectMessage = (indexOf(commandName, "org.apache.activemq.command.ActiveMQObjectMessage") &gt;= 0);</p>
<p>if (isObjectMessage) {</p>
<p>Object msg = command;</p>
<p>Object content = get(field(getSuperclass(getSuperclass(classOf(msg))), "content", false), msg);//捕获消息内容byte[]</p>
<p>byte[] bs = (byte[]) get(field(classOf(content), "data", false), content);</p>
<p>if (bs.length &gt;= 206) {</p>
<p>int off = getInt(field(classOf(content), "offset", false), content);</p>
<p>int code = (0xff00&amp;bs[205]&lt;&lt;8)+(0xff&amp;bs[206]);                                             //转换205,206字节为消息类型</p>
<p>//println(str(code));</p>
<p>Object socket = get(field(classOf(transport), "socket"), transport);</p>
<p>String address = str(socket);                                                              //截取ip地址</p>
<p>int s = indexOf(address, "/");</p>
<p>int e = indexOf(address, ",");</p>
<p>int len = e - s;</p>
<p>String ip = substr(address, s + 1, e);</p>
<p>print(strcat(timestamp(),"---"));</p>
<p>println(strcat(strcat("ip: ", ip), strcat(" queueName: ", str(code))));</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>}</p>
<p>
<br />
</p>
<p style="margin: 0cm 0cm 0pt">
<span>
<span style="font-size: small">打印结果：</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small"> </span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.34 queueName: 2001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.41 queueName: 5001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.22 queueName: 5001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.47 queueName: 2001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.31 queueName: 2001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.13 queueName: 5001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.6 queueName: 5001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.48 queueName: 2001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small">2/3/10 12:38 PM---ip: 172.22.2.39 queueName: 2001</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span lang="EN-US">
<span style="font-family: calibri; font-size: small"> </span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span>
<span style="font-size: small">【补充】</span>
<br />
</span>
</p>
<p style="margin: 0cm 0cm 0pt">
<span style="font-size: small">
<span lang="EN-US">
<span style="font-family: calibri">BTrace</span>
</span>
是一个强大的工具，但是，在线上检测的时候考虑时效性和安全性，必须有一个经过检验的脚本库才可以安全及时的定位系统问题.
</span>
</p>
<img src ="http://www.blogjava.net/zhuanggl/aggbug/326120.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/zhuanggl/" target="_blank">井底青蛙,常望天空</a> 2010-07-14 19:18 <a href="http://www.blogjava.net/zhuanggl/archive/2010/07/14/326120.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>