博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
RPC客户端如何实现-KRPC源码解析
阅读量:6378 次
发布时间:2019-06-23

本文共 3718 字,大约阅读时间需要 12 分钟。

1.前言

这篇文章主要结合(我自己开源的一个RPC框)代码详细分析一下RPC客户端具体实现。在文中,我们主要讲述了一次调用RPC调用中各流程,这篇文章就结合KRPC的代码仔细讲解一下

开始前,我先说一下KRPC的网络传输中的内容: 1.服务实现名字。server端需要你服务实现的名字,才能知道你调用的是哪个实现的方法,跟web项目中的controller写的路径一样。

2.方法名字。知道了调用的是哪个类,接下来就需要调用的哪个方法了,无需多言。

3.方法参数名字。KRPC会获取每个方法参数的class的全路径(原因在下面展开讲)

4.方法中传入的值。

2.源码分析

2.1 客户端端初始化

KRPC在调用时,必须要先执行

KRPC.init("/opt/krpc/client/client.xml");复制代码

该方法内部解析该配置文件,把配置文件中的服务及其地址解析后并放入内存缓存中,供后面TCP请求时,快速获取到服务地址。

2.2 动态代理

可以看到,我们调用的都是接口,并且我们没有引入接口的实现包,调用后怎么能获取到server端的数据呢?

这就引入了动态代理,由代理类替我们处理接口调用的动作。这里使用的是jdk的动态代理实现方式。

调用者接口代理获取方法

UserService service = ProxyFactory.create(UserService.class, "user", "userService");复制代码

ProxyFactory#create方法

public static 
T create(Class
type, String serviceName,String serviceImpleName) { ProxyHandler handler = new ProxyHandler(serviceName,serviceImpleName); return (T) handler.bind(new Class
[]{type});复制代码

ProxyHandler.java

@Override	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {		// 构造请求request		Request request = new Request();		request.setMethodName(method.getName());		request.setServiceImplName(serviceImplName);		request.setParamsValues(Arrays.asList(args));		Class[] sourceTypes = method.getParameterTypes();		List
paramsTypeName = new ArrayList
(); for (int i = 0; i < args.length; i++) { paramsTypeName.add(sourceTypes[i].getName()); } request.setParamsTypesName(paramsTypeName); Class returnClass = method.getReturnType(); return RequestHandler.request(serviceName, request, returnClass); }复制代码

上面放了3段代码,是动态代理相关的全部代理,是的,就这么简单。通过ProxyFactory将接口绑定,获取接口代理,这样调用接口中的方法,直接交给ProxyHandler#invoke方法来执行。

invoke中主要是构建网络请求的参数Request类,然后调用请求控制类request。

我们在前言中有讲到Request的内容。这里说明一下对于参数Class为什么传递类的全路径名字,而不是Class类。

1.因为服务端的类是通过URLClassLoader动态加载进来的,客户端直接传递客户端这边的Class会报ClassNotFound异常

2.传递起类路径数据长度更小

服务端只需要根据类名,通过Class.forname加载进来就ok了。

2.3. 序列化

序列化是RPC框架中重要的一个环结,KRPC采用了Hessian序列化方式,

因为在服务端的各service的类都是动态加载进来的

public static Object deserialize(byte[] by, ClassLoader classLoader) throws IOException {		if (by == null)			throw new NullPointerException();		ByteArrayInputStream is = new ByteArrayInputStream(by);		ClassLoader old = null;		if (classLoader != null) {			old = Thread.currentThread().getContextClassLoader();			// 切换当前线程classloader,保证动态加载的类不会报CNF			Thread.currentThread().setContextClassLoader(classLoader);		}		HessianInput hi = new HessianInput(is);		Object obj = hi.readObject();		if (classLoader != null) {			Thread.currentThread().setContextClassLoader(old);		}		return obj;	}复制代码

所以在凡序列中,server端需要传入动态加载的classLoader。 Hessian在凡序列化时,会获取当前线程的ClassLoader,所以我们在外面修改了当前线程的classloader(这也是迭代的目标,这样做有些不稳妥)。

同时我也引入了压缩功能,这样让传输的字节更少。

2.4.TCP请求

传输的数据准备好了,也从配置文件中获取到服务的地址,接下来就要进行TCP请求了。

public static byte[] send(byte[] sendData,String host,int port,int timeout) throws UnknownHostException, IOException {		Socket socket = new Socket(host,port);		socket.setSoTimeout(timeout);				OutputStream os = socket.getOutputStream();		InputStream is = socket.getInputStream();		byte resultArray[] = null;		try {			os.write(sendData);			os.flush();			socket.shutdownOutput();			resultArray = IOUtils.toByteArray(is);		} catch (Exception e) {			e.printStackTrace();		} finally {			os.close();			is.close();			socket.close();		}		return resultArray;	}复制代码

嗯。。这个TCP请求写的还是相当简单的。以后会迭代这一块的,可能会采用netty的方式实现客户端,可以能用连接池,这个我会深入对比,选择一个好的方案,到时候在来更新博客。

2.5.数据反序列化

通过TCP请求,获取的服务端返回的字节,这时候使用Hessian凡序列就行。 因为客户端会引入服务的接口包,使用AppClassloader加载,所以在客户端无需修改当前线程的classloaer。

3.总结

对于写一个RPC框架,主要是先构造出网络传输的数据格式(协议),客户端的难点主要在TCP请求这一块把。因为用户会引入接口包,所以序列化这一块客户端比较好实现。

对于KRPC在高并发压测下,表现还有要改进的地方,我也会持续迭代更新,争取能让期用于生产环境。关于改进的点,我也会在博客中持续更新。

欢迎对RPC框架原理感兴趣的同学与我交流。https://github.com/yangzhenkun/krpc/ KRPC源码地址,欢迎star,issues.

转载地址:http://ykxqa.baihongyu.com/

你可能感兴趣的文章
实验报告一:网络侦查与网络扫描
查看>>
.net 接入微信商户企业支付API 问题总结
查看>>
防止SQL注入
查看>>
java中的动态代理(三)
查看>>
Ue4的UE_LOG
查看>>
自绘制HT For Web ComboBox下拉框组件
查看>>
基于 HTML5 WebGL 的低碳工业园区监控系统
查看>>
小机房的树CODEVS 2370
查看>>
得到一个范围的随机数函数
查看>>
js返回上一页并刷新、返回上一页、自动刷新页面
查看>>
复数类完整实现 + 四则运算符重载
查看>>
UVA 699 The Falling Leaves 数据结构
查看>>
简单搜索专题的笔记
查看>>
ASP汉字转拼音函数的方法
查看>>
MySQL判断字段值来确定是否插入新记录
查看>>
HTTP协议漫谈 --笔记
查看>>
react实现点击某个元素之外自动隐藏此元素
查看>>
load mainaccount
查看>>
iOS 应用内付费(IAP)开发步骤
查看>>
REST 在 Java 中的使用
查看>>