一、基本概念
RPC的全称是Remote Procedure Call,它是一种进程间的通信方式。允许像调用本地服务一样调用远程服务。
可以将RPC框架整体分为三部分:客户端、服务端、注册中心
-
客户端:
1、引用服务端暴露出来的接口;
2、与注册中心连接,向注册中心发送请求,获取服务地址信息;
3、创建服务接口代理,并调用,最后获取返回的结果。 -
服务端:
1、实现对外暴露的服务接口;
2、与注册中心连接,将服务端的接口注册到注册中心;
3、处理RPC请求,并将结果返回。 -
注册中心:
1、服务端注册服务;
2、客户端发现服务。
二、技术简介
RPC的核心就是能够让本地应用简单、高效地调用远程服务,一般从以下几方面考虑:
(1) 远程服务代理:
本地调用的方法(服务)实质是远程方法的本地代理,因此会需要一个远程服务代理对象。在Java中,我们可以使用JDK原生的动态代理机制,也可以使用一些开源字节码工具框架 如:CgLib、Javassist等。
(2) 通信模型:
客户端与服务器之间的通信方式。Java中一般基于BIO或NIO。
(3) 服务定位:
通过IP与端口确定服务器后,再通过接口名及方法名、参数等确定具体的服务。
(4) 参数序列化:
将接口名、方法名、方法参数类型等要进行网络传输的数据,需要转换成二进制传输,这里可能需要不同的序列化技术方案。如:Protostuff,java原生序列化等。
三、最简单的RPC实现
下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理、反射、Java原生的序列化。服务端使用一个静态变量来存储注册的服务,客户端直接调用。
1、服务端
HelloService.java
package com.wzl.rpc.service;
public interface HelloService {
String sayHello(String name);
}
HelloServiceImpl.java
package com.wzl.rpc.service.impl;
import com.wzl.rpc.service.HelloService;
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String name) {
return "hello " + name;
}
}
RpcProvider.java
package com.wzl.rpc.provider;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Arrays;
import java.util.List;
public class RpcProvider {
/**
* 存储注册的服务列表
*/
private static List<Object> serviceList;
/**
* 发布rpc服务
*
* @param port
* @param services
* @throws Exception
*/
public static void export(int port, Object... services) throws Exception {
serviceList = Arrays.asList(services);
ServerSocket server = new ServerSocket(port);
System.out.println("server listen on port:" + port);
while (true) {
//阻塞等待连接请求
Socket client = server.accept();
System.out.println("receive client connect; localPort:" + client.getPort());
//每一个请求,启动一个线程单独处理
new Thread(new RpcServerThread(client, serviceList)).start();
}
}
}
RpcServerThread.java
package com.wzl.rpc.provider;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
import java.util.List;
public class RpcServerThread implements Runnable {
private Socket client = null;
private List<Object> serviceList = null;
public RpcServerThread(Socket client, List<Object> service) {
this.client = client;
this.serviceList = service;
}
@Override
public void run() {
ObjectInputStream input = null;
ObjectOutputStream output = null;
try {
input = new ObjectInputStream(client.getInputStream());
output = new ObjectOutputStream(client.getOutputStream());
// 读取客户端要访问那个service
Class serviceClass = (Class) input.readObject();
// 找到该服务类
Object obj = findService(serviceClass);
if (obj == null) {
output.writeObject(serviceClass.getName() + "服务不存在!");
return;
}
//利用反射调用该类和方法,返回结果
try {
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
Method method = obj.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(obj, arguments);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
client.close();
input.close();
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private Object findService(Class serviceClass) {
for (Object obj : serviceList) {
boolean isFather = serviceClass.isAssignableFrom(obj.getClass());
if (isFather) {
return obj;
}
}
return null;
}
}
RpcStart.java
package com.wzl.rpc.start;
import com.wzl.rpc.provider.RpcProvider;
import com.wzl.rpc.service.HelloService;
import com.wzl.rpc.service.impl.HelloServiceImpl;
public class RpcStart {
public static void main(String[] args) throws Exception {
HelloService helloService = new HelloServiceImpl();
//发布服务,监听在8026端口
RpcProvider.export(8026, helloService);
}
}
运行结果
2、客户端
HelloService.java
package com.wzl.rpc.service;
public interface HelloService {
String sayHello(String name);
}
RpcConsumer.java
package com.wzl.rpc.consumer;
import java.lang.reflect.Proxy;
public class RpcConsumer {
public static <T> T getService(String ip, int port, Class<T> clazz) {
ProxyHandler proxyHandler = new ProxyHandler(ip, port);
return (T) Proxy.newProxyInstance(RpcConsumer.class.getClassLoader(), new Class<?>[]{clazz}, proxyHandler);
}
}
ProxyHandler.java
package com.wzl.rpc.consumer;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.net.Socket;
public class ProxyHandler implements InvocationHandler {
private String ip;
private int port;
public ProxyHandler(String ip, int port) {
this.ip = ip;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket(this.ip, this.port);
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
output.writeObject(proxy.getClass().getInterfaces()[0]);//类
output.writeUTF(method.getName());//方法名
output.writeObject(method.getParameterTypes());//参数类型
output.writeObject(args);//参数
output.flush();
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
socket.shutdownOutput();
}
}
}
RpcTest.java
package com.wzl.rpc.start;
import com.wzl.rpc.consumer.RpcConsumer;
import com.wzl.rpc.service.HelloService;
/**
* RPC客户端测试类
*/
public class RpcTest {
public static void main(String[] args) {
HelloService helloService = RpcConsumer.getService("127.0.0.1", 8026, HelloService.class);
String result = helloService.sayHello("张三丰");
System.out.println(result);
}
}
运行结果
四、开源知名RPC框架
阿里巴巴 Dubbo:https://github.com/alibaba/dubbo
新浪微博 Motan:https://github.com/weibocom/motan
gRPC:https://github.com/grpc/grpc
rpcx :https://github.com/smallnest/rpcx
Apache Thrift :https://thrift.apache.org/