1、什么是 RPC
RPC 的全称是 Remote Procedure Call,即远程过程调用。简单解读字面上的意思,远程肯定是指要跨机器而非本机,所以需要用到网络编程才能实现,但是不是只要通过网络通信访问到另一台机器的应用程序,就可以称之为 RPC 调用了?显然并不够。我理解的 RPC 是帮助我们屏蔽网络编程细节,实现调用远程方法就跟调用本地(同一个项目中的方法)一样的体验,我们不需要因为这个方法是远程调用就需要编写很多与业务无关的代码。
RPC 的作用:
屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
隐藏底层网络通信的复杂性,让我们更专注于业务逻辑
2、RPC 架构流程
RPC 本质上就是一个远程调用,那肯定就需要通过网络来传输数据。虽然传输协议可以有多种选择,但考虑到可靠性的话,我们一般默认采用 TCP 协议。为了屏蔽网络传输的复杂性,我们需要封装一个单独的数据传输模块用来收发二进制数据,这个单独模块我们可以叫做传输模块。
用户请求的时候是基于方法调用,方法出入参数都是对象数据,对象是肯定没法直接在网络中传输的,我们需要提前把它转成可传输的二进制,这就是我们说的序列化过程。
但只是把方法调用参数的二进制数据传输到服务提供方是不够的,我们需要在方法调用参数的二进制数据后面增加“断句”符号来分隔出不同的请求,在两个“断句”符号中间放的内容就是我们请求的二进制数据,这个过程我们叫做协议封装。
虽然这是两个不同的过程,但其目的都是一样的,都是为了保证数据在网络中可以正确传输。这里我说的正确,可不仅指数据能够传输,还需要保证传输后能正确还原出传输前的语义。所以我们可以把这两个处理过程放在架构中的同一个模块,统称为协议模块。
除此之外,我们还可以在协议模块中加入压缩功能,这是因为压缩过程也是对传输的二进制数据进行操作。在实际的网络传输过程中,我们的请求数据包在数据链路层可能会因为太大而被拆分成多个数据包进行传输,为了减少被拆分的次数,从而导致整个传输过程时间太长的问题,我们可以在 RPC 调用的时候这样操作:在方法调用参数或者返回值的二进制数据大于某个阈值的情况下,我们可以通过压缩框架进行无损压缩,然后在另外一端也用同样的压缩算法进行解压,保证数据可还原。
传输和协议这两个模块是 RPC 里面最基础的功能,它们使对象可以正确地传输到服务提供方。
但距离 RPC 的目标——实现像调用本地一样地调用远程,还缺少点东西。因为这两个模块所提供的都是一些基础能力,要让这两个模块同时工作的话,我们需要手写一些黏合的代码,但这些代码对我们使用 RPC 的研发人员来说是没有意义的,而且属于一个重复的工作,会导致使用过程的体验非常不友好。
这就需要我们在 RPC 里面把这些细节对研发人员进行屏蔽,让他们感觉不到本地调用和远程调用的区别。假设有用到 Spring 的话,我们希望 RPC 能让我们把一个 RPC 接口定义成一个 Spring Bean,并且这个 Bean 也会统一被 Spring Bean Factory 管理,可以在项目中通过 Spring 依赖注入到方式引用。
3、手动实现简易的RPC
1、server端代码
功能接口定义
package com.lf.rpc;
public interface IHelloWorld {
String sayHello(String msg);
}
功能实现
package com.lf.rpc;
public class HelloWorldServiceImpl implements IHelloWorld {
@Override
public String sayHello(String msg) {
// TODO Auto-generated method stub
return "Hello "+msg+", welcome to Rpc!";
}
}
Rpc服务端
package com.lf.rpc;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class RpcServer {
private static final ExecutorService executorService = Executors.newCachedThreadPool();
public void publisher(final Object service,int port){
ServerSocket serverSocket = null;
try {
serverSocket = new ServerSocket(port);//启动一个服务监听
while(true){
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(socket,service));
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if(serverSocket!=null){
try {
serverSocket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
发送处理类
package com.lf.rpc;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;
public class ProcessorHandler implements Runnable {
private Socket socket;
private Object service;//服务端发布的服务
public ProcessorHandler(Socket socket, Object service) {
this.socket = socket;
this.service = service;
}
@Override
public void run() {
// TODO 处理请求
ObjectInputStream objectInputStream =null;
ObjectOutputStream objectOutputStream =null;
try {
objectInputStream = new ObjectInputStream(socket.getInputStream());
RpcRequest request = (RpcRequest) objectInputStream.readObject();
Object result = invoke(request);
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(result);
objectOutputStream.flush();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(objectInputStream!=null){
try {
objectInputStream.close();
objectOutputStream.close();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
private Object invoke(RpcRequest request) throws Exception, IllegalArgumentException, InvocationTargetException{
Object[] args = request.getParameters();
Class<?> [] types = new Class[args.length];
for (int i = 0; i < types.length; i++) {
types[i] = args[i].getClass();
}
Method method = service.getClass().getMethod(request.getMethodName(), types);
return method.invoke(service, args);
}
}
数据传输定义model
package com.lf.rpc;
import java.io.Serializable;
/**
* 传输对象
* @author admin
*
*/
public class RpcRequest implements Serializable{
private static final long serialVersionUID = 6351477854838485391L;
private String className;
private String methodName;
private Object[] parameters;
public RpcRequest(String className, String methodName, Object[] parameters) {
super();
this.className = className;
this.methodName = methodName;
this.parameters = parameters;
}
public RpcRequest() {
super();
// TODO Auto-generated constructor stub
}
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 Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
发布服务
package com.lf.rpc;
public class ServerDemo {
public static void main(String[] args) {
IHelloWorld service = new HelloWorldServiceImpl();
RpcServer server = new RpcServer();
System.out.println("服务已发布!");
server.publisher(service, 8888);
}
}
2、client端代码
功能接口定义
package com.lf.rpc;
public interface IHelloWorld {
String sayHello(String msg);
}
数据传输定义model
package com.lf.rpc;
import java.io.Serializable;
/**
* 传输对象
* @author admin
*
*/
public class RpcRequest implements Serializable{
private static final long serialVersionUID = 6351477854838485391L;
private String className;
private String methodName;
private Object[] parameters;
public RpcRequest(String className, String methodName, Object[] parameters) {
super();
this.className = className;
this.methodName = methodName;
this.parameters = parameters;
}
public RpcRequest() {
super();
// TODO Auto-generated constructor stub
}
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 Object[] getParameters() {
return parameters;
}
public void setParameters(Object[] parameters) {
this.parameters = parameters;
}
}
客户端代理类
```bash
package com.lf.rpc;
import java.lang.reflect.Proxy;
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 RemoteInvocationHandler(host,port));
}
}
InvocationHandler增强类
package com.lf.rpc;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class RemoteInvocationHandler implements InvocationHandler {
private String host;
private int port;
public RemoteInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
RpcRequest request = new RpcRequest();
request.setClassName(method.getDeclaringClass().getName());
request.setMethodName(method.getName());
request.setParameters(args);
TcpTransport trans = new TcpTransport(host, port);
return trans.send(request);
}
}
数据传输类
package com.lf.rpc;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
public class TcpTransport {
private String host;
private int port;
public TcpTransport(String host, int port) {
super();
this.host = host;
this.port = port;
}
Socket newSocket(){
System.out.println("创建一个连接");
Socket socket = null;
try {
socket = new Socket(host,port);
return socket;
} catch (Exception e) {
// TODO: handle exception
throw new RuntimeException("连接建立失败!");
}
}
public Object send(RpcRequest request){
Socket socket = null;
ObjectOutputStream objectOutputStream = null;
ObjectInputStream objectInputStream = null;
try {
socket = newSocket();
objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(request);
objectOutputStream.flush();
objectInputStream = new ObjectInputStream(socket.getInputStream());
Object readObject = objectInputStream.readObject();
return readObject;
} catch (Exception e) {
// TODO: handle exception
e.printStackTrace();
throw new RuntimeException("连接建立失败!");
}finally {
if(socket!=null){
try {
socket.close();
objectOutputStream.close();
objectInputStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
客户端调用服务
package com.lf.rpc;
public class ClientDemo {
public static void main(String[] args) {
RpcClientProxy rpcClientProxy = new RpcClientProxy();
IHelloWorld hello = rpcClientProxy.clientProxy(IHelloWorld.class, "localhost", 8888);
System.out.println(hello.sayHello("lf"));
}
}