什么样的RPC才是好用的RPC

什么样的RPC才是好用的RPC

现在RPC框架很多,但是真正好用的RPC却是少之又少。那么什么是好用的RPC,什么是不好用的RPC呢,有一个评判标准吗?下面是我列举出来的衡量RPC好用与否的几条标准:

  • 真的像本地函数一样调用
  • 使用简单,用户只需要关注业务即可
  • 灵活,RPC调用的序列化方式可以自由定制,比如支持json,支持msgpack等方式

下面来分别解释这几条标准。

  • 标准1:真的像本地函数一样调用

    RPC的本质是为了屏蔽网络的细节和复杂性,提供易用的api,让用户就像调用本地函数一样实现远程调用,所以RPC最重要的就是“像调用本地函数一样”实现远程调用,完全不让用户感知到底层的网络。真正好用的RPC接口,他的调用形式是和本地函数无差别的,但是本地函数调用是灵活多变的。服务器如果提供和客户端完全一致的调用形式将是非常好用的,这也是RPC框架的一个巨大挑战

  • 标准2:使用简单,用户只需要关注业务即可

    RPC的使用简单直接,非常自然,就是和调用本地函数一样,不需要写一大堆额外代码,用户只用写业务逻辑代码,而不用关注框架的细节,其他的事情都由RPC框架完成。

  • 标准3:灵活,RPC调用的序列化方式可以自由定制

    RPC调用的数据格式支持多种编解码方式,比如一些通用的json格式、msgpack格式或者boost.serialization等格式,甚至支持用户自己定义的格式,这样使用起来才会更灵活。

RPC框架评估

下面根据这几个标准来评估一些国内外知名大公司的RPC框架,这些框架的用法在github的wiki中都有使用示例,使用示例代码均来自官方提供的例子。

谷歌gRPC

gRPC最近发布了1.0版本,他是谷歌公司用c++开发的一个RPC框架,并提供了多种客户端。

  • 协议定义

    先定义一个.proto的文件,例如
    
        // Obtains the feature at a given position.
        rpc GetFeature(Point) returns (Feature) {}
    定义了一个服务接口,接收客户端传过来的Point,返回一个Feature,接下来定义protocol buffer的消息类型,用于序列化/反序列化
    
        message Point {
          int32 latitude = 1;
          int32 longitude = 2;
        }
    
  • 服务器代码
    class RouteGuideImpl final : public RouteGuide::Service {
        Status GetFeature(ServerContext* context, const Point* point, Feature* feature) override {
              feature->set_name(GetFeatureName(*point, feature_list_));
              feature->mutable_location()->CopyFrom(*point);
              return Status::OK;
        }
    }
    
    void RunServer(const std::string& db_path) {
      std::string server_address("0.0.0.0:50051");
      RouteGuideImpl service(db_path);
    
      ServerBuilder builder;
      builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
      builder.RegisterService(&service);
      std::unique_ptr<Server> server(builder.BuildAndStart());
      std::cout << "Server listening on " << server_address << std::endl;
      server->Wait();
    }
    
  • 客户端代码
    bool GetOneFeature(const Point& point, Feature* feature) {
        ClientContext context;
        Status status = stub_->GetFeature(&context, point, feature);
        if (!status.ok()) {
          std::cout << "GetFeature rpc failed." << std::endl;
          return false;
        }
        if (!feature->has_location()) {
          std::cout << "Server returns incomplete feature." << std::endl;
          return false;
        }
    
        return true;
    }
    
  • 评价

    gRPC调用的序列化用的是protocal buffer,RPC服务接口需要在.proto文件中定义,使用稍显繁琐。根据标准1,gRPC并没有完全实现像本地调用一样,虽然很接近了,但做不到,原因是RPC接口中必须带一个Context的参数,并且返回类型必须是Status,这些限制导致gRPC无法做到像本地接口一样调用。

    根据标准2,gRPC的使用不算简单,需要关注诸多细节,比如Context和Status等框架的细节。根据标准3,gRPC只支持pb协议,无法扩展支持其他协议。

    综合评价:70分。

百度sofa-pbRPC

