【
BTrace
說明】
http://kenai.com/projects/btrace
是一個實時監(jiān)控工具,使用了
java agent
和
jvm attach
技術,可以在不停機的情況下實時監(jiān)控線上程序的運行情況,另外,對
btrace
腳本(實際上就是
java
程序)做了非常嚴格的安全限制,安全性很高,對應用程序基本沒有影響。在性能方面,
cobar
進行過測試,對方法進行調用耗時統(tǒng)計的時候,基本消費在微秒級別,可以說微不足道。
【背景】
在中文站
napoli
上線過程后,發(fā)現(xiàn)了一個奇怪的現(xiàn)象,盡管"已知"的
offer
發(fā)送端都已經遷移到
napoli
系統(tǒng)中,但是老的
mq
系統(tǒng)仍然有新的
offer
消息進來,因為連接
mq
的服務器非常多,定位消息來源成了一個非常大的問題。這種情況,想到了使用
BTrace
在某一臺服務器進行線上監(jiān)控進而期望發(fā)現(xiàn)這個幽靈。
【過程】
首先,我們需要知道兩個基本信息:消息類型和來源
ip
,這樣才可以定位
offer
消息的來源。
要知道來源
ip
,需要找到服務器端
管理的類,只有在建立
socket
的地方,才可以抓到具體
ip
,經過分析
amq
代碼,發(fā)現(xiàn)
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
對象的成員變量,所有我們只要監(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;
|
但是,在服務器端并沒有
ESBTransferObject
對象,無法反序列化(
BTrace
也不支持反序列化操作),所以沒有方法簡單取得消息類型信息!!!
OK
,我不反序列化,直接拿二進制
byte[]
,類型信息應該是在固定位置的吧?但是發(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) { //捕獲調用對象和返回值
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字節(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))));
}
}
}
}
打印結果:
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 是一個強大的工具,但是,在線上檢測的時候考慮時效性和安全性,必須有一個經過檢驗的腳本庫才可以安全及時的定位系統(tǒng)問題.