就靠这几段代码,带你玩转rpc通信协议,不信你学不明白
RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。
2.基本概念
RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务
本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。
远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?
今天,我们就通过一个实例代码进行演示,一步步的查看,rpc的通信是如何进行的,有兴趣的朋友可以把代码进行实现,自己debug一下,查看每一步的传参
1
package com.mashibing.rpc.common;
import java.io.Serializable;
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public String getName() {
return name;
}
public void setId(Integer id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
package com.mashibing.rpc.common;
public interface IUserService {
public User findUserById(Integer id);
}
package com.mashibing.rpc01;
import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;
public class UserServiceImpl implements IUserService {
@Override
public User findUserById(Integer id) {
return new User(id, "Alice");
}
}
package com.mashibing.rpc01;
import com.mashibing.rpc.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class Client {
public static void main(String[] args) throws Exception {
Socket s = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(123);
s.getOutputStream().write(baos.toByteArray());
s.getOutputStream().flush();
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id, name);
System.out.println(user);
dos.close();
s.close();
}
}
2
package com.mashibing.rpc02;
import com.mashibing.rpc.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;
public class Stub {
public User findUserById(Integer id) throws Exception {
Socket s = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(123);
s.getOutputStream().write(baos.toByteArray());
s.getOutputStream().flush();
DataInputStream dis = new DataInputStream(s.getInputStream());
int receivedId = dis.readInt();
String name = dis.readUTF();
User user = new User(id, name);
dos.close();
s.close();
return user;
}
}
3
package com.mashibing.rpc03;
import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
/**
* 而且Client的调用显得不是很合理(Stub里只有findById的代码),如果有个findByName的新方法,那么就又得重新改进
* 下面这种写法解决了方法增加的问题
*/
public class Stub {
public static IUserService getStub() {
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket s = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.writeInt(123);
s.getOutputStream().write(baos.toByteArray());
s.getOutputStream().flush();
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id, name);
dos.close();
s.close();
return user;
}
};
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[] {IUserService.class}, h);
return (IUserService)o;
}
}
4
package com.mashibing.rpc04;
import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
/**
* 但是这里仅仅实现了findByUserId的方法代理,如果要实现其他方法的代理该怎么做呢?
* 这里就要从协议层做出改进
*
* 服务器端也要做出对应处理
*/
public class Stub {
public static IUserService getStub() {
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket s = new Socket("127.0.0.1", 8888);
ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());
String methodName = method.getName();
Class[] parametersTypes = method.getParameterTypes();
oos.writeUTF(methodName);
oos.writeObject(parametersTypes);
oos.writeObject(args);
oos.flush();
DataInputStream dis = new DataInputStream(s.getInputStream());
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id, name);
oos.close();
s.close();
return user;
}
};
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[] {IUserService.class}, h);
return (IUserService)o;
}
}
package com.mashibing.rpc04;
import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;
import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888);
while (running) {
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws Exception {
InputStream in = s.getInputStream();
OutputStream out = s.getOutputStream();
ObjectInputStream oos = new ObjectInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
String methodName = oos.readUTF();
Class[] parameterTypes = (Class[])oos.readObject();
Object[] args = (Object[])oos.readObject();
IUserService service = new UserServiceImpl();
Method method = service.getClass().getMethod(methodName, parameterTypes);
User user = (User)method.invoke(service, args);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
5
返回值用Object封装,支持任意类型
就这样,直到最后的server端获取到数据,完成了一个数据传输,不知道大家能不能理解,最后,纸上得来终觉浅,我建议大家还是能够可以自己实现一下。
关注我,一个思维跳脱并且有点洒脱的程序员,谢谢
最近在整理我这几年工作以及生活中对我帮助较大的视频以及资料,有需要的,可以去我git仓库中自取
链接在这里:https://gitee.com/biwangsheng/personal.git
本文分享自微信公众号 - Java架构师联盟(msbxq2019)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
谁说有序链表不能进行二分查找?!
关注公众号“彤哥读源码”,解锁更多源码、基础、架构知识! 前言 本文收录于专辑:http://dwz.win/HjK,点击解锁更多数据结构与算法的知识。 你好,我是彤哥。 上一节,我们一起学习了关于哈希的一切,特别是哈希表的进化过程,相信通过上一节的学习,你一定可以从头到尾完整地给面试官讲讲哈希表是如何发展到如今这一步的。 但是,难道HashMap的终极形态只能通过“数组+链表+红黑树”的形式实现吗?有没有可替代方案?为什么Java没有使用你说的这种替代方案呢? 本节,我们就来学习另外一种数据结构——跳表,关于跳表的内容,我将分成两节完成,第一节介绍跳表的演进过程,第二节代码实现跳表,并改写HashMap。 好了,让我们先进入跳表第一小节的学习。 有序数组 大家都知道数组是可以支持随机访问的,也就是通过下标可以快速地定位到元素,时间复杂度是O(1)。 那么,这个随机访问的特性除了根据下标查找元素,还具有哪些用处呢? 试想,如果一个数组是有序的,我要查找某个指定的元素,如何才能做到最快速地查找出来呢? 简单的方法,从头开始遍历整个数组,遇到了要查找的元素就返回,比如,查找8这个元素,要走...
- 下一篇
异步神器CompletableFuture
介绍 上个礼拜我们线上有个接口比较慢,这个接口在刚开始响应时间是正常的。但随着数据量的增多,响应时间变慢了。 这个接口里面顺序调用了2个服务,且2个服务之间没有数据依赖。我就用CompletableFuture把调用2个服务的过程异步化了一下,响应时间也基本上缩短为原来的一半,问题解决。 正好上次分享了函数式接口和Stream的使用,这次就分享一下CompletableFuture,里面也用到了大量的函数式接口 想方便的异步执行任务,就必须放到单独的线程中。继承Thread类,实现Runnable都不能拿到任务的执行结果,这时就不得不提创建线程的另一种方式了,实现Callable接口。 @FunctionalInterfacepublicinterfaceCallable<V>{Vcall()throwsException;} Callable接口一般配合ExecutorService来使用 //ExecutorService.java<T>Future<T>submit(Callable<T>task); ExecutorService...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- CentOS8编译安装MySQL8.0.19
- Linux系统CentOS6、CentOS7手动修改IP地址
- 设置Eclipse缩进为4个空格,增强代码规范
- Windows10,CentOS7,CentOS8安装Nodejs环境
- CentOS6,CentOS7官方镜像安装Oracle11G
- CentOS7,CentOS8安装Elasticsearch6.8.6
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合Redis,开启缓存,提高访问速度
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池