sofa-pbRPC是百度用c++开发的一个RPC框架,和gRPC有点类似,也是基于protocal buffer的,需要定义协议。

  • 协议定义

    // 定义请求消息

    message EchoRequest {

    required string message = 1;

    }

    // 定义回应消息
    message EchoResponse {
        required string message = 1;
    }
    
    // 定义RPC服务,可包含多个方法(这里只列出一个)
    service EchoServer {
        rpc Echo(EchoRequest) returns(EchoResponse);
    }
    
  • 服务器端代码
    #include <sofa/pbrpc/pbrpc.h>  // sofa-pbrpc头文件
    #include "echo_service.pb.h"   // service接口定义头文件
    class EchoServerImpl : public sofa::pbrpc::test::EchoServer
    {
    public:
        EchoServerImpl() {}
        virtual ~EchoServerImpl() {}
    
    private:
        virtual void Echo(google::protobuf::RpcController* controller,
                          const sofa::pbrpc::test::EchoRequest* request,
                          sofa::pbrpc::test::EchoResponse* response,
                          google::protobuf::Closure* done)
        {
            sofa::pbrpc::RpcController* cntl =
                static_cast<sofa::pbrpc::RpcController*>(controller);
            SLOG(NOTICE, "Echo(): request message from %s: %s",
                cntl->RemoteAddress().c_str(), request->message().c_str());
            response->set_message("echo message: " + request->message());
            done->Run();
        }
    };
    
    注意:
    
    服务完成后必须调用done->Run(),通知RPC系统服务完成,触发发送Response;
    在调了done->Run()之后,Echo的所有四个参数都不再能访问;
    done-Run()可以分派到其他线程中执行,以实现了真正的异步处理;
    
  • 客户端代码
    int main()
    {
        SOFA_PBRPC_SET_LOG_LEVEL(NOTICE);
    
        // 定义RpcClient对象,管理RPC的所有资源
        // 通常来说,一个client程序只需要一个RpcClient实例
        // 可以通过RpcClientOptions指定一些配置参数,譬如线程数、流控等
        sofa::pbrpc::RpcClientOptions client_options;
        client_options.work_thread_num = 8;
        sofa::pbrpc::RpcClient rpc_client(client_options);
    
        // 定义RpcChannel对象,代表一个消息通道,需传入Server端服务地址
        sofa::pbrpc::RpcChannel rpc_channel(&rpc_client, "127.0.0.1:12321");
    
        // 定义EchoServer服务的桩对象EchoServer_Stub,使用上面定义的消息通道传输数据
        sofa::pbrpc::test::EchoServer_Stub stub(&rpc_channel);
    
        // 定义和填充调用方法的请求消息
        sofa::pbrpc::test::EchoRequest request;
        request.set_message("Hello world!");
    
        // 定义方法的回应消息,会在调用返回后被填充
        sofa::pbrpc::test::EchoResponse response;
    
        // 定义RpcController对象,用于控制本次调用
        // 可以设置超时时间、压缩方式等;默认超时时间为10秒,默认压缩方式为无压缩
        sofa::pbrpc::RpcController controller;
        controller.SetTimeout(3000);
    
        // 发起调用,最后一个参数为NULL表示为同步调用
        stub.Echo(&controller, &request, &response, NULL);
    
        // 调用完成后,检查是否失败
        if (controller.Failed()) {
            // 调用失败后的错误处理,譬如可以进行重试
            SLOG(ERROR, "request failed: %s", controller.ErrorText().c_str());
        }
    
        return EXIT_SUCCESS;
    }
    
  • 评价

    sofa-pbRPC的使用并没有像sofa这个名字那样sofa,根据标准1,服务端的RPC接口比gRPC更加复杂,更加远离本地调用了。根据标准2,用户要做很多额外的事,需要关注框架的很多细节,比较难用。根据标准3,同样只支持pb协议,无法支持其他协议。

    综合评价:62分。

腾讯Pebble

