从RPC开始(一)

这是一篇关于纯C++RPC框架的文章。所以,我们先看看,我们有什么?

  1、一个什么都能干的C++。(前提是,你什么都干了)

  2、原始的Socket接口,还是C API。还得自己去二次封装...

  3、C++11,这是最令人兴奋的。有了它,才能够有这篇文章;否则,CORBA之类的才是唯一的选择。(因为需要代码生成)

那么,我们没有什么,或者需要什么?

  1、一套完善的序列化框架;在不同的进程间传输数据,序列化是第一步,如何可靠且方便地将对象转化为二进制(或者其他格式),在对端则是如何正确且安全地将其从二进制恢复为对象。

  2、完善的底层通信协议;其需要提供合适的语义抽象:服务端支持怎样的并发,是单客户单访问,还是多访问;而客户端的并发模型由服务端决定。当然,还需要健壮且足够的接口抽象,毕竟分布式环境,“一切皆有可能”,需要应对各种问题。

  3、一个可用的反射系统。是的,需要在C++环境下建立一个反射系统。这一步是最为关键的,其由C++11支持。因为,我们需要注册一个类的各种信息,以供RPC调用。

当然,本文是提纲式的;也就是,在这里暂时只会讲【目标】——一个可用的C++RPC所需要的全部组件。而更详细的内容,即如何构建这些组件,将由后续篇章回答。

一、序列化

  在分布式环境下,序列化是永远绕不过的一个坎;而作为一个支撑性组件,其所需要提供不只是“可用”,而是易用且安全!安全!重要的事说两遍。我们不能够约定从网络传来的内容,代表了什么;而是其本身必须带有信息,告诉我们它是什么。

  在纯C++环境下,要从零开始构建一个序列化框架,是困难的。毕竟语言本身并没有提供什么保证。所幸的是,C++并非完全没有提供,只是需要我们去发掘。template,模板可以给我们所需要的一切;C++中最令人兴奋的部分,其提供了另一个简单且可靠的扩展模型:通过特化模板,能够提供类似面向对象的“接口”。当然,需要的还不止这些,我们需要更强大的工具:模板元编程,由其来提供所需要的类型信息(在我的系统中有两种扩展方式:特化模板和成员函数,后者需要类型信息)。

  下面将是我们需要完成的目标:

Buffer buffer;
Object obj1;
//序列化
Serialize(&buffer, obj1);

Object obj2;
//反序列化
Deserialize(&buffer, obj2);

  其中的buffer,便是我们的成果;其以二进制保存了对象。当然,还有许多的细节需要补充。

二、通信

  UDP还是TCP,这是一个需要纠结的问题。但我们的关注点并不在这,我们需要一个完善的、健壮的通信组件。为了完成这个目标,需要付出一些努力:并发模型、中断与超时通知。前者是整个RPC框架的语义保证,我们需要保证调用语义的完整性:客户端的并发访问请求,到了服务端不能变成串行请求;而普通的通信协议,并不能够直接提供这点,我选择了从零开始构建。中断与超时,是整个调用语义,所面临的最大挑战;所以,我们需要合适的通知,以决定如何进行下一步操作。

  当然,UDP与TCP,依旧是一个重要的话题;我的实现,提供了二者的完整实现,所需的只是,选择。

三、反射

  在C++的世界里,讲这个话题是不合时宜的。毕竟,无论怎样的努力,在一个没有完整运行时环境的语言里,“反射”这样奢侈的东西,只是一个梦想。但,我们并不是想要重新打造一门语言,而仅仅是给我们的RPC提供一些服务而已。

  在RPC的世界里讲“反射”并不是一件突兀的事。看一看,各种没有IDL的RPC在Java里的实现,无一不是使用了反射这一技术。是的,我的RPC里并没有IDL,我并不打算再创建一门语言(我有一个脚本语言),哪怕只是一个DSL(领域特定语言)。为了“封装”类型的各种信息:构造函数、析构函数以及一些我们需要的方法。反射,提供了最合适的语义。

  我的RPC是非侵入式的,在不改变任何原有的代码的前提下,提供相应的RPC调用。而这一“调用”通过反射来实现。

