rpc

RPC框架性能大比拼 :
http://colobu.com/2016/09/05/benchmarks-of-popular-rpc-frameworks/

百分点 harpc git:https://github.com/baifendian/harpc

RPC 简介

      RPC(Remote Procedure Call,远程过程调用)是建立在Socket之上的,出于一种类比的愿望,在一台机器上运行的主程序,可以调用另一台机器上的程序,就像LPC(本地过程调用)
越底层,代码越复杂、灵活性越高、效率越高
越上层,抽象封装的越好、代码越简单、效率越差
     Socket和RPC的区别再次说明了这点。在传统的编程概念中,过程是由程序员在本地编译完成,并只能局限在本地运行的一段代码,也即其主程序和过程之间的运行关系是本地调用关系。因此这种结构在网络日益发展的今天已无法适应实际需求。传统过程调用模式无法充分利用网络上其他主机的资源(如CPU、Memory等),也无法提高代码在实体间的共享程度,使得主机资源大量浪费。
     通过RPC我们可以充分利用非共享内存的多处理器环境(例如通过局域网连接得多台工作站),这样可以简便地将你的应用分布在多台工作站上,应用程序就像运行在一个多处理器的计算机上一样。你可以方便的实现过程代码共享,提高系统资源的利用率,也可以将以大量数值处理的操作放在处理能力较强的系统上运行,从而减轻前端机的负担。
     RPC作为普遍的C/S开发方法,开发效率高效,可靠.但RPC方法的基本原则是:以模块调用的简单性忽略通讯的具体细节,以便程序员不用关心C/S之间的通讯协议,集中精力对付实现过程.这就决定了 RPC生成的通讯包不可能对每种应用都有最恰当的处理办法,与Socket方法相比,传输相同的有效数据,RPC占用更多的网络带宽.
RPC是在Socket的基础上实现的,它比socket需要更多的网络和系统资源.另外,在对程序优化时,程序员虽然可以直接修改由rpcgen产生的令人费解的源程序,但对于追求程序设计高效率的RPC而言,获得的简单性则被大大削弱.
RPC 这个概念术语在上世纪 80 年代由 Bruce Jay Nelson 提出。这里我们追溯下当初开发 RPC 的原动机是什么?在 Nelson 的论文 "Implementing Remote Procedure Calls" 中他提到了几点:
    1. 简单:RPC 概念的语义十分清晰和简单,这样建立分布式计算就更容易。
    2. 高效:过程调用看起来十分简单而且高效。
    3. 通用:在单机计算中过程往往是不同算法部分间最重要的通信机制。
通俗一点说,就是一般程序员对于本地的过程调用很熟悉,那么我们把 RPC 作成和本地调用完全类似,那么就更容易被接受,使用起来毫无障碍。Nelson 的论文发表于 30 年前,其观点今天看来确实高瞻远瞩,今天我们使用的 RPC 框架基本就是按这个目标来实现的

RPC 实现


1、 服务调用方(client)调用以本地调用方式调用服务;
2、 client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;在Java里就是序列化的过程
3、 client stub找到服务地址,并将消息通过网络发送到服务端;
4、 server stub收到消息后进行解码,在Java里就是反序列化的过程;
5、 server stub根据解码结果调用本地的服务;
6、 本地服务执行处理逻辑;
7、 本地服务将结果返回给server stub;
8、 server stub将返回结果打包成消息,序列化;
9、 server stub将打包后的消息通过网络并发送至消费方
10、 client stub接收到消息,并进行解码, 反序列化;
11、 服务调用方(client)得到最终结果


RPC框架的目标就是把2-10步封装起来,把调用、编码/解码的过程封装起来,让用户像调用本地服务一样的调用远程服务。要做到对客户端(调用方)透明化服务, RPC框架需要考虑解决如下问题:
1、 服务端提供的服务如何发布,客户端如何发现服务;
2、 如何对请求对象和返回结果进行序列化和反序列化;
3、 如何更高效进行网络通信。

rpc vs http

RPC是一种编程模式,把对服务器的调用抽象为过程调用,通常还伴随着框架代码自动生成等功能。使用RPC做网络服务开发时,通常只需要实现服务器端的一个处理函数,其余的客户端调用,序列化反序列化,方法派发(收到什么样的消息,调用服务器端的什么函数)等都由框架或者生成的代码来完成,较大地减轻了网络服务开发和调用的复杂性。
HTTP是一种应用层网络协议,RPC可以采用自定义协议,也可以通过HTTP协议来传输,thrift,grpc,xml-rpc,json-rpc都是通过HTTP传输的。HTTP既支持长连接,也支持短连接。

常用的rpc框架
Dubbo

