一、什么是RPC协议?

全称:远程过程调度协议

效果:使消费者向调用本地方法一样调用远程服务方法,对使用者透明

目前常用:Dubbo、Thirft、Sofa....

功能:

  • 建立远程通信(socket)TCP/UDP
  • 数据传递
  • 序列化和反序列化(XML/json/Protobuf/avro/kyro/hessian)

流程图:

二、Demo思路

  1. 首先我们需要搭建两个项目,一个作为服务端提供服务,另一个作为客户端来调用服务端的接口方法。
  2. 因为客户端需要调用服务端的接口,所以我们需要客户端依赖这个通信服务所需的公共资源文件,所以服务端分为两部分,一部分为api资源,一部分为提供服务的方法。
  3. 为客户端与服务端建立连接,服务端等待请求,客户端等待响应结果
  4. 最终测试

三、Demo流程

1.首先建立两个maven项目,快速建立即可,一个命名为rpc-client作为客户端,另一个命名为rpc-server作为服务端。在rpc-server下建立两个子模块,一个命名为server-api,作为公共资源,另一个命名server-provider作为服务提供程序。





2.在server下,新建一个测试接口,我这里命名为IHelloServer,让客户端调用此接口实现方法,该接口方法如下:



3.将api的依赖添加到rpc-client中,为了能让其调用公共资源。当然,服务提供模块也需要,所以也需要依赖进去。因为是本地小Demo测试,所以就直接Install本地,进行依赖添加了。



4.在server-provider中新建impl类,实现IhelloService接口,重写其中方法



5.这时,我们就可以在客户端使用接口对象了,但是因为是接口,我们无法对它进行实例化,那样就成为本地调用了,所以我们需要进行动态代理。

在dubbo中,是使用注解进行动态代理,把信息注入到对象中:



所以我们需要写一个动态代理方法,来为对象进行实例化操作:

常见的动态代理方式有很多,如:jdk、cglib、javassist、asm。。等

(当然,作为新手小白,我还不太了解,需要后续学习)

新建RpcClientProxy类,类中方法如下:(动态代理标注写法)



这里实现了InvocationHandler接口,其中的invoke方法,是在使用代理对象中的方法时,就会被调用

然后我们在客户端mian方法中实例化动态代理类(指上面的类),执行其中的方法,将IhelloService动态代理,调用IHelloService的sayhello方法,控制台得到的结果是:test,因为我们返回的就是test,并且还没进行远程连接。

6.之后需要进行网络间的通信了,我们新写一个类,来继承InvocationHandler接口,实现invoke方法,在方法中,主要工作有已下三步:

  1. 组装参数
  2. 序列化
  3. 进行网络传输

7.我们需要对参数进行封装,这样方便进行参数的传输,而这个类,因为是公共资源,所以我们建立在server-api下:

实现序列化接口,因为要数据传输,写入set、get方法

8.在客户端的invoke方法中,组装参数,封装到实体,等待传输过去就行了



新写通信类,用来向服务方发送请求参数,只需要写入服务地址和端口,把数据传输过去即可



9.新建发布类,写一个发布方法,当然位置在server-provider里,由于是小演示,所以用BIO即可

BIO(阻塞IO)---当没有客户端连接过来时,accept会阻塞(当前进程),没有数据传输,IO也会阻塞,以至于一次只能处理一个IO,服务端的吞吐量会很低

NIO,多路复用,一个线程管理N个连接

在这里我们用socket进行通信,方法传入两个参,一个是服务器地址,一个是端口,死循环不断监听



10.由于BIO单线程吞吐量太低,所以我们用线程池进行优化,当我们获取到socket之后,放到线程里去执行,这样可以提高一些效率

新建类,实现runable接口,在run方法中,进行反序列化,获取传来的参数,然后通过反射,去调用我们IHelloServiceImpl中的方法,再把得到的结果写回去,这样就完成了网络通信。



11.最后,provider模块主方法,我们新建实现类,新建发布类,设定端口号,运行即可。我们的客户端主方法,新建代理类,写入端口参数,调用方法,输出结果即可。





最后控制台输出的结果:



通信成功!!!(后附代码)

四、总结

个人认为rpc框架通信的实现逻辑简单来说就是这个样子,只是不同的框架,针对某些特定的功能进行了加强,对某些操作进行了封装,使得使用者用起来更加方便。

Demo总体流程如下:

1.客户端与服务提供端依赖公共资源文件

2.客户端需要对对象进行代理

3.服务端与客户端进行远程通信

4.客户端将参数发送给服务端,服务端读取参数,将结果返回

五、代码

rpc-client模块下:

app类:

public class App
{
public static void main( String[] args )
{
RpcClientProxy rpcClientProxy = new RpcClientProxy();
IHelloService iHelloService = rpcClientProxy.clientProxy(IHelloService.class,"localhost",8080);
String rs = iHelloService.sayHello("test");
System.out.println( rs);
}
}