四、RPC

  前面三大组件,和我的RPC并没有什么必然的联系。但没有这三个组件,我的RPC框架,将不复存在。所以,最后再来讲讲我们的主角:RPC。

  其实,RMI(远程方法调用)更合适一点。我的RPC提供了3中“对象”:

    1、由单一客户端独占的,非共享对象;只有该客户端能够访问该对象,其创建和析构,都由一个客户端发起和执行。

    2、服务对象,这在服务端本身是一个“服务”;也就是其本身是一个单例,因此,所有客户都是直接在一个对象上,执行各种请求;该对象的构造和析构,可由服务端的本地服务管理框架(我的另一个东西)管理。这个方式,提供了“服务化”的最佳语义;当然,也就需要面对并发。

    3、注册对象,其也是全局可访问的,所有客户端都可以发送请求;不同的是,其是由服务端的某段代码显示注册的,该对象的所有权也由其持有;客户端只能访问其接口。一旦服务端取消了注册,便也消失在所有的客户端眼前,并且是可能在任何时刻取消注册,即使有请求还在路上(当然,有请求正在执行,需要等待其执行完成)。

  还有一个重要的事情,我只提供了,同步访问;因为,我有一个分布式消息系统,可提供异步服务(这只是托词,懒,而已)。

  其实,在拥有了前面的三个组件后,RPC本身便只剩下,需不需要构建,而不是如何。这样,我们也就可以将重心转向RPC的语义保证上了。一般有三种语义可以提供:至少一次、至多一次,可能一次。分别代表如下3中结果:

    1、该请求被执行,因为我们将重复地发送请求,直到答复。也就意味着,该请求可能被重复执行多次。

    2、该请求被执行,我们会一直发送重复的请求,直到答复。但服务端,将识别这些重复的请求,并只会执行一次。

    3、该请求被发送,但我们并不保证被执行;因为,我们没有等待答复。

  对于通用的RPC,除了“至多一次”,并没有其他的选择。当然,为了完成这点,需要一些努力,所幸并不困难。我的RPC正是提供这样的调用语义。

  我们将要构建的RPC并没有使用IDL。所以,客户端的访问,将会是显示的RPC调用;而非是其他框架所宣传的,能够完全屏蔽本地和分布,但我们是在C++的环境下,要完全屏蔽,是不可能的;所以,我所提供的调用方式,并没有排除原始的方案。其与本地调用的不同是:需要统一从一个RPC客户端对象中传入所需要的对象名字,以及在方法调用时,需要一个额外的方法名字。

  比如:

RPCClient client("Something");
int val = client.Invoke("GetValue", 12, "I Want ...").To<int>();

  当然,如果你不习惯这样的方式(你会习惯的);我还提供了类似IDL的功能:代码生成。其能够将上面的显示访问,封装起来,提供和本地调用完全一样的方式:

#include "Generated\\Something.h"//这个头文件是生成的

Something obj;
int val = obj.GetValue(12, "I Want ...");

  而在服务端则,使用如下方式提供远程对象:

Class<Somthing>* type = GetServiceAs<ReflectionService>()->Register<Something>();//也可以重新命名:Register<Something>("OtherName");
type->AddMethod("GetValue", &Somthing::GetValue);//也可注册重载函数(方式后面再讲)

  总之,本篇,完。

  PS:如需,请期待下一篇——序列化。

  PS:以及,下下篇——通信。

  PS:还有,下下下篇——反射。

时间: 2025-01-31 03:54:50

从RPC开始(一)的相关文章

下载-深入浅出Netty源码剖析、Netty实战高性能分布式RPC、NIO+Netty5各种RPC架构实战演练三部曲视频教程

下载-深入浅出Netty源码剖析.Netty实战高性能分布式RPC.NIO+Netty5各种RPC架构实战演练三部曲视频教程 第一部分:入浅出Netty源码剖析 第二部分:Netty实战高性能分布式RPC 第三部分:NIO+Netty5各种RPC架构实战演练

网络编程 -- RPC实现原理 -- RPC -- 迭代版本V1 -- 本地方法调用

网络编程 -- RPC实现原理 -- 目录 啦啦啦 V2--RPC -- 本地方法调用:不通过网络 入门 1. RPCObjectProxy rpcObjectProxy = new RPCObjectProxy(new LocalRPCClient()); : 绑定目标对象 2. IUserService userService = (IUserService) rpcObjectProxy.create(IUserService.class); :返回代理类 3. List<User> u

Spring远程服务(RPC)

Spring支持几种不同的RPC模型,包括远程方法调用(RMI).Caucho的Hessian和Burlap和Spring自带的HTTP invoker.如下: 无论选择哪一种RPC模型,我们都会发现Spring对每一种模型都提供了风格一致的支持.在所有的模型中,服务都作为Spring所管理的Bean配置到我们的应用中.这是采用一个代理工厂Bean实现的,这个Bean能够像本地对象一样将远程服务装配到其他Bean的属性中去.它的工作原理如下:

一个入门rpc框架的学习

一个入门rpc框架的学习 参考 huangyong-rpc 轻量级分布式RPC框架 该程序是一个短连接的rpc实现 简介 RPC,即 Remote Procedure Call(远程过程调用),说得通俗一点就是:调用远程计算机上的服务,就像调用本地服务一样. RPC 可基于 HTTP 或 TCP 协议,Web Service 就是基于 HTTP 协议的 RPC, 它具有良好的跨平台性,但其性能却不如基于 TCP 协议的 RPC.会两方面会直接影响 RPC 的性能,一是传输方式,二是序列化. 众所

RPC框架性能基本比较测试

gRPC是Google最近公布的开源软件,基于最新的HTTP2.0协议,并支持常见的众多编程语言. 我们知道HTTP2.0是基于二进制的HTTP协议升级版本,目前各大浏览器都在快马加鞭的加以支持. 我们可以设想一下,未来浏览器支持HTTP2.0,并通过现有开源序列化库比如protobuf等,可以直接和各种语言的服务进行高效交互,这将是多么“美好”的场景! gPRC的Java实现底层网络库是Netty,而且是用到最新的Netty5.0.0.Alpha3的开发版本,因为最新版本针对HTTP/2做了很

python通过protobuf实现rpc

由于项目组现在用的rpc是基于google protobuf rpc协议实现的,所以花了点时间了解下protobuf rpc.rpc对于做分布式系统的人来说肯定不陌生,对于rpc不了解的童鞋可以自行google,这里只是做个简单的介绍.rpc的主要功能是让分布式系统的实现更为简单,为提供强大的远程调用而不损失本地调用语义的简洁性.为了实现这个目标,rpc框架需要提供一种透明调用机制让使用者不必显示区分本地调用还是远程调用.rpc架构涉及的组件如下: 客户方像调用本地方法一样去调用远程接口方法,R

基于protobuf的RPC实现

可以对照使用google protobuf RPC实现echo service一文看,细节本文不再描述. google protobuf只负责消息的打包和解包,并不包含RPC的实现,但其包含了RPC的定义.假设有下面的RPC定义: service MyService { rpc Echo(EchoReqMsg) returns(EchoRespMsg) } 那么要实现这个RPC需要最少做哪些事?总结起来需要完成以下几步: 客户端 RPC客户端需要实现google::protobuf::RpcCh

腾讯正式对外开源高性能 RPC 开发框架与微服务平台Tars

Tars 是将腾讯内部使用的微服务架构 TAF(Total Application Framework)多年的实践成果总结而成的开源项目,目前已于4月10日正式对外开源. 作为支持多语言的高性能 RPC 开发框架和配套一体化的服务治理平台,Tars可以帮助企业或者用户以微服务的方式快速构建稳定可靠的分布式应用,它的设计灵感来源于采取分层思想,实现开发与运营之间的分离.目前该框架在腾讯内部,已经在 160 多个业务(如手机浏览器.应用宝.手机管家.手机QQ.手机游戏等).1.6 多万台服务器上运行

Avro实现RPC

场景:一个客户端,一个服务端(创建两个avro工程).客户端向服务端发送数据,服务端根据算法算出结果,返回给客户端. Http主外,RPC主内.(解决分布式环境下,节点间的数据通信或远程过程调用) 实现步骤 1.创建两个maven工程 2.引入pom文件 3.更改maven工程结构(src/main/avro) 4.创建模式文件(协议文件) 5.根据avro插件生成文件对应的接口类 6.利用API实现rpc 具体实现: 1. 创建两个maven项目,修改jdk版本和编译的版本  1.5->1.7

RPC的发展历史(本质就是双方定义好协议,传递参数后远程调用)

服务器通讯原理就是一台socket服务器A,另一台socket客户端B,现在如果要通讯的话直接以流方式写入或读出. 这样能实现通讯,但有个问题.如何知道更多信息?比如需要发送流大小,编码,Ip等. 这样就有了协议,协议就是规范,就是发送的流中携带了很多的内容. RPC的实现就是一种规范.可参考http://javatar.iteye.com/blog/1123915 这个简单RPC实现. RPC(远程过程调用)是什么 简单的说,RPC就是从一台机器(客户端)上通过参数传递的方式调用另一台机器(服