阿里巴巴公司开源的一个Java高性能优秀的服务框架,使得应用可通过高性能的 RPC 实现服务的输出和输入功能,可以和 Spring框架无缝集成。不过,略有遗憾的是,据说在淘宝内部,dubbo由于跟淘宝另一个类似的框架HSF(非开源)有竞争关系,导致dubbo团队已经解散(参见http://www.oschina.net/news/55059/druid-1-0-9 中的评论),反到是当当网的扩展版本仍在持续发展,墙内开花墙外香。其它的一些知名电商如当当、京东、国美维护了自己的分支或者在dubbo的基础开发,但是官方的库缺乏维护,相关的依赖类比如Spring,Netty还是很老的版本(Spring 3.2.16.RELEASE, netty 3.2.5.Final),倒是有些网友写了升级Spring和Netty的插件。

Motan

新浪微博开源的一个Java 框架。它诞生的比较晚,起于2013年,2016年5月开源。Motan 在微博平台中已经广泛应用,每天为数百个服务完成近千亿次的调用。

rpcx

Go语言生态圈的Dubbo, 比Dubbo更轻量,实现了Dubbo的许多特性,借助于Go语言优秀的并发特性和简洁语法,可以使用较少的代码实现分布式的RPC服务。

gRPC

Google开发的高性能、通用的开源RPC框架,其由Google主要面向移动应用开发并基于HTTP/2协议标准而设计,基于ProtoBuf(Protocol Buffers)序列化协议开发,且支持众多开发语言。本身它不是分布式的,所以要实现上面的框架的功能需要进一步的开发。

thrift

Apache的一个跨语言的高性能的服务框架,也得到了广泛的应用

性能比较:

吞吐率:rpcx > thrift > grpc > montan > dubbo
平均响应时间: rpcx > thrift > grpc > montan > dubbo

举个栗子

一个非常简单的非分布式demo

定义 server stub

public class RPCServer {
    private ExecutorService threadPool;  
    private static final int DEFAULT_THREAD_NUM = 10;  
      
    public RPCServer(){  
        threadPool = Executors.newFixedThreadPool(DEFAULT_THREAD_NUM);  
    }  
    public void register(Object service, int port) {
         try {  
                System.out.println("server starts...");  
                ServerSocket server = new ServerSocket(port);  
                Socket socket = null;  
                while((socket = server.accept()) != null){  
                    System.out.println("client connected...");  
                    threadPool.execute(new Processor(socket, service));  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            }          
    }  
      
    class Processor implements Runnable{  
        Socket socket;  
        Object service;  
        public Processor(Socket socket, Object service){  
            this.socket = socket;  
            this.service = service;  
        }  
        public void process(){  
        }  
        @Override  
        public void run() {  
            try {  
                ObjectInputStream in = new ObjectInputStream(socket.getInputStream());  
                String methodName = in.readUTF();  
                Class<?>[] parameterTypes = (Class<?>[]) in.readObject();  
                Object[] parameters = (Object[]) in.readObject(); 
                Method method = service.getClass().getMethod(methodName, parameterTypes);  
                try {  
                    Object result = method.invoke(service, parameters);  
                    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());  
                    out.writeObject(result);  
                } catch (IllegalAccessException e) {  
                    e.printStackTrace();  
                } catch (IllegalArgumentException e) {  
                    e.printStackTrace();  
                } catch (InvocationTargetException e) {  
                    e.printStackTrace();  
                }  
            } catch (IOException e) {  
                e.printStackTrace();  
            } catch (NoSuchMethodException e) {  
                e.printStackTrace();  
            } catch (SecurityException e) {  
                e.printStackTrace();  
            } catch (ClassNotFoundException e1) {  
                e1.printStackTrace();  
            }  
        }  
    }
}
View Code

定义 client stub

public class RPCClient {
    @SuppressWarnings("unchecked")  
    public static <T> T getClient(Class<T> clazz, final String ip, final int port){  
        return  (T) Proxy.newProxyInstance(RPCClient.class.getClassLoader(), new Class<?>[]{clazz}, new InvocationHandler() {  
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    Socket socket = new Socket(ip, port);  
                    ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());  
                    out.writeUTF(method.getName());  
                    out.writeObject(method.getParameterTypes());  
                    out.writeObject(args); 
                    System.out.println(method.getName()+method.getParameterTypes()+args);
                    ObjectInputStream in = new ObjectInputStream(socket.getInputStream());  
                    return in.readObject();  
            }  
        });  
    }  
}
View Code

定义接口 (server)

public interface HelloService {
    public String hello(String name);
}
View Code

定义实现类(server)

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

启动 rpc服务

public class ServerStart {

    public static void main(String[] args) {
        HelloService helloService = new HelloServiceImpl();
        RPCServer server = new RPCServer();
        server.register(helloService, 5001);
    }

}
View Code

调用rpc服务 (client)

public class Client {

    public static void main(String args[]) {
        HelloService helloService = RPCClient.getClient(HelloService.class, "127.0.0.1", 5001);
        System.out.println(helloService.hello("young z"));
    }

}
View Code
原文地址:https://www.cnblogs.com/amei0/p/8569741.html