RPC及其简单实现

  • 一、RPC介绍
  • 1. 概念
  • 2. 通讯模式
  • 3. 通用架构
  • 4. 调用原理
  • 二、简单实现

一、RPC介绍


1. 概念

远程过程调用(Remote Procedure Call,RPC)是一种常用的分布式网络通信协议,它允许运行于一台计算机的程序调用另一台计算机的子程序,同时将网络的通信细节隐藏起来,使得用户无须额外地为这个交互作用编程。

2. 通讯模式

基于Client/Server客户机与服务器的交互模式

3. 通用架构

  • 通用架构
  • 通信模块
    基于请求-应答模式,在客户程序与服务程序间传递数据,一般不会对数据包进行任何处理。请求-应答两种通信方法:
    1、同步
    2、异步(高并发场景下通常采用异步)
  • Stub程序模块
    本质上就是一个代理程序。当客户程序调用远程方法时由系统生成。其作用:
    1、在客户端,将请求信息通过网络模块发送给客户端;
    2、在服务器端,依次解码请求信息参数、调用相应的服务过程、编码应答返回结果等
  • 调度模块;
    根据通信模块传递过来的消息,调用对应的一个Stub程序。
  • 客户程序/服务程序模块
    真正需要用户编写的程序

4. 调用原理

  • 调用原理图

RPC是如何实现的 rpc的实现方式_.net

  • 服务器提供者
    运行于服务器端,提供服务接口定义及其实现类
  • 服务发布者
    运行于RPC服务器端,负责将本地服务发布为远程服务,供其他消费者使用
  • 本地服务代理
    运行于RPC客户端,通过代理调用远程服务提供者,并将结果封装返回给本地消费者

二、简单实现


  • 定义服务接口
/**
 * @author suben
 * @apiNote  服务提供者接口
 */
public interface EchoRpcService {
    /**
     * 接口方法:传入ping,返回结果
     * @param ping
     * @return
     */
    String echo(String ping);
}
  • 定义服务接口实现类
/**
	 * 服务提供者接口实现类
	 */
	public class EchoRpcServiceImpl implements EchoRpcService {
	    @Override
	    public String echo(String ping) {
	        return ping != null ? ping + "--> I am Ok!!" : "I am not Ok.";
	    }
	}
  • 定义服务发布者
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 服务发布者:运行在RPC服务端,将远程服务发布为本地服务,并供其他消费者调用
 */
public class RpcExporter {

    // 创建程序在运行期间可用的固定的线程池
    static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

    // 发布方法
    public static void export(String hostname,int port) {
        ServerSocket server = null;
        try {
            server = new ServerSocket();
            server.bind(new InetSocketAddress(hostname,port));
            // executor.execute(new ExporterTask(server.accept()));// 只能接收一个消费者请求
            while (true){
                executor.execute(new ExporterTask(server.accept()));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                server.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 内部线程类
     */
    private static class ExporterTask implements Runnable{

        Socket client = null; // 声明Socket
        public ExporterTask(Socket client){
            this.client = client;
        }

        @Override
        public void run() {
            ObjectOutputStream output = null;
            ObjectInputStream input = null;

            try {
                // 创建对象输入流
                input = new ObjectInputStream(client.getInputStream());
                // 通过对象输入流读取远程接口名称、方法名、接口参数及其类型等
                String interfaceName = input.readUTF();
                // 根据接口名称和反射机制,获取接口Class
                Class<?> service = Class.forName(interfaceName);
                // 获取方法名称
                String methodName = input.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
                Object[] arguments = (Object[]) input.readObject();

                // 通过找到的接口服务,获取真正的远程方法Method
                Method method = service.getMethod(methodName, parameterTypes);
                // 调用远程方法
                Object invokeResult = method.invoke(service.newInstance(), arguments);
                // 创建输出流对象,将远程方法结果返回回写
                output = new ObjectOutputStream(client.getOutputStream());
                output.writeObject(invokeResult);

            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                if (output != null){
                    try {
                        output.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (input != null){
                    try {
                        input.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
  • 定义RPC客户端本地服务器代理
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;

/**
 * RPC客户端本地服务器代理
 */
public class RpcImporterProxy<S> {

    public S importer(final Class<?> serviceClass, final InetSocketAddress address){
        return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(),
                new Class[]{serviceClass.getInterfaces()[0]},
                new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = null;
                ObjectInputStream input = null;
                ObjectOutputStream output = null;
                try{
                    socket = new Socket();
                    socket.connect(address);
                    output = new ObjectOutputStream(socket.getOutputStream());
                    output.writeUTF(serviceClass.getName());
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);
                    input = new ObjectInputStream(socket.getInputStream());
                    return input.readObject();
                }finally {
                    if (socket != null){
                        socket.close();
                    }
                    if (output != null){
                        output.close();
                    }
                    if (input != null){
                        input.close();
                    }
                }

            }
        });
    }
}
  • 定义测试类
import java.net.InetSocketAddress;

/**
 * 测试类
 */
public class RpcTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                RpcExporter.export("localhost",8088);
            }
        }).start();

        RpcImporterProxy<EchoRpcService> importerProxy = new RpcImporterProxy<>();
        EchoRpcService echoRpcService = importerProxy.importer(EchoRpcServiceImpl.class, new InetSocketAddress("localhost", 8088));
        System.out.println(echoRpcService.echo("Are you Ok?"));
    }
}
  • 运行结果

RPC是如何实现的 rpc的实现方式_.net_02