-
Notifications
You must be signed in to change notification settings - Fork 11
MethodEventSubscribe
该功能用于异步订阅方法的调用事件
订阅方法调用的实现方式较多,常用方式为动态代理,在方法调用前后做处理,但是这种方式对于第三方jar包中的类做代理是有局限性的(类实例直接在jar包中创建出来,无法代理),而该文档介绍的使用方式可以不局限于业务代码的代理 依赖于javaagent实现,通过字节增强实现方法调用订阅,之所以为订阅而不是拦截,主要是考虑该功能主要应用于监控,而非业务逻辑,即便是去掉探针接入,也不应该影响到业务,所以在使用场景上需要用户考虑是否适用
- 使用注解或者一行配置即可订阅方法调用;
- 订阅的方法调用事件,不局限于业务类方法,第三方jar包中的类方法一样可以订阅;
- 支持批量订阅;
配置key | 类型 | 默认值 | 说明 |
---|---|---|---|
eventSubscribe.check.minSize | Integer | 10 | 订阅事件检测死循环的最小调用栈大小,避免因代码错误导致死循环同时也降低检测性能损耗,如果死循环时的调用栈长度小于该值,则需要人为调小该值,默认为10 |
eventSubscribe.callAsync | Boolean | true | 事件是否为异步订阅,默认为true,即异步调用,如果为false,则事件源与订阅逻辑处于同一个线程,同步调用 |
如果未配置忽略被订阅方法参数,则订阅方法必须包含被订阅方法的每个参数,且位置一一对应,订阅方法参数可在最后增加三个参数,分别为:被订阅类实例、被订阅方法返回值、被订阅方法抛出的异常,这三个参数的位置可不限先后(根据类型注入),且个数是可选的
- 【必须】在订阅方法上添加@Subscribe,如果希望订阅多个方法可添加多个@Subscribe;
- 【可选】如果订阅时忽略被订阅方法的参数,则在订阅方法上增加@IgnoreParams;
- 【可选】如果订阅方法的类实例希望自动创建,则在类上增加@AutoInstance,如果自己管理类实例(spring),则无需添加该注解;
- 【可选】在服务入口类上增加注解@Register({订阅类.class}),保证订阅类加载早于被订阅类,如果可确定订阅类早于被订阅类加载,则无需增加该注解;
Subscribe注解配置说明(源码中也有对应注释),如果某个注解方法不配置,则不过滤该方法
方法名 | 说明 | 注意事项 | 配置参考 |
---|---|---|---|
interfaces | 被订阅类匹配的接口,任何一个匹配到(包含多级接口)则匹配成功 | interfaces、parent、className方法必须配置至少一个(不能全为默认值) | @Subscribe(interfaces = {"xxxx", "xxxx"}, .....) |
parent | 被订阅类匹配的父类(多级父类时,任何一个匹配到,则匹配成功) | interfaces、parent、className方法必须配置至少一个(不能全为默认值) | @Subscribe(parent= "xxxx", .....) |
className | 被订阅类的类名 | interfaces、parent、className方法必须配置至少一个(不能全为默认值) | @Subscribe(className= "xxxx", .....) |
methodName | 被订阅类匹配的方法名 | @Subscribe(methodName= "xxxx", .....) | |
isMethodNameRegex | methodName值是否正则表达式 | @Subscribe(methodName= "execute.*", isMethodNameRegex=true, .....) | |
argumentNumber | 被订阅方法的参数个数 | @Subscribe(argumentNumber= 3, .....) | |
argumentType | 被订阅方法的对应位置的参数类型,如果不指定某个位置的参数类型,可在对应位置使用空字符串,如:argumentsType={"java.lang.Integer", "", "java.lang.String"} | @Subscribe(argumentType= {"java.lang.Integer", "", "java.lang.String"}, .....) |
被订阅类
package cn.xxx.test;
public class TestService {
public String execute1(String cmd) {
// 业务逻辑....
return "success";
}
public String execute2(String cmd) {
// 业务逻辑....
return "success";
}
public void method(Integer num, String type) {
// 业务逻辑....
}
}
订阅类
package cn.xxx.test;
@AutoInstance
public class TestSubscribe {
@IgnoreParams
@Subscribe(className="cn.xxx.test.TestService", methodName = "execute.*", isMethodNameRegex = true)
@Subscribe(className="cn.xxx.test.TestService", methodName = "method")
public void deal(TestService service, String ret, Throwable t) {
// 异常逻辑处理....
}
}
服务入口类
package cn.xxx.test;
@Register({TestSubscribe.class})
public class Application {
public static void main(String[] args) {
// 启动
}
}
在配置文件中增加相应配置,针对上面的示例,不使用注解的等价配置为
eventSubscribe.group.ignoreParams.serviceSubscribe=.*cn\\.xxx\\.test\\.TestService\\.execute.* > cn.xxx.test.TestSubscribe.deal\\(.*
eventSubscribe.group.ignoreParams.serviceSubscribe2=.*cn\\.xxx\\.test\\.TestService\\.method\\(.* > cn.xxx.test.TestSubscribe.deal\\(.*
事件订阅配置,格式为:eventSubscribe.group.xxx=source classMethodRegular > subscribe className.methodRegular
key说明:xxx为自定义字符串,代表事件分组,如果包含.ignoreParams.则不强制订阅方法参数必须包含被订阅方法参数,被订阅方法中的参数也不会传递到订阅方法的参数中,如果key中不包含.ignoreParams.,则subscribe className.methodRegular的方法参数必须包含source classMethodRegular的方法参数且位置一致
value说明:source classMethodRegular为被订阅类方法正则,subscribe className.methodRegular为订阅类全称及方法正则,其中方法正则中必须包含一个参数起始(# 订阅方法参数后面可多出3种类型的参数(ignoreParams的也一样):被订阅类实例、被订阅方法返回值、被订阅方法抛出的异常,参数的位置放在最后,这个三个参数的顺序不限先后
配置支持一个方法被多个注册者监听,一个监听者也可以监听多个方法,多对多的关系
注意:在订阅类中不能再调用被订阅类的订阅方法,否则会导致死循环(已做了运行限制,但有些用法可能失效);事件的通知是异步的,如果消费慢于生产可能导致事件丢弃;在一个分组内的事件通知为单线程调用;
为什么要设置不同分组,因为在一个组内被订阅、订阅配置都可能匹配多个方法,而且订阅与被订阅的注册时间不分先后,所以为了绑定注册与被注册关系,需要中间的桥梁,即时间组,事件传递为:多个被订阅方法 -> 组事件 ->多个订阅方法
- 监控所有mysql客户端包请求异常、mongodb客户端包请求异常
- 如果出现无法订阅事件,可能是被订阅类过早的加载了,如配置了订阅JDK中的类,很有可能导致无法订阅;