腾讯开源的Pebble也是基于protocal buffer的,不过他的用法比gRPC和sofaRPC更好用,思路都是类似的,先定义协议。

  • 协议定义

    struct HeartBeatInfo {
      1: i64 id,
      2: i32 version = 1,
      3: string address,
      4: optional string comment,
    }
    
    service BaseService {
    
       i64 heartbeat(1:i64 id, 2:HeartBeatInfo data),
    
       oneway void log(1: string content)
    
    }
    
  • 服务器端代码
    class BaseServiceHandler : public BaseServiceCobSvIf {
    public:
    
        void log(const std::string& content) {
            std::cout << "receive request : log(" << content << ")" << std::endl;
        }
    };
    
    int main(int argc, char* argv[]) {
        // 初始化RPC
        pebble::rpc::Rpc* rpc = pebble::rpc::Rpc::Instance();
        rpc->Init("", 0, "");
    
        // 注册服务
        BaseServiceHandler base_service;
        rpc->RegisterService(&base_service);
    
        // 配置服务监听地址
        std::string listen_addr("tcp://127.0.0.1:");
        if (argc > 1) {
            listen_addr.append(argv[1]);
        } else {
            listen_addr.append("8200");
        }
    
        // 添加服务监听地址
        rpc->AddServiceManner(listen_addr, pebble::rpc::PROTOCOL_BINARY);
    
        // 启动server
        rpc->Serve();
    
        return 0;
    }
    
  • 客户端代码
    // 初始化RPC
    pebble::rpc::Rpc* rpc = pebble::rpc::Rpc::Instance();
    rpc->Init("", -1, "");
    
    // 创建rpc client stub
    BaseServiceClient client(service_url, pebble::rpc::PROTOCOL_BINARY);
    
    // 同步调用
    int ret = client.log("pebble simple test : log");
    std::cout << "sync call, ret = " << ret << std::endl;
    
  • 评价

    Pebble比gRPC和sofa-pbrpc更好用,根据标准1,调用方式和本地调用一致了,接口中没有任何限制。根据标准2,除了定义协议稍显繁琐之外已经比较易用了,不过服务器在使用上还是有一些限制,比如注册服务的时候只能注册一个类对象的指针,不能支持lambda表达式,std::function或者普通的function。根据标准3,gRPC只支持pb协议,无法扩展支持其他协议。

    综合评价:75分。

apache msgpack-RPC

msgpack-RPC是基于msgpack定义的RPC框架,不同于基于pb的RPC,他无需定义专门的协议。

  • 服务器端代码

    #include <jubatus/msgpack/rpc/server.h>
    
    class myserver : public msgpack::rpc::server::base {
    public:
        void add(msgpack::rpc::request req, int a1, int a2)
        {
            req.result(a1 + a2);
        }
    
    public:
        void dispatch(msgpack::rpc::request req)
        try {
            std::string method;
            req.method().convert(&method);
    
            if(method == "add") {
                msgpack::type::tuple<int, int> params;
                req.params().convert(&params);
                add(req, params.get<0>(), params.get<1>());
    
            } else {
                req.error(msgpack::rpc::NO_METHOD_ERROR);
            }
    
        } catch (msgpack::type_error& e) {
            req.error(msgpack::rpc::ARGUMENT_ERROR);
            return;
    
        } catch (std::exception& e) {
            req.error(std::string(e.what()));
            return;
        }
    };
    
  • 客户端代码
    #include <jubatus/msgpack/rpc/client.h>
    #include <iostream>
    
    int main(void)
    {
        msgpack::rpc::client c("127.0.0.1", 9090);
        int result = c.call("add", 1, 2).get<int>();
        std::cout << result << std::endl;
    }
    
  • 评价

    msgpack-RPC使用起来也很简单,不需要定义proto文件,根据标准1,客户端的调用和本地调用一致,不过,服务器的RPC接口有一个msgpack::rpc::request对象,并且也必须派生于base类,使用上有一定的限制。根据标准2,服务器端提供RPC服务的时候需要根据method的名字来dispatch,这种方式不符合开闭原则,使用起来有些不方便。根据标准3,msgpack-rpc只支持msgpack的序列化,不能支持其他的序列化方式。

    综合评价:80分。

总结

目前虽然国内外各大公司都推出了自己的RPC框架,但是真正好用易用的RPC框架却是不多的,这里对各个厂商的RPC框架仅从好用的角度做一个评价,一家之言,仅供参考,希望可以为大家做RPC的技术选型的时候提供一些评判依据。

时间: 2024-08-29 00:37:55

什么样的RPC才是好用的RPC的相关文章

自定义RPC的完整实现---深入理解rpc内部原理

倘若不使用RPC远端调用的情况下,代码如下: local.py # coding:utf-8 # 本地调用除法运算的形式 class InvalidOperation(Exception): def __init__(self, message = None): self.message = message or 'involid operation' def divide(num1, num2 = 1): if num2 == 0: raise InvalidOperation res = n

【Rpc】基于开源Dubbo分布式RPC服务框架的部署整合

