RPC模式的介绍以及简单的实现

栏目: 服务器 · 发布时间: 6年前

内容简介:RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。(此段百度的,不管来自哪儿,弄明白就是自己的)下面来展示下本人拙劣的画图技术Client:客户端,服务消费者,负责发起RPC调用,通过远程代理对象调用远程服务。 Serialization/Deserilization:负责对RPC调用通过网络传输的内容进行序列化与反序列化。 Stub Proxy:代理对象,屏蔽RPC调用过程中复

RPC(Remote Procedure Call,远程过程调用)一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地资源一样,通过网络传输去访问远端系统资源。(此段百度的,不管来自哪儿,弄明白就是自己的)

RPC框架实现的架构原理:

下面来展示下本人拙劣的画图技术

RPC模式的介绍以及简单的实现

Client:客户端,服务消费者,负责发起RPC调用,通过远程代理对象调用远程服务。 Serialization/Deserilization:负责对RPC调用通过网络传输的内容进行序列化与反序列化。 Stub Proxy:代理对象,屏蔽RPC调用过程中复杂的网络处理逻辑,使得RPC调用透明化,能够保持与本地调用一样的代码风格。 TransPort:作为RPC框架底层的通信传输模块,一般通过Socket在客户端与服务端之间传递请求与应答信息。

我所理解的原理过程(不对的地方求指教):

  • 服务消费方(client)调用以本地调用方式调用服务;
  • stub Proxy接收到调用后负责将方法、参数等组装成可以序列化的Invocation对象;
  • stub Proxy找到服务地址,建立socket连接并将消息发送到服务端;
  • server端将接收到的数据反序列化,交给stub Proxy处理;
  • server端将stub收集到的调用信息分发给具体的方法去执行,这里并不是真正的client去执行该方法,而是在server端通过反射生成的client动态对象去执行;
  • RPC接口执行完毕,返回执行结果,思路类似。

RPC的简易实现

技术方案

采用的是Socket通信、动态代理与反射与 Java 原生的序列化,由于能力有限,只会基于BIO来实现,以后会尝试用zookeeper、netty、 redis 等来实现!

具体实现

1、服务提供者提供的服务实现

package com.ooliuyue.simplRpc.test;

/**
 * @Auther: ly
 * @Date: 2019/3/14 11:45
 */

public class HelloServiceImpl implements HelloService {
    @Override
    public String hello(String name) {
        return "hello " + name;
    }
}

复制代码

2、客户端获取远程服务,服务端暴露自己的服务,Socket来完成通信工作(注释比较详细,一目了然)

package com.ooliuyue.simplRpc.rpc;

import com.ooliuyue.simplRpc.utils.StringUtils;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;

/**
 * @Auther: ly
 * @Date: 2019/3/13 17:53
 */

