最近在看dubbo的源码,所以就参考着这本书籍作为辅助(这个诣极总是让我看成了极诣,嗯,极诣狂战士):
dubbo在3.0版本就有三十万行代码,光看个核心功能的代码就快折磨死人了,为了加强理解,我觉得动手实现一个rpc框架倒是个非常不错的方法。
本文主要会围绕RPC的基本功能来展开。主要是对RPC有个认知,至于SPI、注册中心、负载均衡、netty传递信息等的实现,我会放在下一篇文章中进行讲解。
市面上有很多RPC框架,虽然种类比较多,但是他们所围绕的中心思想是一样的。我们看下书中是这么介绍的:
这张图就很好的展示了RPC的调用过程:把客户端需要调用的信息传递到服务端,每次服务调用的一些公用的信息包括服务调用接口、方法名、方法参数类型和方法参数值等,在传递方法参数值时 需要先序列化对象并经过网络传输到服务端,在服务端需要按照客户端序列化顺序再做一次反 序列化来读取信息,然后拼装成请求对象进行服务反射调用,最终将调用结果再传给客户端。
所以总结起来呢,整个RPC调用过程可以分为四个部分:
服务提供者:服务端,他需要将可以被引用的服务注册到注册中心以供消费者使用。
服务消费者:即客户端,他可以从注册中心去获取自己需要的服务。这里需要注意的是,提供者与消费者只是个相对的概念,一个服务既可以当提供者,也可以当消费者。
注册中心:保存提供者提供的服务,像dubbo中默认的注册中心是zookeeper,但是它也支持别的中间件作为注册中心,比如像redis、nacos等。在下一篇中我会以zookeeper为注册中心来进行示例。
其实要实现基本的RPC的话,以上这些就够了,我们就直接来看实现吧,为了能更清楚整个调用有哪些部分,我对整个调用过程进行了分块:
如果你想看到RPC调用结果的话,也可以不用像我这样进行分模块,只需要复制我接下来的代码就OK了。
api模块:存放接口的模块,便于提供端provider进行接口的实现。
public interface DemoService {
String sendAndGetMessage(String message);
}
Common:主要存放这个过程中需要的一些实体类:
Invocation:因为我们客户端需要将信息发送到服务端,所以需要对信息进行一些封装:
public class Invocation implements Serializable {
//因为要进行网络传输,所以需要进行序列化。
//接口全限定名
private String className;
//方法名
private String methodName;
//方法参数类型
private Class<?>[] paramTypes;
//方法参数
private Object[] params;
/*getter and setter*/
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 Class<?>[] getParamTypes() {
return paramTypes;
}
public void setParamTypes(Class<?>[] paramTypes) {
this.paramTypes = paramTypes;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
}
URL:因为要进行网络传输,所以需要保存请求的ip、端口:
public class URL implements Serializable {
private String hostname;
private Integer port;
public URL(String hostname, Integer port) {
this.hostname = hostname;
this.port = port;
}
public String getHostname() {
return hostname;
}
public void setHostname(String hostname) {
this.hostname = hostname;
}
public Integer getPort() {
return port;
}
public void setPort(Integer port) {
this.port = port;
}
}
定义返回信息:
public class RpcResponse implements Serializable {
//异常
private Throwable error;
//调用结果
private Object result;
/*getter and setter*/
public Throwable getError() {
return error;
}
public void setError(Throwable error) {
this.error = error;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
}
Framework:整个传递过程的功能类。
RpcConsumer:消费者的处理逻辑
public class RpcConsumer {
public Object execute(Invocation invocation, String host, int port) throws Throwable {
Socket server = new Socket(host, port);
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
try{
//将请求写到连接服务端socket的输出流
oos = new ObjectOutputStream(server.getOutputStream());
oos.writeObject(invocation);
oos.flush();
//读取输入流的内容
ois = new ObjectInputStream(server.getInputStream());
Object res = ois.readObject();
RpcResponse response = (RpcResponse) res;
return response.getResult();
} catch (ClassNotFoundException e) {
e.printStackTrace();
throw e;
} finally {
if (ois!=null)ois.close();
if (oos != null) oos.close();
if (server != null) server.close();
}
}
}
RpcProvider提供者的处理逻辑:
public class RpcProvider {
public void start(int port, Map<String, Object> services){
ServerSocket server = null;
try {
//1、创建socket连接
server = new ServerSocket(port);
//2、获取所有服务类
// Map services = getService(clazz);
//3、创建线程池
Executor executor = new ThreadPoolExecutor(5, 10, 10, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>() {
});
while(true){
//4、获取客户端连接
Socket client = server.accept();
//5、将服务端被调用的服务放到线程池中异步执行
RpcProviderHandler service = new RpcProviderHandler(client,services);
executor.execute(service);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (server!=null){
try {
server.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
public class RpcProviderHandler implements Runnable {
private Socket clientSocket;
private Map<String,Object> serviceMap;
public RpcProviderHandler(Socket client, Map<String, Object> services) {
this.clientSocket = client;
this.serviceMap = services;
}
@Override
public void run() {
ObjectInputStream ois = null;
ObjectOutputStream oos = null;
RpcResponse response = new RpcResponse();
try{
ois = new ObjectInputStream(clientSocket.getInputStream());
oos = new ObjectOutputStream(clientSocket.getOutputStream());
//反序列化
Object object = ois.readObject();
Invocation invocation = invocation = (Invocation) object;
//查找并执行服务
Class<?> clazz = (Class<?>) serviceMap.get(invocation.getClassName());
Method method = clazz.getMethod(invocation.getMethodName(),invocation.getParamTypes());
Object result = method.invoke(clazz.newInstance(),invocation.getParams());
response.setResult(result);
oos.writeObject(response);
oos.flush();
} catch (Exception e) {
if (oos != null) {
response.setError(e);
try {
oos.writeObject(response);
oos.flush();
} catch (IOException e1) {
e1.printStackTrace();
}
}
} finally {
try {
if (ois!=null) ois.close();
if (oos != null) oos.close();
if (clientSocket != null) clientSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
一个客户端代理类,可以不用在代码中写死方法名、参数类型等:
public class RpcProviderProxy {
Logger log= LoggerFactory.getLogger(RpcProviderProxy.class);
private URL url;
public RpcProviderProxy(URL url) {
this.url=url;
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<T> clazz){
return (T) Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
log.info("可以做一些执行方法的前置处理");
Invocation invocation = new Invocation();
invocation.setClassName(method.getDeclaringClass().getName());
invocation.setMethodName(method.getName());
invocation.setParamTypes(method.getParameterTypes());
invocation.setParams(args);
Object result = new RpcConsumer().execute(invocation, url.getHostname(), url.getPort());
//功能增强,比如记录流水信息
log.info("反射中的方法执行完毕,返回结果为:"+result);
log.info("可以做一些执行方法的后置处理");
return result;
}
});
}
}
OK,这时我们只需要再来个客户端测试类、服务端测试类即可:
public class ConsumerMain {
public static void main(String[] args) {
URL url=new URL("127.0.0.1", 7777);
RpcProviderProxy clientProxy = new RpcProviderProxy(url);
//代理类
DemoService proxy = clientProxy.getProxy(DemoService.class);
System.out.println(proxy.sendAndGetMessage("测试发送信息"));
}
}
public class ProviderMain {
public static void main(String[] args) {
Map<String,Object> services = new HashMap<>();
services.put(DemoService.class.getName(), DemoServiceImpl.class);
RpcProvider server= new RpcProvider();
server.start(7777,services);
}
}
public class DemoServiceImpl implements DemoService {
Logger log=LoggerFactory.getLogger(DemoServiceImpl.class);
@Override
public String sendAndGetMessage(String message) {
log.info("执行了客户端的方法,参数为:"+message);
return message;
}
}
这样,我们就完成了一个简单的RPC了,run一下看下效果怎么样,记得先启动服务端(ProviderMain),再启动客户端(ConsumerMain)
Ok,完美。这个例子直接复制上面的代码即可运行。至于SPI、注册中心等,我们在下一篇中再进行实现,可能又要和之前解析Springboot启动流程那样来个万字长文了。