MyInvocationHandler类:

public class MyInvocationHandler implements InvocationHandler {
private String host;
private int port;
public MyInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//开始执行代理,并且触发远程调用
//1.组装参数
//2.序列化
//3.进行网络传输
RpcRequest request = new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParams(args);
request.setTypes(method.getParameterTypes());
//下面开始进行数据传输
RpcTransPort rpcTransPort = new RpcTransPort(host,port);
return rpcTransPort.send(request);
}
}

RpcClientProxy类:

public class RpcClientProxy {
//jdk、cglib、javassist、asm
/* public <T> T clientProxy(final Class<T> interfaceCls){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//如果调用interfaceCls的方法。则会被动态代理执行到invole方法
return "test";
}
});*/
public <T> T clientProxy(final Class<T> interfaceCls,String service,int port){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls},new MyInvocationHandler(service,port));
}
}

RpcTransPort类:

public class RpcTransPort {
private String host;
private int port; public RpcTransPort(String host, int port) {
this.host = host;
this.port = port;
}
public Object send(RpcRequest rpcRequest){
try(Socket socket = new Socket(host,port);
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){
outputStream.writeObject(rpcRequest);
outputStream.flush();
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
return inputStream.readObject();
} catch (Exception e) {
e.printStackTrace();
}finally {
//TODO close IO
}
return null;
}
}

rpc-server-api模块下:

IHelloService类:

public interface IHelloService {
String sayHello(String txt);
}

RpcRequest类:

public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Object[] params;
private Class[] types; public String getClassName() {
return className;
} public void setClassName(String className) {
this.className = className;
} public String getMethodName() {
return methodName;
} public void setMethodName(String methodName) {
this.methodName = methodName;
} public Object[] getParams() {
return params;
} public void setParams(Object[] params) {
this.params = params;
} public Class[] getTypes() {
return types;
} public void setTypes(Class[] types) {
this.types = types;
}
}

rpc-server-provider模块下:

App类:

public class App
{
public static void main( String[] args )
{
IHelloService iHelloService = new IHelloServiceImpl();
RpcProxyServer rpcProxyServer = new RpcProxyServer();
rpcProxyServer.publisher(iHelloService,8080); }
}

IHelloServiceImpl类:

public class IHelloServiceImpl implements IHelloService{
@Override
public String sayHello(String txt) {
return "receive msg :"+txt;
}
}

ProcessHandlerThread类:

public class ProcessHandlerThread implements Runnable {
Socket socket;
Object service; public ProcessHandlerThread(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
//数据通信的处理
try(ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream outputStream = new ObjectOutputStream(socket.getOutputStream())){
RpcRequest request = (RpcRequest)inputStream.readObject();//获得客户端传来的数据,反序列化
Object object = invoke(request);
outputStream.writeObject(object);
outputStream.flush();
}catch (Exception e){
e.printStackTrace();;
}
}
private Object invoke(RpcRequest request) throws Exception {
Class clazz = Class.forName(request.getClassName());
Method method = clazz.getMethod(request.getMethodName(),request.getTypes());
return method.invoke(service,request.getParams());
}
}

RpcProxyServer类:

public class RpcProxyServer {
/**
*
* @param service 发布到具体的服务器
* @param port 发布的端口号
*/
public void publisher(Object service,int port){
//BIO(阻塞IO)---当没有客户端连接过来时,accept会阻塞(当前进程),没有数据传输,IO也会阻塞,以至于一次只能处理一个IO,服务端的吞吐量会很低
//NIO,多路复用,一个线程管理N个连接
ExecutorService executorService = Executors.newFixedThreadPool(10);//创建线程池,防止IO阻塞,提高服务端并行处理的连接数
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);
while(true) {//死循环,不断的去监听
Socket socket = serverSocket.accept();//等待客户端连接
//----上面为建立通信,下面为数据传输----
executorService.execute(new ProcessHandlerThread(socket,service));//一个socket对象,代表一个客户端的连接
//socket.getInputStream();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}

最后,新手小白一枚,望各位大佬点评

RPC框架学习+小Demo实例的更多相关文章

  1. Android学习小Demo(19)利用Loader来实时接收短信

    之前写过一篇文章<Android学习小Demo(13)Android中关于ContentObserver的使用>,在里面利用ContentOberver去监測短信URI内容的变化.我们先来 ...

  2. 移动端页面弹幕小Demo实例说明

    代码地址如下:http://www.demodashi.com/demo/11595.html 弹幕小Demo实例地址,点击看效果 写在前面:尝试做了一下弹幕的实例,欢迎提出并指正问题 问题说明: D ...

  3. 简易RPC框架-学习使用

    *:first-child { margin-top: 0 !important; } body>*:last-child { margin-bottom: 0 !important; } /* ...

  4. Android学习小Demo一个显示行线的自定义EditText

    今天在处理一个EditText的时候,想着把EditText做成像一本作业本上的纸一样,每一行都可以由线条隔开,具体效果如下: 1)最开始的思路 一开始的想法是很简单的,找出每一行的高度,然后一行一行 ...

  5. Android学习小Demo(20)关于Fragment的应用

    Android在3.0之后引入了Fragment的概念,我推測其想法可能仅仅是想更好地兼容大屏幕或者平板的开发,由于大屏幕能够展示很多其它的内容,而内容一多,逻辑有可能就乱,而利用Fragment,则 ...

  6. QT学习小demo之LightMD(MarkDown编辑器)

    很早之前就有了写一个类似Windows记事本的想法,加上最近也刚好在学编译原理,所以就想把两者结合起来,于是就打算结合MarkDown,开发一款MarkDown编辑器. 不过由于我之前一直使用的是Ja ...

  7. RPC框架学习总结

    1.RPC是一种技术框架的称呼,不是某种具体协议,不局限于某种协议,RPC顾名思义就是远程过程调用,其核心思想是,RPC客户端调用远程服务器上的接口完成过程调用,远程服务器把结果返回. 2.RPC的最 ...

  8. Android学习小Demo(21)ListView的联动选择

    在日常的App开发中,尤其是在开发生活服务的应用上,非常多时候,我们会须要联动地展现省市区的数据等,需求大概例如以下: 1)展现全部省份 2)当点击某省份的时候,在二级菜单上展现此省份以下所属的城市列 ...

  9. webpack+vue+vueRouter模块化构建小demo实例超详细步骤(附截图、代码、入门篇)

    说明:本demo使用yarn代替npm指令来下载一系列依赖,有详细指令说明:使用WebStorm下Terminal来输入指令: >开始(确认已经安装node环境和yarn包管理工具) 1.新建项 ...

随机推荐

  1. PyQt(Python+Qt)学习随笔:Qt中的部分类型QString、QList和指针、引用在PyQt中的实现方式

    老猿Python博文目录 老猿Python博客地址 在我们查阅Qt的文档资料时,可以看到Qt中的链表使用的是QList,字符串使用的是QString,但老猿在测试时发现这两个类型PyQt不支持,无法找 ...

  2. PyQt(Python+Qt)学习随笔:Designer中PushButton按钮flat属性

    flat属性用于确认按钮边框是否凸起,如果为False则凸起,如果为True则边框与背景是平坦的. 默认值为False,如果设置为True,则除非按下按钮,否则大多数样式都不会绘制按钮背景.通过使用s ...

  3. 性能测试学习之路 (四)jmeter 脚本开发实战(JDBC &JMS &接口脚本 & 轻量级接口自动化测试框架)

    1.业务级脚本开发 登录脚本->思路:在线程组下新建两个HTTP请求,一个是完成访问登录页,一个是完成登录的数据提交.   步骤如下: 1) 访问登录页 2) 提交登录数据的HTTP PS:对于 ...

  4. Python中错误之 TypeError: object() takes no parameters、TypeError: this constructor takes no arguments

    TypeError: object() takes no parameters TypeError: this constructor takes no arguments 如下是学习python类时 ...

  5. 巨经典论文!推荐系统经典模型Wide & Deep

    今天我们剖析的也是推荐领域的经典论文,叫做Wide & Deep Learning for Recommender Systems.它发表于2016年,作者是Google App Store的 ...

  6. 题解-FJOI2014 树的重心

    FJOI2014 树的重心 \(Q\) 组测试数据.给一棵树大小为 \(n\),求有多少个子树与其重心相同.重心可能有多个. 数据范围:\(1\le Q\le 50\),\(1\le n\le 200 ...

  7. js实现元素范围内拖动

    元素拖拽,网上一堆的实现,其中很多是原生js写的,都不够简洁,甚至运行后看不到效果. 于是乎,安静地想了下,拖动元素貌似就是一个滑动事件的监听处理,具体操作如下: 1.一个外层DIV,或者直接用根节点 ...

  8. Mac下安装appium+python+Android sdk 环境完整流程

    安装大纲:1,安装jdk (jdk1.8及以上版本都可以,尽量不要用最新可能会不兼容) 2,安装android-sdk (mac版本的android-sdk) 3,mumu模拟器 (随便找的一个) 4 ...

  9. .Net Core Excel导入导出神器Npoi.Mapper

    前言 我们在日常开发中对Excel的操作可能会比较频繁,好多功能都会涉及到Excel的操作.在.Net Core中大家可能使用Npoi比较多,这款软件功能也十分强大,而且接近原始编程.但是直接使用Np ...

  10. 精尽Spring MVC源码分析 - WebApplicationContext 容器的初始化

    该系列文档是本人在学习 Spring MVC 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释 Spring MVC 源码分析 GitHub 地址 进行阅读 Spring 版本:5.2. ...