public class SimpleRpc {
    /**
     *将指定服务暴露出来,供客户端远程调用
     * @param service 需要暴露的服务
     * @param port  暴露的端口号
     * @throws Exception
     */
    public static void export(final Object service,int port) throws Exception{
        if (service == null)
            throw new IllegalArgumentException("service not null");
        if (port < 0 || port > 65535)
            throw new IllegalArgumentException("port in [0,65535]");
        System.out.println("Export Service:" + service.getClass().getName() + " port:" + port );
        /**
         *  Socket编程步骤
         *  服务器端创建ServerSocket对象,调用accept方法返回Socket对象
         *  客户端创建Socket对象,通过端口连接到服务器
         *  客户端、服务器端都使用Socket中的getInputStream方法和getOutputStream方法获得输入流和输出流,
         *  进一步进行数据读写操作
         */
        /**
         * socket在服务器端上的使用
         * 1.getInputStream方法得到的是一个输入流,服务端的Socket对象上的getInputStream方法得到的输入流其实就是从客户端发送给服务器端的数据流。
         * 2.getOutputStream方法得到的是一个输出流,服务端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给客户端的数据。
         */
        ServerSocket serverSocket = new ServerSocket(port);
        while (true) {
            final Socket socket = serverSocket.accept();
            new Thread(() -> {
                ObjectInputStream input = null;
                ObjectOutputStream output = null;
                try {
                    try {
                        //将客户端发送给服务端的数据流反序列化成对象
                        input = new ObjectInputStream(socket.getInputStream());
                        String methodName = input.readUTF();
                        Class<?>[] paramTypes = (Class<?>[]) input.readObject();
                        Object[] arguments = (Object[]) input.readObject();

                        Method method = service.getClass().getMethod(methodName, paramTypes);
                        //反射调用服务实现,获取执行结果
                        Object result = method.invoke(service, arguments);

                        //将执行结果反序列化,然后发送给客户端
                        output = new ObjectOutputStream(socket.getOutputStream());
                        output.writeObject(result);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    if (input != null)
                        try {
                            input.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                    if (output != null)
                        try {
                            output.close();
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                }
            }).start();
        }
    }

    /**
     * 获取远程服务
     * @param interfaceClass 服务接口class
     * @param host 远程IP地址
     * @param port 远程端口号
     * @param <T> 指定接口的实例
     * @return
     * @throws Exception
     */
    /**
     *
     * socket在客户端上的使用
     * 1.getInputStream方法可以得到一个输入流,客户端的Socket对象上的getInputStream方法得到输入流其实就是从服务器端返回的数据。
     * 2.getOutputStream方法得到的是一个输出流,客户端的Socket对象上的getOutputStream方法得到的输出流其实就是发送给服务器端的数据。
     */
    public static <T> T getRemoteService(final Class<T> interfaceClass,final String host,final int port)
            throws Exception {
        verifyGetRemoteService(interfaceClass,host,port);
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),new Class<?>[]{interfaceClass},(Object proxy, Method method, Object[] args) ->{
            Socket socket = new Socket(host,port);
            System.out.println("get remote service :" + interfaceClass.getName() + " from " + host + ":" + port);

            //将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
            ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
            output.writeUTF(method.getName());
            output.writeObject(method.getParameterTypes());
            output.writeObject(args);

            //result就是从服务器返回的数据
            ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
            Object result = input.readObject();
            return  result;
        });

    }

    private static <T> void verifyGetRemoteService(final Class<T> interfaceClass, final String host, final int port) {
        if (interfaceClass == null)
            throw new IllegalArgumentException("interfaceClass not null");
        if (!interfaceClass.isInterface())
            throw new IllegalArgumentException("interfaceClass not a interface");
        if (!StringUtils.isNotBlank(host))
            throw new IllegalArgumentException("host not blank");
        if (port < 0 || port > 65535)
            throw new IllegalArgumentException("port in [0,65535]");
    }

}

复制代码

3、测试 首先服务端暴露服务,运行main

package com.ooliuyue.simplRpc.test;

/**
 * @Auther: ly
 * @Date: 2019/3/14 11:43
 */

import com.ooliuyue.simplRpc.rpc.SimpleRpc;

/**
 * 服务提供者
 */
public class Server {
    public static void main(String[] args) throws Exception {
        HelloService helloService = new  HelloServiceImpl();
        SimpleRpc.export(helloService,1000);
    }

}
复制代码

运行结果: Export Service:com.ooliuyue.simplRpc.test.HelloServiceImpl port:1000

然后客户端进行调用,运行main

package com.ooliuyue.simplRpc.test;

import com.ooliuyue.simplRpc.rpc.SimpleRpc;

/**
 * @Auther: ly
 * @Date: 2019/3/14 11:43
 */

public class Client {
    public static void main(String[] args) throws Exception {
        HelloService remoteService = SimpleRpc.getRemoteService(HelloService.class, "127.0.0.1", 1000);
        String str = remoteService.hello("王大锤");
        System.out.println(str);
    }

}
复制代码

运行结果 get remote service :com.ooliuyue.simplRpc.test.HelloService from 127.0.0.1:1000 hello 王大锤


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Landing Page Optimization

Landing Page Optimization

Tim Ash / Wiley Publishing / 2008-1-29 / USD 29.99

在线阅读本书 How much money are you losing because of poor landing page design? In this comprehensive, step-by-step guide, you’ll learn all the skills necessary to dramatically improve your bottom li......一起来看看 《Landing Page Optimization》 这本书的介绍吧!

图片转BASE64编码
图片转BASE64编码

在线图片转Base64编码工具

SHA 加密
SHA 加密

SHA 加密工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具