什么是RPC
RPC(Remote Procedure Call)远程过程调用,简单来说是一个服务请求另一个服务提供的数据。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。
大概的流程图
代码实现
1. 新建rpc_server模块,在server模块下面创建server_api和server_provider
api提供一些公共的方法,provider主要是服务端的一些处理逻辑
2. 编写api模块
添加一个公共方法接口和一个RPC请求参数对象
package com.xjp.rpc;
/**
* @Description
* @Author XJP
* @Date 2020-04-24 21:53
*/
public interface IHelloServer {
String sayHello(String s);
}
package com.xjp.rpc;
import java.io.Serializable;
import java.util.Arrays;
/**
* @Description
* @Author XJP
* @Date 2020-04-25 9:29
*/
public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Object[] parameters;
private Class[] types;
//添加get和set,toString方法
}
4. 编写provider
1. 实现IHelloServer接口
加入api的pom依赖
<dependency>
<groupId>org.example</groupId>
<artifactId>rpc_server_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
package com.xjp.impl;
import com.xjp.rpc.IHelloServer;
/**
* @Description
* @Author XJP
* @Date 2020-04-24 21:55
*/
public class HelloServerImpl implements IHelloServer {
@Override
public String sayHello(String s) {
System.out.println("Rpc Start");
return "hello: " + s;
}
}
2. rpc服务端代理
创建RpcProxyServer 类,通过一个线程池,通过线程池异步处理请求
package com.xjp;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @Description
* @Author XJP
* @Date 2020-04-25 9:05
*/
public class RpcProxyServer {
public void publisher(Object service, int port){
ExecutorService executorService = Executors.newFixedThreadPool(10);
ServerSocket serverSocket = null;
try {
//监听端口
serverSocket = new ServerSocket(8080);
//收取客户端请求,没有请求的话处于阻塞状态
while (true) {
Socket socket = serverSocket.accept();
//socket.getInputStream();//获得客户端输入流
//改为线程实现,不用阻塞
executorService.execute(new Process(socket,service));
}
}catch (Exception e){
e.printStackTrace();
}
}
}
要线程具体执行的代码,Socket获取输入输出流,通过反射获取被调用方法的方法名,类型,invoke方法得到调用结果返回。
package com.xjp;
import com.xjp.rpc.RpcRequest;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;
/**
* @Description
* @Author XJP
* @Date 2020-04-25 9:18
*/
public class Process implements Runnable{
private Socket socket;
private Object service;
public Process(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
//说明接收了一个客户端请求
try {
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
//读取客户端数据
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
Object invoke = invoke(rpcRequest);
objectOutputStream.writeObject(invoke);
objectOutputStream.flush();
}catch (Exception e){
e.printStackTrace();
}
}
//反射过程调用
public Object invoke(RpcRequest rpcRequest) throws Exception{
Object[] args = rpcRequest.getParameters();
Class clazz = Class.forName(rpcRequest.getClassName());
Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
Object invoke = method.invoke(service, args);
return invoke;
}
}
创建Test测试类,发布接口服务。
package com.xjp;
import com.xjp.impl.HelloServerImpl;
import com.xjp.rpc.IHelloServer;
/**
* @Description
* @Author XJP
* @Date 2020-04-25 10:07
*/
public class Test {
public static void main(String[] args) {
IHelloServer helloServer = new HelloServerImpl();
RpcProxyServer proxyServer = new RpcProxyServer();
proxyServer.publisher(helloServer,8080);
}
}
服务端代码已经完成。
5. 客户端代码
导入api依赖
创建客户端对目标对象的一个代理
package com.xjp.rpc.proxy;
import java.lang.reflect.Proxy;
/**
* @Description
* @Author XJP
* @Date 2020-04-24 22:05
*/
public class RpcClientProxy {
public <T> T clientProxy(final Class<T> interfaceCls, final String host, final int port){
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(),
new Class<?>[]{interfaceCls}, new RemoteInvoHander(host,port));
}
}
host是调用的目标地址,port是端口
package com.xjp.rpc.proxy;
import com.xjp.rpc.RpcRequest;
import com.xjp.rpc.RpcTranspost;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @Description
* @Author XJP
* @Date 2020-04-24 22:12
*/
public class RemoteInvoHander implements InvocationHandler {
private String host;
private int port;
public RemoteInvoHander(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setParameters(args);
rpcRequest.setTypes(method.getParameterTypes());
RpcTranspost rpcTranspost = new RpcTranspost(host,port);
return rpcTranspost.sendRequest(rpcRequest);
}
}
创建rpc发送请求的类
package com.xjp.rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
/**
* @Description
* @Author XJP
* @Date 2020-04-25 9:38
*/
public class RpcTranspost {
private String host;
private int port;
public RpcTranspost(String host, int port) {
this.host = host;
this.port = port;
}
public Object sendRequest(RpcRequest rpcRequest) throws IOException {
Socket socket = null;
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
socket = new Socket(host,port);
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(rpcRequest);
objectOutputStream.flush();//清空缓冲区
objectInputStream = new ObjectInputStream(socket.getInputStream());
Object o = objectInputStream.readObject();
return o;
}catch (Exception e){
e.printStackTrace();
}finally {
objectInputStream.close();
objectOutputStream.close();
}
return null;
}
}
客户端调用服务端接口测试代码
package com.xjp.rpc;
import com.xjp.rpc.proxy.RpcClientProxy;
/**
* @Description
* @Author XJP
* @Date 2020-04-24 22:14
*/
public class RpcClient {
public static void main(String[] args) {
RpcClientProxy rpcClientProxy = new RpcClientProxy();
IHelloServer helloServer = rpcClientProxy.clientProxy(IHelloServer.class, "localhost", 8080);
System.out.println(helloServer.sayHello("测试RPC通信"));
}
}
6. 启动测试
启动服务端的Test测试类
启动客户端的测试代码
可以看到客户端的请求已经通过网络传输到了服务端
因为这种是demo演示,所以socket是短连接,一次通信完成之后连接就断掉了,其他RPC框架用的是基于长连接的通信。
主要的技术点还是在客户端对目标对象的代理,网络IO流的传输以及序列化,然后服务端的反序列化,对客户端的方法进行反射调用,RPC主流框架有dubbo,grpc,Thrift,jsonrpc等,框架封装了底层调用的逻辑,使得开发者不需要过多关注底层原理,就能调用本地方法一样的去调用远程方法。
小白一个,还在继续学习中。。。。。。