使用spring容器来管理RPC代理类非常方便,依赖注入使得用户感受不到远程调用的繁琐。
首先定义向远程调用需要的基本信息:
目标类、方法、参数。
远程调用到结果的回传需要时间的,所以执行方法之后我们需要等待,在结果回传之后唤醒线程获取结果,通常使用Map将对象保存起来,将key发送给远程服务,远程服务返回时也要回传该key用来获取保存的对象并唤醒,所以基本信息中还需要一个key或者一类的参数。将其封装为Resuest
public class Request implements Serializable {
private static final long serialVersionUID = 1L;
private Class<?> target;
private Object[] params;
private String methodName;
private java.util.List<Class<?>> paramTypes;
private int id;
}
接下来是对方法的代理封装。由于IOC容器的存在,用户声明需要注入将要执行的类,但是不让spring扫描注入,使用代码注入对应的代理类,将方法执行替换为远程调用取得结果。
使用包扫描为每个需要代理的类生成代理并注入IOC容器,要保证在初始化其他用户类之前执行,所以我们将@order设为最大。
Set<Class<?>> classes = ClassScaner.scanPackageByAnnotation("cn.lemon.web.service",
cn.lemon.annotation.LemonService.class);
DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext
.getAutowireCapableBeanFactory();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(cls);
if (server==null) {
server = new RequestServer(rpcServer, rpcPort);
}
enhancer.setCallback(new RequestProxy(server,cls));
Object proxy = enhancer.create();
beanFactory.registerSingleton(dataInit(cls), cls.cast(proxy));
代理中将返回值交给了RequestProxy这个实现MethodInterceptor的类,在intercept方法中执行远程请求。并且把netty服务器传入,以便发送请求。
Request request = new Request();
//保存当前对象
int id = ProxyContext.storeProxy(this);
//封装request
request.setParams(args).setTarget(targetClass).setId(id).setMethodName(method.getName());
Class<?>[] paramTypes = method.getParameterTypes();
request.setParamTypes(paramTypes);
server.request(request);
//线程等待
Object result = getResult();
Class<?> toType = method.getReturnType();
return result;
private Object getResult() {
synchronized (this) {
try {
while (!hasDone) {
wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
return this.result;
}
}
提供唤醒线程的方法
public void setDone(Object result) {
synchronized (this) {
this.result = result;
hasDone = true;
notifyAll();
}
远程服务器中接收到信息之后,通过反射调用相关的方法取得结果并返回。
Request request = (Request) msg;
Class<?> target = request.getTarget();
String methodName = request.getMethodName();
Class<?>[] paramTypes = request.getParamTypes();
Object[] args = request.getParams();
Builder resultBuilder = RpcResponseProxy.Builder();
try {
FastClass fastClass = FastClass.create(target);
FastMethod method = fastClass.getMethod(methodName, paramTypes);
Object result = method.invoke(fastClass.newInstance(), args);
resultBuilder.Status(1).Result(result);
} catch (Exception e) {
e.printStackTrace();
resultBuilder.Status(0).Msg(e.getMessage());
}
resultBuilder.Id(request.getId());
ctx.channel().writeAndFlush(resultBuilder.Build());
当前程序接收到结果之后唤醒等待
int id = msg.getId();
int status = msg.getStstus();
if (status == 0) {
throw new RuntimeException(msg.getMsg());
}
ProxyContext.load(id).setDone(msg.getResultObj());
在spring本地项目中定义好类,返回结果全部写为null,使用注解注入,就可以直接使用了。远程当然要执行方法返回正确的结果。