项目中用到了dubbo,默认当然都是同步调用的。某一天想到,既然是调用远程服务,那应该是可以异步调用的,这样说不定可以增加并发性,减少时间。于是上网查了一下,果然可以。配置远程服务为异步之后,像如下调用:
//调用后立即返回null
Person person=demoServer2.getPerson("www", 13);
System.err.println("立即返回的为null:"+person);
//拿到调用的Future引用,当结果返回后,会被通知和设置到此Future。
Future<Person> pFuture = RpcContext.getContext().getFuture();
//如果Person已返回,直接拿到返回值,否则线程wait,等待Person返回后,线程会被notify唤醒。
person = pFuture.get();
System.out.println("返回的有值"+person);
System.out.println(person);
见:
但感觉这样使用也不太方便,每次调用完都得去拿一个future。然后想到了hibernate好像有一种代理模式,就是查询到的对象其实是一个代理对象,当调用这个代理对象的方法的时候,才会真正去执行sql查询到对象。
于是想到可以仿照这种思路,每次异步调用之后返回一个代理结果,当调用代理结果的方法时,才去执行future.get()拿到返回结果。代码如下:
1.先写一个远程服务的代理类。
package com.csair.csm.web.proxy;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.Future;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.alibaba.dubbo.rpc.RpcContext;
/**
* 服务代理类,用于对异步服务提供代理
* <br>!!!使用代理服务调用的返回对象不为null,如果实际为null,则调用返回对象的任何方法都将返回null
* @author abo
*/
public class ProxyService implements MethodInterceptor {
/**
* 服务者
*/
private Object service;
private ProxyService(Object service) {
this.service = service;
}
/**
* 创建一个代理服务
* @param service
* @return
*/
@SuppressWarnings("unchecked")
public static <S> S create(S service) {
// 1.工具类
Enhancer en = new Enhancer();
// 2.设置父类
en.setSuperclass(service.getClass());
// 3.设置回调函数
en.setCallback(new ProxyService(service));
// 4.创建子类(代理对象)
return (S) en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 执行服务者方法
Object result = method.invoke(service, args);
// 获取返回
Future<Object> future = RpcContext.getContext().getFuture();
if (future == null) {
// future为null,说明是同步调用,直接返回
return result;
}
// 返回类型
Class<?> returnClass = method.getReturnType();
if (Modifier.isFinal(returnClass.getModifiers())) {
// final类直接返回
return future.get();
}
// 返回代理结果
return ProxyServiceResult.create(future, returnClass);
}
}
这个类用来给远程的服务提供代理,调用这个代理服务的方法时,先是调用原本的异步服务的方法,然后向调用者返回一个代理结果。
不过这样子也有一些不足,就是如果异步服务的返回是null的话,因为在创建代理结果的当时并不知道实际结果,所以代理结果都不会为null。另外就是对于final类无法代理,只能立即等待并返回实际结果。要解决这个问题,目前想到的方法是可以给异步服务的结果都用一个Result类包装起来。
2.然后是一个服务结果的代理类。
package com.csair.csm.web.proxy;
import java.lang.reflect.Method;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.csair.csm.servicelog.LoggerUtils;
/**
* 异步服务返回值的代理
* <br>!!!代理对象不为null,如果实际为null,则调用其任何方法都将返回null
* @author abo
*/
public class ProxyServiceResult implements MethodInterceptor {
/**
* 异步调用的返回
*/
private Future<Object> resultFuture;
/**
* 日志
*/
private static Logger logger = LoggerUtils.getCommonLogger();
private ProxyServiceResult(Future<Object> resultFuture) {
this.resultFuture = resultFuture;
}
/**
* 创建一个代理结果
* @param resultFuture
* @param resultClass
* @return
*/
public static Object create(Future<Object> resultFuture, Class<?> resultClass) {
// 1.工具类
Enhancer en = new Enhancer();
// 2.设置父类
en.setSuperclass(resultClass);
// 3.设置回调函数
en.setCallback(new ProxyServiceResult(resultFuture));
// 4.创建子类(代理对象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args,
MethodProxy proxy) throws Throwable {
// 从future获取返回值
Object result = resultFuture.get();
if (result == null) {
// 结果为null,调用会出异常,直接返回null
logger.warn("远程异步服务的实际返回结果为null,代理结果不为null。");
return null;
}
// 再去调用
return method.invoke(result, args);
}
}
这个用来给调用异步服务的返回结果提供代理,在调用代理结果的方法时,会从future中get到实际结果,然后去调用实际结果上的方法。
这样就大功告成了,使用时只需要这样子:
/**
* 日志服务
*/
@GuardedBy("itself")
@Reference(timeout = 60000, async = true) // 异步服务
private LoggerService loggerService;
/**
* 初始化之后执行,替换异步服务为代理服务
*/
@PostConstruct
public void init() {
this.loggerService = ProxyService.create(this.loggerService);
}
先是配置远程服务为异步(async = true),然后在@PostConstruct中替换原来的服务为代理服务。然后就可以像使用同步服务那样正常的使用了。
因为对cglib代理也不熟悉,代理类只是仿照网上的例子改写的,如果有写得不好的地方,还希望有人批评指正。