• java 远程调用 RPC


    1. 概念

      RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)、Hessian、Http invoker等。RPC是与语言无关的。直观说法就是A通过网络调用B的过程方法。也就是说两台服务器A,B,一个应用部署在A服务器上,想要调用B服务器上应用提供的函数/方法,由于不在一个内存空间,不能直接调用,需要通过网络来表达调用的语义和传达调用的数据。

    1、首先要解决寻址的问题,也就是说,A服务器上的应用怎么告诉底层的RPC框架,B服务器的IP,以及应用绑定的端口,还有方法的名称,这样才能完成调用

    2、方法的参数需要通过底层的网络协议如TCP传递到B服务器,由于网络协议是基于二进制的,内存中的参数的值要序列化成二进制的形式

    3、在B服务器上完成寻址后,需要对参数进行反序列化,恢复为内存中的表达方式,然后找到对应的方法进行本地调用,然后得到返回值,

    4、返回值还要发送回服务器A上的应用,也要经过序列化的方式发送,服务器A接到后,再反序列化,恢复为内存中的表达方式,交给应用

    1.1 框架

    1.1.1 RMI

      java自带远程调用框架

    1.1.2 THrift

      thrift是一个软件框架,用来进行可扩展且跨语言的服务的开发。它结合了功能强大的软件堆栈和代码生成引擎,以构建在 C++, Java, Go,Python, PHP, Ruby, Erlang, Perl, Haskell, C#, Cocoa, JavaScript, Node.js, Smalltalk, and OCaml 这些编程语言间无缝结合的、高效的服务。

    1.1.3 Dubbo(Dubbox)  

      服务治理,也可以做微服务

    1.1.4 gRPC

      gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpcgrpc-javagrpc-go. 其中 C 版本支持 CC++Node.jsPythonRubyObjective-CPHP 和 C# 支持.

      开源中国组织翻译的《gRPC 官方文档中文版》:http://doc.oschina.net/grpc

      gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。

    2. RPC的实现

      RPC能够让本地应用简单、高效地调用服务器中的过程(服务)。它主要应用在分布式系统。如Hadoop中的IPC组件。但怎样实现一个RPC框架呢?

      从下面几个方面思考,仅供参考:

      1.通信模型:假设通信的为A机器与B机器,A与B之间有通信模型,在Java中一般基于BIO或NIO;。

      2.过程(服务)定位:使用给定的通信方式,与确定IP与端口及方法名称确定具体的过程或方法;

      3.远程代理对象:本地调用的方法(服务)其实是远程方法的本地代理,因此可能需要一个远程代理对象,对于Java而言,远程代理对象可以使用Java的动态对象实现,封装了调用远程方法调用;

      4.序列化,将对象名称、方法名称、参数等对象信息进行网络传输需要转换成二进制传输,这里可能需要不同的序列化技术方案。如:protobuf,Arvo等。

    2.1 实现技术方案

    下面使用比较原始的方案实现RPC框架,采用Socket通信、动态代理与反射与Java原生的序列化。

    2.2 RPC框架架构

    RPC架构分为三部分:

    1)服务提供者,运行在服务器端,提供服务接口定义与服务实现类。

    2)服务中心,运行在服务器端,负责将本地服务发布成远程服务,管理远程服务,提供给服务消费者使用。

    3)服务消费者,运行在客户端,通过远程代理对象调用远程服务。

    2.3 Demo

    2.3.1 服务提供者接口定义

    HelloService.java

    1 package com.loveincode.rpc;
    2 
    3 public interface HelloService {
    4 
    5     String hello(String name);
    6 
    7 }

    2.3.2 服务提供者接口实现

    HelloServiceImpl.java

     1 package com.loveincode.rpc;
     2 
     3 //HelloServices接口实现类:
     4 public class HelloServiceImpl implements HelloService {
     5      
     6     public String hello(String name) {
     7         return "hello, " + name;
     8     }
     9  
    10 }

    2.3.3 RPC框架服务中心

    RpcFramework.java

      1 package com.loveincode.rpc;
      2 
      3 import java.io.ObjectInputStream;
      4 import java.io.ObjectOutputStream;
      5 import java.lang.reflect.InvocationHandler;
      6 import java.lang.reflect.Method;
      7 import java.lang.reflect.Proxy;
      8 import java.net.ServerSocket;
      9 import java.net.Socket;
     10 
     11 public class RpcFramework {
     12     /**
     13      * 暴露服务
     14      * 
     15      * @param service
     16      *            服务实现
     17      * @param port
     18      *            服务端口
     19      * @throws Exception
     20      */
     21     public static void export(final Object service, int port) throws Exception {
     22         if (service == null)
     23             throw new IllegalArgumentException("service instance == null");
     24         if (port <= 0 || port > 65535)
     25             throw new IllegalArgumentException("Invalid port " + port);
     26         System.out.println("Export service " + service.getClass().getName() + " on port " + port);
     27         ServerSocket server = new ServerSocket(port);// 前面都是验证调用是否符合规则,这里在被调用端开一个服务。下面就是死循环运行。
     28         for (;;) {
     29             try {
     30                 final Socket socket = server.accept();
     31                 new Thread(new Runnable() {// 对每一个请求new一个线程,匿名类
     32                     @Override
     33                     public void run() {
     34                         try {
     35                             try {
     36                                 ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
     37                                 try {
     38                                     String methodName = input.readUTF();
     39                                     Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
     40                                     Object[] arguments = (Object[]) input.readObject();
     41                                     ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());// 接收客户端传来的方法名、参数类型、参数
     42                                     try {
     43                                         Method method = service.getClass().getMethod(methodName, parameterTypes);// 在本地生成对应的方法,
     44                                         Object result = method.invoke(service, arguments);// 调用
     45                                         output.writeObject(result);// 返回结果
     46                                     } catch (Throwable t) {
     47                                         output.writeObject(t);
     48                                     } finally {
     49                                         output.close();
     50                                     }
     51                                 } finally {
     52                                     input.close();
     53                                 }
     54                             } finally {
     55                                 socket.close();
     56                             }
     57                         } catch (Exception e) {
     58                             e.printStackTrace();
     59                         }
     60                     }
     61                 }).start();
     62             } catch (Exception e) {
     63                 e.printStackTrace();
     64             }
     65         }
     66     }
     67 
     68     /**
     69      * 引用服务
     70      * 
     71      * @param <T>
     72      *            接口泛型
     73      * @param interfaceClass
     74      *            接口类型
     75      * @param host
     76      *            服务器主机名
     77      * @param port
     78      *            服务器端口
     79      * @return 远程服务
     80      * @throws Exception
     81      */
     82     @SuppressWarnings("unchecked")
     83     public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
     84         if (interfaceClass == null)
     85             throw new IllegalArgumentException("Interface class == null");
     86         if (!interfaceClass.isInterface())
     87             throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
     88         if (host == null || host.length() == 0)
     89             throw new IllegalArgumentException("Host == null!");
     90         if (port <= 0 || port > 65535)
     91             throw new IllegalArgumentException("Invalid port " + port);
     92         System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
     93         return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] { interfaceClass },
     94                 new InvocationHandler() {// 用动态代理的方法进行包装,看起来是在调用一个方法,其实在内部通过socket通信传到服务器,并接收运行结果
     95                     public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
     96                         Socket socket = new Socket(host, port);
     97                         try {
     98                             ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
     99                             try {
    100                                 output.writeUTF(method.getName());
    101                                 output.writeObject(method.getParameterTypes());
    102                                 output.writeObject(arguments);
    103                                 ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
    104                                 try {
    105                                     Object result = input.readObject();
    106                                     if (result instanceof Throwable) {
    107                                         throw (Throwable) result;
    108                                     }
    109                                     return result;// 返回结果
    110                                 } finally {
    111                                     input.close();
    112                                 }
    113                             } finally {
    114                                 output.close();
    115                             }
    116                         } finally {
    117                             socket.close();
    118                         }
    119                     }
    120                 });
    121     }
    122 }

    2.3.4 服务提供者

    RpcProvider.java

    1 package com.loveincode.rpc;
    2 
    3 public class RpcProvider {
    4     public static void main(String[] args) throws Exception {
    5         HelloService service = new HelloServiceImpl();
    6         RpcFramework.export(service, 1234);
    7     }
    8 }

    2.3.5 服务消费者

    RpcConsumer.java

    package com.loveincode.rpc;
    
    public class RpcConsumer {
        public static void main(String[] args) throws Exception {
            HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                String hello = service.hello("World " + i);
                System.out.println(hello);
                Thread.sleep(1000);
            }
        }
    }

    运行结果

    执行RpcProvider 结果:

    Export service com.loveincode.rpc.HelloServiceImpl on port 1234

    执行RpcConsumer 结果:

    Get remote service com.loveincode.rpc.HelloService from server 127.0.0.1:1234
    hello, World 0
    hello, World 1
    hello, World 2
    hello, World 3
    hello, World 4
    hello, World 5
    hello, World 6
    hello, World 7
    hello, World 8
    hello, World 9
    ...
  • 相关阅读:
    FLASH置于底层
    图片等比缩放
    fedora 系统使用 Broadcom BCM4312 无线网卡(转)
    ubuntu语言问题
    轻松安装、卸载Linux软件
    redhat6.0下使用vnc
    http网络安装centos 5.5系统总结
    如何在windows下搭建python的IDE开发环境
    对做技术的一点思考
    C++继承类和基类之间成员函数和虚函数调用机制
  • 原文地址:https://www.cnblogs.com/loveincode/p/7348177.html
Copyright © 2020-2023  润新知