上周五接近6个小时都在开会,悲剧阿。 美好的一天又这样被浪费了。
还好开会的时候自带了笔记本,闲来无聊又重新把btrace的内容重新梳理了一遍。
ps : 以前虽然看过btrace的使用,但根本是一种阅读者的态度,并没有反编译btrace源码进行查看。 还有就是实际也没写过几个btrace脚本,很多使用就显得很生疏。所以需要多加强下,只能多看过个2,3遍。
更详细,更精彩的一些btrace内容,请查看: btrace一些你不知道的事(源码入手)
几个常用url :
1. javadoc文档: http://btrace.kenai.com/javadoc/1.2/index.html
2. UserGuide : http://kenai.com/projects/btrace/pages/UserGuide
3. DeveloperGuide : http://kenai.com/projects/btrace/pages/DeveloperGuide
4. jvisualvm plugins : http://visualvm.java.net/pluginscenters.html , http://visualvm.java.net/plugins.html
几个使用注意点:
1. Kind.CALL 和 Kind.ENTRY的使用理解
- Kind.ENTRY意指进入匹配probe点,跟你@Location设置的clazz和method没有任何关系。
- Kind.CALL意指从某个匹配probe的方法中调用了匹配A class method的点,一定要和clazz,method配合使用。clazz和method的默认值为"",所以不能被匹配。
说白了一个就是跟踪A和B的调用关系,另一个只是关注调用了B的方法。
2. @ProbeClassName 和 @ProbeMethodName的理解
因为btrace的probe点,clazz和method都支持正则匹配,clazz还支持super class or inteface匹配。所以对应匹配该probe的点会不同。
- @ProbeClassName : 就是具体匹配对应的clazz规则的class name。如果没配置正则,则就是Probe clazz自己。
- @ProbeMethodName : 就是具体匹配对应的method规则的method name。如果没配置正则,则就是Probe method自己。
3. @TargetInstance 和 @TargetMethodOrField的理解
这个可以结合Kind.CALL进行说明,如果是probe A class的方法中调用了匹配B class。 @TargetInstance 返回的就是B class的实例,@Self返回的就是A class
- @TargetInstance : 就是probe点内部间接调用class的对象引用
- @TargetMethodOrField : 就是probe点内部间接调用class的方法或者对象属性。
说明: @TargetInstance和@TargetMethodOrField,必须结合Kind.CALL模式进行使用,具体原因看一下其代表的意思也就很明确了。
4. @Return , @Duration 和 方法参数AnyType的理解
可以理解下,我们常见的日志记录的需求,一般要求记录请求参数和返回结果,再加上对应的处理时间。那这时@Return , @Duration就派上用场了
- @Return : 就是获取方法的返回对象。
- @Duration : 就是整个Probe method调用所耗费的时间,单位us。
- AnyType : 就是获取对应请求的参数,可以泛指任意类型。 同样你如果明确参数对象的,可以强制指定具体参数的类型
一般常见的使用: public static void log(@Return Object result, Object proxy, Method method, Object[] args, @Duration long durationL)
说明: @Return和@Duration,必须结合Kind.Return模式进行使用,具体原因也挺明确了
5. @OnEvent的理解
就是和命令行进行交互,对事件的响应处理。比如btrace运行后,通过Ctrl + C指令,会有如下提示:
1.Please enter your option:
2. 1. exit
3. 2. send an event
4. 3. send a named event
如果你选择2或者3,就是对btrace client发起一个event事件。也挺好理解的
1.@OnEvent
2. public static void event() {
3. println("event");
4. }
5.
6. @OnEvent("A") // 相应name A event
7. public static void eventA() {
8. println("eventA");
9. }
10.
11. @OnEvent("B") // 相应name B event
12. public static void eventB() {
13. println("eventB");
14. }
说明: 一般可通过event事件,控制一下输出统计的内容信息。
6. @OnExit的理解
老实说,我也并不是非常理解。只知道通过OnExit可以监控btrace脚本发起的exit()调用,在btrace client退出之前做点事情。暂没想到特定的应用场景,数据清理?
1.@OnExit
2. public static void onexit(int code) {
3. println("exit");
4. }
5.
6. @OnTimer(1000)
7. public static void ontime() {
8. println(i++);
9. if (i == 5) {
10. println("do exit");
11. exit(0);
12. }
13. }
推荐jvisualvm btrace GUI
1. 启动,关闭,Event事件发送都挺方便的。
2. 不用每次vi 打开java文件进行编辑,修改script就方便多了
常用btrace监控脚本:
1. Jetty监控request/response buffer,有项目在使用中发现出现http 413错误(Request entity too large) , http://www.checkupdown.com/status/E413_cn.html
初步怀疑是和buffer参数有关,原先使用jboss的参数为maxHttpHeadSize=8196,所以写了脚本提取了下线上的jetty参数,后面就修改了jetty参数为8k,解决了问题
1.@BTrace
2.public class JettyHeadBufferTracer {
3.
4. @OnMethod(clazz = "org.eclipse.jetty.http.HttpBuffers", method = "/.*get.*Buffers/", location = @Location(value = Kind.ENTRY))
5. public static void bufferMonitor(@Self Object self) {
6. Field requestBuffersField = field("org.eclipse.jetty.http.HttpBuffers", "_requestBuffers");
7. Field responseBuffersField = field("org.eclipse.jetty.http.HttpBuffers", "_responseBuffers");
8.
9. Field bufferSizeField = field("org.eclipse.jetty.io.ThreadLocalBuffers", "_bufferSize");
10. Field headerSizeField = field("org.eclipse.jetty.io.ThreadLocalBuffers", "_headerSize");
11. Object requestBuffers = get(requestBuffersField, self);
12. int requestBufferSize = (Integer) get(bufferSizeField, requestBuffers);
13. int requestHeaderSize = (Integer) get(headerSizeField, requestBuffers);
14. Object responseBuffers = get(responseBuffersField, self);
15. int responseBufferSize = (Integer) get(bufferSizeField, responseBuffers);
16. int responseHeaderSize = (Integer) get(headerSizeField, responseBuffers);
17.
18. println(strcat(strcat(strcat("requestBufferSize : ", str(requestBufferSize)), " requestHeaderSize : "),
19. str(requestHeaderSize)));
20. println(strcat(strcat(strcat("responseBufferSize : ", str(responseBufferSize)), " responseHeaderSize : "),
21. str(responseHeaderSize)));
22. }
23.}
结果:
1.requestBufferSize : 8192 requestHeaderSize : 6144
2.responseBufferSize : 12288 responseHeaderSize : 6144
2. 项目中使用了dbcp做为数据库连接池,但对于连接池大小是否够用没有很直观的数据可以提供,所以写了个脚本提取一下数据
主要的数据内容:
* maxActive(最大连接池大小),numActive(目前处于使用中),numIdle(处于空闲状态的连接数)
* maxTotal(开启ps的最大值),totalActive(目前处于使用ps的总数),keyActive(当前sql的ps使用数),keyIdle(当前sql的ps空闲数) 针对开启了ps cache
1.<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
2.....
3. <property name="poolPreparedStatements" value="true" />
4. <property name="maxOpenPreparedStatements" value="10" />
5.....
6.</bean>
1.@BTrace
2.public class DbcpTracer {
3.
4. @OnMethod(clazz = "org.apache.commons.pool.impl.GenericObjectPool", method = "/.*Object/", location = @Location(value = Kind.ENTRY))
5. public static void poolMonitor(@Self Object self) {
6. Field maxActiveField = field("org.apache.commons.pool.impl.GenericObjectPool", "_maxActive");
7. Field numActiveField = field("org.apache.commons.pool.impl.GenericObjectPool", "_numActive");
8. Field poolField = field("org.apache.commons.pool.impl.GenericObjectPool", "_pool");
9. Field sizeField = field("org.apache.commons.pool.impl.CursorableLinkedList", "_size");
10. int maxActive = (Integer) get(maxActiveField, self);
11. int numActive = (Integer) get(numActiveField, self);
12. int numIdle = (Integer) get(sizeField, get(poolField, self));
13.
14. println(strcat(strcat(strcat(strcat(strcat("maxActive : ", str(maxActive)), " numActive : "), str(numActive)),
15. " numIdle : "), str(numIdle)));
16. }
17.
18. @OnMethod(clazz = "org.apache.commons.pool.impl.GenericKeyedObjectPool", method = "/.*Object/", location = @Location(value = Kind.ENTRY))
19. public static void psMonitor(@Self Object self, Object key) {
20. Field maxTotalField = field("org.apache.commons.pool.impl.GenericKeyedObjectPool", "_maxTotal"); // connectio的maxActive
21. Field totalActiveField = field("org.apache.commons.pool.impl.GenericKeyedObjectPool", "_totalActive"); // connectio的active
22. Field poolMapField = field("org.apache.commons.pool.impl.GenericKeyedObjectPool", "_poolMap"); // connectio的active
23.
24. Field keyActiveField = field("org.apache.commons.pool.impl.GenericKeyedObjectPool$ObjectQueue", "activeCount"); // key的active
25. Field keyIdleField = field("org.apache.commons.pool.impl.GenericKeyedObjectPool$ObjectQueue", "queue"); // key的idle
26. Field keyIdleSizeField = field("org.apache.commons.pool.impl.CursorableLinkedList", "_size");
27.
28. Field sqlField = field("org.apache.commons.dbcp.PoolingConnection$PStmtKey", "_sql");
29.
30. int maxTotal = (Integer) get(maxTotalField, self);
31. int totalActive = (Integer) get(totalActiveField, self);
32. Map<Object, Object> poolMap = (Map<Object, Object>) get(poolMapField, self);
33. int keyActive = 0, keyIdle = 0;
34. if (poolMap != null) {
35. Object queue = get(poolMap, key);
36. if (queue != null) { // ObjectQueue
37. keyActive = (Integer) get(keyActiveField, queue);
38. keyIdle = (Integer) get(keyIdleSizeField, get(keyIdleField, queue));
39. }
40. }
41. println(strcat(strcat(strcat(strcat(strcat(strcat(strcat("maxTotal : ", str(maxTotal)), " totalActive : "),
42. str(totalActive)), " keyActive : "), str(keyActive)), " keyIdle "),
43. str(keyIdle)));
44.
45. println(strcat("Ps Key: ", str(get(sqlField, key))));
46. }
47.
48.}