项目中用到了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代理也不熟悉,代理类只是仿照网上的例子改写的,如果有写得不好的地方,还希望有人批评指正。