一.前言 Dubbo 作为SOA服务化治理方案的核心框架,用于提高业务逻辑的复用.整合.集中管理,具有极高的可靠性(HA)和伸缩性,被应用于阿里巴巴各成员站点,同时在包括JD.当当在内的众多互联网项目中有着广泛应用.dubbo 通过高性能 RPC 实现服务的输出和输入功能,框架基于 Spring Framework 进行无缝集成,使用过程中基本看不到 Dubbo API的直接调用,Dubbo服务支持RMI.Hessian.Dubbo.WebService等众多通信协议,同时提供了对服务的监控和管

RPC是什么?为什么要学习RPC?

随着近几年分布式.微服务架构的火热,RPC在开发工作中使用的越来越多,也变的越来越重要. 今天我们来看RPC是什么,为什么要了解RPC,通过学习RPC我们能掌握什么内容? 什么是「RPC」 RPC 全称 Remote Procedure Call, wikipedia的部分说明: RPC is a request–response protocol. An RPC is initiated by the client , which sends a request message to a kn

基于netty轻量的高性能分布式RPC服务框架forest&lt;下篇&gt;

基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开发的RPC框架,除了常规的点对点调用外,Motan还提供服务治理功能,包括服务节点的自动发现.摘除.高可用和负载均衡等. 架构概述 Forest中分为服务提供方(RPC Server),服务调用方(RPC Client)和服务注册中心(Registry)三个角色. Server提供服务,向Registry注册

Hadoop RPC通信Client客户端的流程分析

Hadoop的RPC的通信与其他系统的RPC通信不太一样,作者针对Hadoop的使用特点,专门的设计了一套RPC框架,这套框架个人感觉还是有点小复杂的.所以我打算分成Client客户端和Server服务端2个模块做分析.如果你对RPC的整套流程已经非常了解的前提下,对于Hadoop的RPC,你也一定可以非常迅速的了解的.OK,下面切入正题. Hadoop的RPC的相关代码都在org.apache.hadoop.ipc的包下,首先RPC的通信必须遵守许多的协议,其中最最基本的协议即使如下: /**

细水长流Hadoop源码分析(3)RPC Server初始化构造

声明:个人原创,转载请注明出处.文中引用了一些网上或书里的资料,如有不妥之处请告之. 本文是我阅读Hadoop 0.20.2第二遍时写的笔记,在阅读过程中碰到很多问题,最终通过各种途径解决了大部分.Hadoop整个系统设计精良,源码值得学习分布式的同学们阅读,以后会将所有笔记一一贴出,希望能方便大家阅读源码,少走弯路. 目录 4 RPC服务器(org.apache.hadoop,ipc.Server) 4.1 服务器初始化 4 RPC服务器(org.apache.hadoop,ipc.Serve

远程过程调用(RPC)详解

原文同步至 http://waylau.com/remote-procedure-calls/ 本文介绍了什么是远程过程调用(RPC),RPC 有哪些常用的方法,RPC 经历了哪些发展阶段,以及比较了各种 RPC 技术的优劣. 什么是 RPC RPC 是远程过程调用(Remote Procedure Call)的缩写形式,Birrell 和 Nelson 在 1984 发表于 ACM Transactions on Computer Systems 的论文<Implementing remote

RPC服务简述

RPC=remote procedure call 计算机A可以跟调用本地方法一样调用远程计算机B的方法,省略了显示编码远程调用的细节 过程:user相当于一个client,当client调用远程方法时,user-stub将接口方法参数封装起来,通过特定的协议规范打包成rpc的协议数据由C端的RPCRuntime送到S端的RPCRuntime,s端接收到后,还是按照特定的协议规范解析,然后调用本地方法,得到结果后返回给Client RPC调用: 同步调用:客户端等待调用执行完并返回结果 异步调用

远程通信(RPC,Webservice,RMI,JMS、EJB、JNDI的区别)对比

总结这些概念都是易混淆,最基本概念定义复习和深入理解,同时也是架构师必备课程 RPC(Remote Procedure Call Protocol) RPC使用C/S方式,采用http协议,发送请求到服务器,等待服务器返回结果.这个请求包括一个参数集和一个文本集,通常形成"classname.methodname"形式.优点是跨语言跨平台,C端.S端有更大的独立性,缺点是不支持对象,不支持异步调用,无法在编译器检查错误,只能在运行期检查. Web Service Web Service