现在RPC框架很多,但是真正好用的RPC却是少之又少。那么什么是好用的RPC,什么是不好用的RPC呢,有一个评判标准吗?下面是我列举出来的衡量RPC好用与否的几条标准:
引用
真的像本地函数一样调用
使用简单,用户只需要关注业务即可
灵活,RPC调用的序列化方式可以自由定制,比如支持json,支持msgpack等方式
使用简单,用户只需要关注业务即可
灵活,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!");
- // 可以通过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(¶ms);
- 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(Remote Procedure Call)是一种计算机通信协议,它允许程序在分布式环境中的一个系统上执行另一系统上的函数或方法,就像是本地调用一样。这个过程涉及到了客户端、服务器端和服务调用的封装,使得开发者无需...
### REST与RPC的区别详解 #### 一、概述 在现代软件开发中,特别是分布式系统设计领域,REST(Representational State Transfer)与RPC(Remote Procedure Call)是两种非常重要的服务调用方式。这两种方法各有...
RPC(Remote Procedure Call)协议是计算机通信中的一种技术,它允许程序通过网络调用远程机器上的函数或方法,就像调用本地函数一样简单。RPC协议的核心思想是隐藏底层的网络通信细节,使得开发者能专注于应用逻辑...
**Go-rpcx:分布式RPC服务框架的崭新选择** Go-rpcx 是一个高度可扩展的、功能丰富的分布式RPC(远程过程调用)服务框架,它的设计灵感来源于阿里巴巴的Dubbo和微博的Motan。这个框架是用Golang语言构建的,利用了...
在性能基础之浅谈常见接口性能压测一文中我们有简单介绍常见的RPC接口,本文将单篇详细介绍RPC框架。RPC(RemoteProcedureCall)—远程过程调用,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络...
Finagle 是 JVM 的可扩展 RPC 系统,用于构建高并发服务器。 Finagle 为多种协议实现了统一的客户端和服务器 API,并且专为高性能和并发而设计。 Finagle 的大部分代码与协议无关,简化了新协议的实现。 Finagle 是...
ipfs服务提供商 这是一个“样板”存储库... REST API的框架身份验证(护照和JWT) 数据库(猫鼬) 测试(摩卡) 使用apidoc生成文档使用标准棉绒打包为Docker容器 JSON RPC,用于通过IPFS镜像REST API要求节点^ 10.15.1
什么是DelphiMVCFramework DMVCFramework是一个非常流行的Delphi框架,它为Delphi开发人员提供了易于使用,可伸缩,灵活的RESTful , JSON-RPC和ActiveRecord框架。 DMVCFramework是GitHub上最受欢迎的Delphi项目,...
5. **RPC**(Remote Procedure Call):RPC允许一个程序调用另一个在不同地址空间运行的程序,通常跨越网络。asio2提供了RPC的支持,使分布式系统间的通信变得更加简单。 6. **SSL**(Secure Sockets Layer)/**TLS...
最基本的操作就是对一个URL流执行println,不过这要求您理解太多有关SOAP协议的知识,并且要知道某个服务期望收到的SOAP消息是什么样,发出的又是什么样。我们无法很方便地从服务本身获得这些信息,因此这种方法的可...
材料分析(XRD)实验报告 根据提供的文件信息,我们可以生成以下知识点: 一、X 射线衍射(XRD)实验概述 * X 射线衍射是利用X射线在晶体中的衍射现象来研究晶体结构的技术。 * XRD 实验的主要目的是了解 X 射线...
Remo 是一个快速且简单的 Java RPC 库,不会妨碍您。 启动和运行一个简单的服务不需要任何配置文件和最少的样板文件。 基本用法 首先,为您要提供的服务创建一个接口。 这个接口是描述服务期望从客户端得到什么以及...
Apache Dubbo项目 Apache Dubbo是一个基于Java的高性能开源RPC框架。 请访问以获取快速入门和文档,以及访问的新闻,常见问题解答和发行说明。 我们现在正在收集Dubbo用户... 我们建议按照以下说明引用该目录中的样
gRPC的代码格式是什么样的?支持定义默认值吗?定义数组的关键字是什么? 除了gRPC你还用过哪些RPC技术栈,你所知道的RPC框架有哪些? QUIC相对于HTTP2有哪些重大变化? Python 和 Go 的内存管理区别 ......
本章节主要聚焦于中性样品的反相色谱(Reversed-Phase Chromatography,RPC)与正相色谱(Normal-Phase Chromatography,NPC)的应用。 反相色谱是HPLC中最常用的分离模式,尤其适用于大多数常规样品。RPC的分离...
允许您的Dapp使用JSON-RPC与以太坊网络(及其上部署的智能合约)进行交互。 在我们的示例中,我们使用Truffle框架,该框架是用于简化某些过程并生成样板代码的工具集。 主题背景 Moesif是领先的API分析和监视平台,...
所有服务器多传输和多协议的简单RPC服务器和(可选)客户端。 无样板。 自以为是。 简约的。 DX优先。 考虑使用完全相同的客户端和服务器代码的HTTP,gRPC,GraphQL,WebSocket,Lambda,进程间,Unix套接字等远程...