什么样的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[]) {// 初始化RPCpebble::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);// 启动serverrpc->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的技术选型的时候提供一些评判依据。

什么样的RPC才是好用的RPC相关推荐

  1. 什么样的 RPC 才是好用的 RPC

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

  2. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成...

    原文:NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成 本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博 ...

  3. c++socket多个客户端通过不同端口与一个服务端通信_手写RPC,深入底层理解整个RPC通信...

    一.前言 RPC,远程过程调用,调用远程方法像调用本地方法一样.RPC交互分为客户端和服务端,客户端调用服务端方法,服务端接收数据并打印到控制台,并response响应给客户端. RPC和HTTP的联 ...

  4. 【RPC Dubbo】各大开源rpc 框架 比较(dubbo支持的各种协议)

    文章目录 1. 前言 2. 服务 2.1 为什么要做服务 2.2 服务带来的挑战 2.3 服务未来的趋势 3. 框架 3.1 服务框架对比 3.1.1 Dubbo 3.1.2 Dubbox 3.1.3 ...

  5. java基础巩固-宇宙第一AiYWM:为了维持生计,手写RPC~Version07(RPC原理、序列化框架们、网络协议框架们 、RPC 能帮助我们做什么呢、RPC异常排查:ctrl+F搜超时)整起

    上次Version06说到了咱们手写迷你版RPC的大体流程, 对咱们的迷你版RPC的大体流程再做几点补充: 为什么要封装网络协议,别人说封装好咱们就要封装?Java有这个特性那咱就要用?好像是这样.看 ...

  6. RPC 笔记(01)— RPC概念、调用流程、RPC 与 Restful API 区别

    1. 基本概念 PRC 远程过程调用 Remote Procedure Call,其就是一个节点请求另外一个节点提供的服务.当两个物理分离的子系统需要建立逻辑上的关联时,RPC 是牵线搭桥的常见技术手 ...

  7. 一周一论文(翻译 总结)— [SOCC 14] DaRPC: Data Center RPC 基于RDMA的高性能通信RPC

    目录 Abstract 1. Introduction 2. Motivation 3. Background 4. Design of DaRPC 4.1 Single Client-Server ...

  8. 整合rpc远程调用_远程过程调用(RPC)

    分布式系统的主要特点是能够将一台机器上的一个任务分解到系统中其他的机器上运行,实现多个CPU的协同工作.远程过程调用RPC就是实现这一特点的有效方法之一 1.什么是RPC RPC的基本思想 (1984 ...

  9. 【手撸RPC框架】SpringBoot+Netty4实现RPC框架

    [手撸RPC框架]SpringBoot+Netty4实现RPC框架 线程模型1:传统阻塞 I/O 服务模型 模型特点: 采用阻塞IO模式获取输入的数据 每个链接都需要独立的线程完成数据的输入,业务处理 ...

最新文章

  1. 【行业报告】基于社交图谱关系的反欺诈产品应用——青云
  2. 玩转产品排期:让小伙伴们高效协作
  3. SQL Server 2005两本教程案例数据库
  4. ws2812b灯带容易坏_吊灯吸顶灯筒灯壁灯射灯灯带LED线条灯……都是个啥?
  5. python程序详细描述_如何逐行描述Python代码?
  6. 客户端显示服务器图片不显示,客户端请求服务器图片不显示
  7. Orion:谷歌的新一代SDN控制器
  8. 马老师 生产环境mysql主从复制、架构优化方案
  9. java猜拳论文_Java人机猜拳实现的思路及方法实例
  10. 面试官:说一下Jena推理
  11. 添加CAB最大压缩到右键菜单
  12. 那些“不顾一切”要搞开源的人,现在怎样了?
  13. python 除数总是提示为0_Python错误的处理方法
  14. OpenCV入门: Mat数据类型及其转换,访问
  15. silverlight创建新的控件——一个简单的弹出菜单
  16. Lua 可变参数之arg与select
  17. 为什么正定矩阵等于转置_正定矩阵的定义性质-正定矩阵的判定方法-正定矩阵转置和本身...
  18. ElasticSearch创建索引指定分片和副本
  19. c++实验6-【项目3:矩阵求和】
  20. glassfish mysql datasource_GlassFish连接池(GlassFish connection pool)

热门文章

  1. 认识计算机系统学反思,认识计算机学设计及反思.doc
  2. 主成分分析 PCA 应用实例 鸢尾花数据集
  3. 自学-ES6篇-Promise对象
  4. DICOM3.0中的VR相关介绍
  5. Impinj英频杰超高频Indy R2000开发资料代码
  6. [Linux C编程]Linux文件锁
  7. 纳芯微全新驱动器NSi66x1A/NSi6601M发布
  8. oracle planning bom,OracleERP表结构--BOM模块
  9. Redis 网络模型 -- 阻塞非阻塞IO、IO多路复用、epoll详解
  10. 小学信息说课稿范文认识计算机,小学信息技术说课稿《认识计算机》-20210410201013.docx-原创力文档...