RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,是的程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。RPC框架实现的架构原理都是类似的,如下图所示。
Client Code:客户端调用方代码,负责发起RPC调用,为调用方提供接口
Serialization:负责对RPC调用通过网络传输的内容进行序列化和反序列化
Stub Proxy:一个代理对象,屏蔽RPC调用过程中复杂的网络处理逻辑
Transport:作为RPC框架底层的通信传输模块,一般通过Socket在客户端和服务器端进行信息交互
Server Code:服务端服务业务逻辑具体的实现
下面就开始吧。
首先编写Client Code,也就是调用方的接口
package Caller;
public interface ProductService {
public Product queryById(long id);
}
一个很简的接口,通过id进行查询商品
接下来是商品本身:
package Caller;
import java.io.Serializable;
public class Product implements Serializable {
private long id;
private String name;
private double price;
/**
* get/set方法
**/
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", price=" + price +
'}';
}
}
因为使用的是Java的原生序列化,所以需要实现Serializable接口。
接下来是客户端的实现,我把客户端的实现和代理写在一块了。
public class ProductCaller {
public static void main(String[] args) {
ProductService service = (ProductService) rpc(ProductService.class);
Product product = service.queryById(100);
System.out.println(product);
}
private static Object rpc(Class<?> clazz) {
return Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
Socket socket = null;
try {
socket = new Socket("localhost",8777);
oos = new ObjectOutputStream(socket.getOutputStream());
//发送调用方法的一些属性
oos.writeUTF(clazz.getName());
oos.writeUTF(method.getName());
oos.writeObject(method.getParameterTypes());
oos.writeObject(args);
//等待服务端的结果
ois = new ObjectInputStream(socket.getInputStream());
return ois.readObject();
}finally {
if (oos!=null){
try {
oos.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (ois!=null){
try {
ois.close();
}catch (Exception e){
e.printStackTrace();
}
}
if (socket!=null){
try {
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
});
}
}
可以看到,给服务端发送了类的全路径名,方法名,方法的参数类型,方法的实参,并等待服务端的执行结果
服务端的实现,首先是业务逻辑的实现
public class ProductServiceImpl implements ProductService{
@Override
public Product queryById(long id) {
Product product = new Product();
product.setId(id);
product.setName("张三");
product.setPrice(1.0);
return product;
}
}
其次是服务端的实现
public class ProductServer {
private static final Map<String,Class<?>> map = new HashMap<>();
static {
map.put("Caller.ProductService",ProductServiceImpl.class);
}
public static void main(String[] args) {
try {
ServerSocket serverSocket = new ServerSocket(8777);
Socket socket = serverSocket.accept();
//读取信息
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
String ClassName = ois.readUTF();
String methodName = ois.readUTF();
Class[] paramterTypes = (Class[]) ois.readObject();
Object[] paramters = (Object[]) ois.readObject();
Class<?> clazz = map.get(ClassName);
if (clazz==null)
throw new Exception("not found "+ClassName);
Method method = clazz.getMethod(methodName,paramterTypes);
Object result = method.invoke(clazz.newInstance(), paramters);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
oos.writeObject(result);
oos.close();
ois.close();
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
服务端接收客户端的请求,并进行反序列化得到相应的属性,然后使用反射执行方法并返回结果。
让我们运行一下,先启动服务端,再启动客户端,结果如下:
好了,一个简单的RPC框架以及完成。
在以上代码中有可以改进的方法:
比如使用NIO或者Netty进行通信
使用Json字符串或者一个序列化性能高的工具