往事如風(fēng)
          記錄工作中的點點滴滴 留住那些淡淡的回憶
          posts - 6,  comments - 3,  trackbacks - 0

          BTrace 說明】 http://kenai.com/projects/btrace
          是一個實時監(jiān)控工具,使用了 java agent jvm attach 技術(shù),可以在不停機的情況下實時監(jiān)控線上程序的運行情況,另外,對 btrace 腳本(實際上就是 java 程序)做了非常嚴(yán)格的安全限制,安全性很高,對應(yīng)用程序基本沒有影響。在性能方面, cobar 進行過測試,對方法進行調(diào)用耗時統(tǒng)計的時候,基本消費在微秒級別,可以說微不足道。

          【背景】

          在中文站 napoli 上線過程后,發(fā)現(xiàn)了一個奇怪的現(xiàn)象,盡管"已知"的 offer 發(fā)送端都已經(jīng)遷移到
          napoli 系統(tǒng)中,但是老的 mq 系統(tǒng)仍然有新的 offer 消息進來,因為連接 mq 的服務(wù)器非常多,定位消息來源成了一個非常大的問題。這種情況,想到了使用 BTrace 在某一臺服務(wù)器進行線上監(jiān)控進而期望發(fā)現(xiàn)這個幽靈。

          【過程】

          首先,我們需要知道兩個基本信息:消息類型和來源 ip ,這樣才可以定位 offer 消息的來源。

          要知道來源 ip ,需要找到服務(wù)器端 管理的類,只有在建立 socket 的地方,才可以抓到具體 ip ,經(jīng)過分析 amq 代碼,發(fā)現(xiàn) tcp 連接基本是由下面這個類來服務(wù)所有消息的接收的:

          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 對象的成員變量,所有我們只要監(jiān)控 readCommand 方法,這個方法的返回值實際上就是一個 ActivemqObjectMessage 對象,這樣就可以在一個方法上加攔截器就可以同時捕獲到 ip 和消息對象,兩全其美?。?!

          protected Object readCommand() throws IOException {

          return wireFormat.unmarshal(dataIn);

          }

          因為原有 ESB 消息通道都是一個隊列 ESBQueue ,所以無法通過隊列名稱來確定消息類型,必須通過
          ESBTransferObject 對象來取得消息類型: destType offer 的區(qū)間是 1000-1008

          public class ESBTransferObject implements Serializable {

          private static final long serialVersionUID = -5975115234845303878L;

          /**

          * 消息體,原則上對象序列化后的XML數(shù)據(jù)(String) 注意使用XML1.1規(guī)范。

          */

          private Object content;

          /**

          * 用戶自定義數(shù)據(jù)

          */

          private Object userDefineData;

          /**

          * 目的消息類型

          */

          private int destType = -1;


          但是,在服務(wù)器端并沒有 ESBTransferObject 對象,無法反序列化( BTrace 也不支持反序列化操作),所以沒有方法簡單取得消息類型信息!!!

          OK ,我不反序列化,直接拿二進制 byte[] ,類型信息應(yīng)該是在固定位置的吧?但是發(fā)現(xiàn)這個對象
          content 變長字符串定義在類型之前,類型位置不確定了,暈倒啊
          不死心,輸出二進制數(shù)據(jù),柳暗花明啊,原來對象序列化的時候, primitive field 都是緊接著類型信息寫入的,所以,類型信息是在固定位置的 ,類型信息始終是 255 , 256 兩個字節(jié)(實際上是 4 個字節(jié),但是目前我們只占有 2 個) Ok ,編寫代碼,測試環(huán)境運行一下,暈倒,竟然有數(shù)組溢出!

          使用 BTrace ,把這個數(shù)組打印下來(這個需要點技巧, btrace for 都不允許),竟然發(fā)現(xiàn) 位置偏移到 205 , 206 位置 ,這個真的不知道什么原因,估計是客戶端發(fā)送的時候壓縮了,簡單修改偏移量,測試運行, 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) { //捕獲調(diào)用對象和返回值

          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);//捕獲消息內(nèi)容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]); //轉(zhuǎn)換205,206字節(jié)為消息類型

          //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))));

          }

          }

          }

          }


          打印結(jié)果:


          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 是一個強大的工具,但是,在線上檢測的時候考慮時效性和安全性,必須有一個經(jīng)過檢驗的腳本庫才可以安全及時的定位系統(tǒng)問題.

          posted on 2010-07-14 19:18 井底青蛙,常望天空 閱讀(374) 評論(0)  編輯  收藏 所屬分類: java 、tools

          <2010年7月>
          27282930123
          45678910
          11121314151617
          18192021222324
          25262728293031
          1234567

          常用鏈接

          留言簿

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 乐都县| 北京市| 绥德县| 青阳县| 富蕴县| 阳新县| 开鲁县| 台山市| 大同市| 郧西县| 水富县| 永登县| 托克逊县| 林西县| 衡阳市| 永仁县| 荔波县| 金川县| 洛扎县| 汶川县| 许昌县| 万全县| 安龙县| 连城县| 彭阳县| 泸水县| 南康市| 石河子市| 彭山县| 海城市| 忻城县| 大兴区| 西吉县| 鄂托克旗| 四川省| 乌兰县| 新平| 嘉兴市| 都匀市| 台北县| 鄯善县|