前言
看了flink-rpc之后,感觉rpc框架真的是太强大了,比如netty、akka,之前基于akka写过一个能够处理千万级实时数据量的处理系统,netyy的话之前也是了解一点,但是对于rpc思想理解上总是感觉差点什么,于是决定跟着老师们,详细的学习下rpc是什么,怎么用,如何能够用好,开始一步一步的手撸吧。
站长巨人的肩膀上
https://www.bilibili.com/video/BV1qT4y1a7Es?p=11&spm_id_from=pageDriver&vd_source=2d49f382281cffe8d57c37bf2c3e9da0
RPC学习——原理图
RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。
当程序部署在多台机器上的时候,使用rpc思想调用远程方法就像调用本地程序一样,把中间的网络和io操作都已经封装起来了,应用程序员只需要写业务逻辑即可。
code
创建一个Product实体类
/**
* @author happy
* @since 2022/8/28
*/
public class Product {
private Integer id;
private String name;
public Product(Integer id, String name) {
this.id = id;
this.name = name;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
import rpc.common.IProductService;
/**
* @author happy
* @since 2022/8/28
*/
public class Client {
public static void main(String[] args) throws Exception {
IProductService service = (IProductService) Stub.getStub(IProductService.class);
System.out.println(service.findProductById(321));
}
}
/**
* @author happy
* @since 2022/8/28
*/
public interface IProductService {
Product findProductById(Integer id);
}
import rpc.common.IProductService;
import rpc.common.IUserService;
import rpc.common.Product;
import rpc.entity.User;
/**
* @author happy
* @since 2022/8/28
*/
public class ProductServiceImpl implements IProductService {
@Override
public Product findProductById(Integer id) {
return new Product(id,"white");
}
}
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
/**
* @author happy
* @since 2022/8/28
*/
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(8888);
while (running) {
Socket accept = serverSocket.accept();
process(accept);
accept.close();
}
serverSocket.close();
}
private static void process(Socket s) throws Exception {
InputStream in = s.getInputStream();
OutputStream outputStream = s.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(in);
String clazzName = ois.readUTF();
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[]) ois.readObject();
Object[] args = (Object[]) ois.readObject();
Class clazz = null;
// 从服务注册表找到具体的类
clazz = ProductServiceImpl.class;
Method method = clazz.getMethod(methodName, parameterTypes);
Object o = (Object) method.invoke(clazz.newInstance(), args);
ObjectOutputStream oos = new ObjectOutputStream(outputStream);
oos.writeObject(o);
oos.flush();
}
}
package rpc.rpc07;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
/**
* 但是这里仅仅实现了findByUserId的方法代理,如果要实现其它方法的代理该怎么做呢?
* <p>
* 这里就要从协议层做出改进
* <p>
* 服务器端也要做出对应处理
*
* @author happy
* @since 2022/8/28
*/
public class Stub {
public static Object getStub(Class clazz) {
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 8888);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
// 方法名
String clazzName = clazz.getName();
String methodName = method.getName();
// 参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(clazzName);
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object o = ois.readObject();
oos.close();
socket.close();
return o;
}
};
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, h);
System.out.println(o.getClass().getName());
System.out.println(o.getClass().getInterfaces()[0]);
return o;
}
}
在客户端通过Stub类代理了远程操作,封装了网络相关的操作,让你操作远程如同调用本地api一样。
本篇文章只是一个开胃菜,能够体会到rpc思想的妙处,距离企业级的rpc框架差之甚远。