最近在优化一个老系统,这是一个对外提供的dubbo服务,对响应时间要求很高,目前遇到的问题是到业务高峰时段,会有部分响应超时的情况出现。
既然要优化,第一步是得分析分析时间都消耗在哪里了,于是想在服务内的方法上加切面,使用spring的stopwatch来统计每次请求中每个方法执行的时间。写好了注解和切面,把注解加到方法上,然后本地跑起来测试,启动是成功了,但是dubbo admin上查无此服务,这就尴尬了,为什么呢。
在dubbo AnnotationBean里打断点跟了下
Service service = bean.getClass().getAnnotation(Service.class);
if (service != null) {
ServiceBean<Object> serviceConfig = new ServiceBean<Object>(service);
serviceConfig.setRef(bean);
if (void.class.equals(service.interfaceClass())
&& "".equals(service.interfaceName())) {
if (bean.getClass().getInterfaces().length > 0) {
serviceConfig.setInterface(bean.getClass().getInterfaces()[0]);
} else {
throw new IllegalStateException("Failed to export remote service class " + bean.getClass().getName() + ", cause: The @Service undefined interfaceClass or interfaceName, and the service class unimplemented any interfaces.");
}
}
发现这个时候bean.getClass()得到的是xxxFacadeImpl$$EnhancerBySpringCGLIB$$f77b0d54,getInterafaces得到的是[org.springframework.aop.SpringProxy,org.springframework.aop.framework.Advised,org.springframework.cglib.proxy.Factory]。大概有些同学就疑惑了,为啥这取到的是动态代理类呢,还不是因为咱用了spring aop来搞切面,人就是用动态代理方式来实现的。。。
public void setInterface(String interfaceName) {
this.interfaceName = interfaceName;
if (id == null || id.length() == 0) {
id = interfaceName;
}
}
public void setInterface(Class<?> interfaceClass) {
if (interfaceClass != null && !interfaceClass.isInterface()) {
throw new IllegalStateException("The interface class " + interfaceClass + " is not a interface!");
}
this.interfaceClass = interfaceClass;
setInterface(interfaceClass == null ? (String) null : interfaceClass.getName());
}
setInterface之后,serviceConfig的interfaceName和interfaceClass都变成了org.springframework.aop.SpringProxy,export后url是
dubbo://10.0.75.1:11601/org.springframework.aop.SpringProxy?anyhost=true&dubbo=2.5.7&generic=false&interface=org.springframework.aop.SpringProxy&methods=*&pid=14592
莫名喜感哈哈。看代码之后只能把这个if 给绕过去,这样export的时候interfaceName是你指定的那个,也就是在service注解里要么设置interfaceClass,要么设置interfaceName。于是这个问题解决了,服务又在dubbo admin上出现啦。
试着调用了一下,然后看日志,发现我去,这日志怎么跟想象的不一样,少了很多,折腾了许久,在切面上加断点,发现有些加了注解的方法就是不生效,怎么办,搜索一下找找灵感吧,发现了一篇文章https://www.baeldung.com/spring-aop-vs-aspectj,这文章本来看着也觉得没什么意思,后来一不小心瞄到了一句话It’s also worth noting that in Spring AOP, aspects aren’t applied to the method called within the same class。啊啊啊啊啊,真是一语惊醒梦中人啊,让我想起了以前就有的一个印象,在使用spring框架的工程中,service里a方法调用b方法,如果b上加了事务注解而a没加,那么事务是不生效的。
当时只是记住了这个结论,但是没有了解为啥,现在想想,动态代理是类似
public class proxy(){
public void a(){
doSomething();
super.a();
doSometing();
}
所以通过调用a去调用b的时候,实际不是在代理类的对象里,而是在被代理类的对象里了,也就是切面被略过了,咋办,难道我要改写spring aop吗,算了吧,想了想还是直接用aspectj吧,正好刚刚看过了spring-aop-vs-aspectj不是吗哈哈。