使用netty和spring实现rpc

使用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,使用注解注入,就可以直接使用了。远程当然要执行方法返回正确的结果。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值