后台技术gRPC基础概念详解

本文介绍gRPC的基础概念。首先通过关系图直观展示这些基础概念之间关联,介绍异步gRPC的Server和Client的逻辑;然后介绍RPC的类型,阅读和抓包分析gRPC的通信过程协议,gRPC上下文;最后分析grpc.pb.h文件的内容,包括Stub的能力、Service的种类以及与核心库的关系。

之所以谓之基础,是这些内容基本不涉及gRPCCore的内容。

上图中列出了gRPC基础概念及其关系图。其中包括:Service(定义)、RPC、API、Client、Stub、Channel、Server、Service(实现)、ServiceBuilder等。

接下来,以官方提供的example/helloworld为例进行说明。

.proto文件定义了服务Greeter和APISayHello:

classGreeterClient是Client,是对Stub封装;通过Stub可以真正的调用RPC请求。

Channel提供一个与特定gRPCserver的主机和端口建立的连接。

Stub就是在Channel的基础上创建而成的。

Server端需要实现对应的RPC,所有的RPC组成了Service:

Server的创建需要一个Builder,添加上监听的地址和端口,注册上该端口上绑定的服务,最后构建出Server并启动:

RPC和API的区别:RPC(RemoteProcedureCall)是一次远程过程调用的整个动作,而API(ApplicationProgrammingInterface)是不同语言在实现RPC中的具体接口。一个RPC可能对应多种API,比如同步的、异步的、回调的。一次RPC是对某个API的一次调用,比如:

不管是哪种类型RPC,都是由Client发起请求。

Client看文档可以理解,但Server的代码复杂,文档和注释中的解释并不是很好理解,接下来会多做一些解释。

1.异步Client

greeter_async_client.cc中是异步Client的Demo,其中只有一次请求,逻辑简单。

stub_->PrepareAsyncSayHello()+rpc->StartCall()

stub_->AsyncSayHello()

2.异步Server

RequestSayHello()这个函数没有任何的说明。只说是:"we*request*thatthesystemstartprocessingSayHellorequests."也没有说跟cq_->Next(&tag,&ok);的关系。我这里通过加上一些日志打印,来更清晰的展示Server的逻辑:

上边绿色的部分为创建的第一个CallData对象地址,橙色的为第二个CallData的地址。

传入ServerContextctx_

传入HelloRequestrequest_

传入ServerAsyncResponseWriterresponder_

传入ServerCompletionQueue*cq_

将对象自身的地址作为tag传入

该动作,能将事件加入事件循环,可以在CompletionQueue中等待

创建新的CallData对象以接收新请求

处理消息体并设置reply_

将状态设置为FINISH

调用responder_.Finish()将返回发送给客户端

该动作,能将事件加入到事件循环,可以在CompletionQueue中等待

3.关系图

将上边的异步Client和异步Server的逻辑通过关系图进行展示。右侧RPC为创建的对象中的内存容,左侧使用相同颜色的小块进行代替。

以下CallData并非gRPC中的概念,而是异步Server在实现过程中为了方便进行的封装,其中的Status也是在异步调用过程中自定义的、用于转移的状态。

4.异步Client2

在example/cpp/helloworld中还有另外一个异步Client,对应文件名为greeter_async_client2.cc。这个例子中使用了两个线程去分别进行发送请求和处理返回,一个线程批量发出100个SayHello的请求,另外一个不断的通过cq_.Next()来等待返回。

无论是Client还是Server,在以异步方式进行处理时,都要预先分配好一定的内存/对象,以存储异步的请求或返回。

5.回调方式的异步调用

使用回调方式简介明了,结构上与同步方式相差不多,但是并发有本质的区别。可以通过文件对比,来查看其中的差异。

其实,回调方式的异步调用属于实验性质的,不建议直接在生产环境使用,这里也只做简单的介绍:

5.1回调Client

发送单个请求,在调用SayHello时,除了传入Request、Reply的地址之外,还需要传入一个接收Status的回调函数。

例子中只有一个请求,因此在SayHello之后,就直接通过condition_variable的wait函数等待回调结束,然后进行后续处理。这样其实不能进行并发,跟同步请求差别不大。如果要进行大规模的并发,还是需要使用额外的对象进行封装一下。

5.2回调Server

与同步Server不同的是:

可以按照Client和Server一次发送/返回的是单个消息还是多个消息,将gRPC分为:

1.Server对RPC的实现

Server需要实现proto中定义的RPC,每种RPC的实现都需要将ServerContext作为参数输入。

如果是一元(Unary)RPC调用,则像调用普通函数一样。将Request和Reply的对象地址作为参数传入,函数中将根据Request的内容,在Reply的地址上写上对应的返回内容。

如果涉及到流,则会用Reader或/和Writer作为参数,读取流内容。如ServerStream模式下,只有Server端产生流,这时对应的Server返回内容,需要使用作为参数传入的ServerWriter。这类似于以'w'打开一个文件,持续的往里写内容,直到没有内容可写关闭。

另一方面,Client来的流,Server需要使用一个ServerReader来接收。这类似于打开一个文件,读其中的内容,直到读到EOF为止类似。

如果Client和Server都使用流,也就是Bidirectional-Stream模式下,输入参数除了ServerContext之外,只有一个ServerReaderWriter指针。通过该指针,既能读Client来的流,又能写Server产生的流。

例子中,Server不断地从stream中读,读到了就将对应的写过写到stream中,直到客户端告知结束;Server处理完所有数据之后,直接返回状态码即可。

2.Client对RPC的调用

Client在调用一元(Unary)RPC时,像调用普通函数一样,除了传入ClientContext之外,将Request和Response的地址,返回的是RPC状态:

Client在调用ServerStreamRPC时,不会得到状态,而是返回一个ClientReader的指针:

Reader通过不断的Read(),来不断的读取流,结束时Read()会返回false;通过调用Finish()来读取返回状态。

调用ClientStreamRPC时,则会返回一个ClientWriter指针:

Writer会不断的调用Write()函数将流中的消息发出;发送完成后调用WriteDone()来说明发送完毕;调用Finish()来等待对端发送状态。

而双向流的RPC时,会返回ClientReaderWriter,:

前面说明了Reader和Writer读取和发送完成的函数调用。因为RPC都是Client请求而后Server响应,双向流也是要Client先发送完自己流,才有Server才可能结束RPC。所以对于双向流的结束过程是:

示例中创建了单独的一个线程去发送请求流,在主线程中读返回流,实现了一定程度上的并发。

3.流是会结束的

并不似长连接,建立上之后就一直保持,有消息的时候发送。(是否有通过建立一个流RPC建立推送机制?)

Server并没有像Client一样调用WriteDone(),而是在消息之后,将statuscode、可选的statusmessage、可选的trailingmetadata追加进行发送,这就意味着流结束了。

本节通过介绍gRPC协议文档描述和对helloworld的抓包,来说明gRPC到底是如何传输的。

1.ABNF语法

2.请求协议

*表示element会重复多次(最少0次)。知道这个就能理解概况里的描述了:

这表示Request是由3部分组成,首先是Request-Headers,接下来是可能多次出现的Length-Prefixed-Message,最后以一个EOS结尾(EOS表示End-Of-Stream)。

2.1Request-Headers

根据上边的协议描述,Request-Headers是由一个Call-Definition和若干Custom-Metadata组成。

[]表示最多出现一次,比如Call-Definition有很多组成部分,其中Message-Type等是选填的:

通过Wireshark抓包可以看到请求的Call-Definition中共有所有要求的Header,还有额外可选的,比如user-agent:

因为helloworld的示例比较简单,请求中没有填写自定义的元数据(Custom-Metadata)

2.2Length-Prefixed-Message

传输的Length-Prefixed-Message也分为三部分:

同样的,Wireshark抓到的请求中也有这部分信息,并且设置.proto文件的搜索路径之后可以自动解析PB:

其中第一个红框(Compressed-Flag)表示不进行压缩,第二个红框(Message-Length)表示消息长度为7,蓝色反选部分则是Protobuf序列化的二进制内容,也就是Message。

这里Length-Prefixed-Message中传输的可以是PB也可以是JSON,须通过Content-Type头中描述告知。

2.3EOS

End-Of-Stream并没有单独的数据去描述,而是通过HTTP2的数据帧上带一个END_STREAM的flag来标识的。比如helloworld中请求的数据帧,也携带了END_STREAM的标签:

3.返回协议

()表示括号中的内容作为单个元素对待,/表示前后两个元素可选其一。Response的定义说明,可以有两种返回形式,一种是消息头、消息体、Trailer,另外一种是只带Trailer:

这里需要区分gRPC的Status和HTTP的Status两种状态。

不管是哪种形式,最后一部分都是Trailers,其中包含了gRPC的状态码、状态信息和额外的自定义元数据。

同样地,使用END_STREAM的flag标识最后Trailer的结束。

4.与HTTP/2的关系

ThelibrariesinthisrepositoryprovideaconcreteimplemnetationofthegRPCprotocol,layeredoverHTTP/2.

gRPC支持上下文的传递,其主要用途有:

客户端添加自定义的metadatakey-value对没有特别的区分,而服务端添加的,则有inital和trailing两种metadata的区分。这也分别对应这ClientContext只有一个添加Metadata的函数:

而ServerContext则有两个:

还有一种CallbackServer对应的上下文叫做CallbackServerContext,它与ServerContext继承自同一个基类,功能基本上相同。区别在于:

1.Stub

.proto中的一个service只有一个Stub,该类中会提供对应每个RPC所有的同步、异步、回调等方式的函数都包含在该类中,而该类继承自接口类StubInterface。

为什么需要一个StubInterface来让Stub继承,而不是直接产生Stub?别的复杂的proto会有多个Stub继承同一个StubInterface的情况?不会,因为每个RPC对应的函数名是不同。

Greeter中唯一一个函数是用于创建Stub的静态函数NewStub:

Stub中同步、异步方式的函数是直接作为Stub的成员函数提供,比如针对一元调用:

回调方式的RPC调用是通过一个experimental_async的类进行了封装(有个async_stub_的成员变量),所以回调Client中提到,回调的调用方式用法是stub_->async()->SayHello(...)。

experimental_async类定义中将Stub类作为自己的友元,自己的成员可以被Stub直接访问,而在StubInterface中也对应有一个experimental_async_interface的接口类,规定了要实现哪些接口。

2.Service

有几个概念都叫Service:proto文件中RPC的集合、proto文件中service产生源文件中的Greeter::Service类、gRPC框架中的::grpc::Service类。本小节说的Service就是helloworld.grpc.pb.h中的Greeter::Service。

2.1Service是如何定义的

helloworld.grpc.pb.h文件中共定义了7种Service,拿出最常用的Service和AsyncService两个定义来说明下Service的定义过程:通过类模板链式继承。

Service跟其他几种Service不同,直接继承自grpc::Service,而其他的Service都是由类模板构造出来的,而且使用类模板进行嵌套,最基础的类就是这里的Service。

Service有以下特点:

所以Service类中的所有RPCAPI都是同步的。

再看AsyncService的具体定义:

所以AsyncService的含义就是继承自Service,加上了WithAsyncMethod_SayHello的新功能:

通过gRPC提供的route_guide.proto例子能更明显的理解这点:

这里RouteGuide服务中有4个RPC,GetFeature、ListFeatures、RecordRoute、RouteChat,通过4个WithAsyncMethod_{RPC_name}的类模板嵌套,能将4个API都设置成ApiType::ASYNC、添加上对应的RequestXXX()函数、禁用同步函数。

2.2Service的种类

helloworld.grpc.pb.h文件中7种Service中,有3对Service的真正含义都相同(出于什么目的使用不同的名称?),实际只剩下4种Service。前三种在前边的同步、异步、回调Server的介绍中都有涉及。

其实这些不同类型的Service是跟前边提到的api_type_有关。使用不同的::grpc::Service::MarkMethodXXX设置不同的ApiType会产生不同的API模板类,所有API模板类级联起来,就得到了不同的Service。这三者的关系简单列举如下:

另外还有两种模板是通过设置其他属性产生的,这里暂时不做介绍:

3.与::grpc核心库的关系

Stub类中主要是用到gRPCChannel和不同类型RPC对应的方法实现

Service类则继承自::grpc::Service具备其父类的能力,需要自己实现一些RPC方法具体的处理逻辑。其它Service涉及到gRPC核心库的联系有:

AsyncService::RequestSayHello()调用::grpc::Service::RequestAsyncUnary。

CallbackService::SayHello()函数返回的是::grpc::ServerUnaryReactor指针。

CallbackService::SetMessageAllocatorFor_SayHello()函数中调用

::grpc::internal::CallbackUnaryHandler::SetMessageAllocator()函数设置RPC方法的回调的消息分配器。

THE END
1.java多人协作文档开源mob64ca12d9b014的技术博客如何实现一个Java多人协作文档开源项目 作为一名新手开发者,创建一个Java多人协作文档的开源项目可能听起来有点复杂。但是,通过循序渐进的方式,我们可以把这个项目拆分为几个简单的步骤。本文将带你了解该项目的不同阶段,以及每一步所需的代码及其注释。 https://blog.51cto.com/u_16213338/13230058
2.codeserver多用户工具,实现多人在线vscode编辑器code为code-server实现了多用户和管理(测试平台:Ubuntu) 项目地址: https://gitee.com/xiaojiang0208/Code-serverManager https://github.com/xiaojiang0208/Code-serverManager 本项目还待完善慎重使用 安装教程-视频教程 根据这个教程安装code-server 创建一个用户和目录来供示例使用 https://blog.csdn.net/qq_41108184/article/details/132451655
3.codeVisual Studio Code(简称 vscode)是一款由微软开发且跨平台的免费源代码编辑器,结合插件能够用于多种语言程序开发(如 python, java, javascript, go 等)。但是其属于一款客户端软件,在远程服务器上使用不如 code-server,code-server 是一款能够在网页上使用的 vscode,极大的方便程序开发人员快速访问与编码。本篇介绍https://xujinzh.github.io/2024/04/10/install-code-server/
4.云风的BLOG:技术Archives昨天做内部晋升评审时听到候选人介绍他即将开始的一个项目。大致是我们公司从韩国买过来一个游戏(有全部源码)打算自己运营。该游戏服务器全部用 C++ 编写,使用 SQL Server 做数据库。我们这个项目,除了需要根据市场做二次开发外,还希望把 SQL Server 迁移到 MySQL 上。成本是最主要的原因,如果可以迁移成功,成本将减https://blog.codingnow.com/eo/
5.code比如joplin 虽然多端同步和移动端支持做的非常好,但是界面真的丑 现在使用code-server当替代方案,感觉除了移动端的支持弱一些,其他方面简直完美 code-server 就是vscode在云端运行,可以在浏览器使用vscode,试用后发现体验和本地运行基本没啥太大差距。 https://www.jianshu.com/p/e4237be19ce0
6.VSCode在线运行:codeserver部署(系列一)铁皮鸭子部署方式有多种,这里我们选择下载安装包进行部署,下载地址; https://github.com/coder/code-server/releases/download/v4.0.2/code-server-4.0.2-linux-amd64.tar.gz 解压安装包 tar -zxvf code-server-4.0.2-linux-amd64.tar.gz 创建启动脚本 在解压目录内执行./code-server --help可以查看启动相关的命令 https://www.cnblogs.com/barros/p/15934894.html
7.悄悄学习Doris,偷偷惊艳所有人ApacheDoris四万字小总结Palo 于 2017 年正式在 GitHub 上开源,并且在 2018 年贡献给 Apache 社区,并将名字改为 Apache Doris(incubating)进行正式孵化。随着开源,Doris 已经在京东、美团、搜狐、小米等公司的生产环境中正式使用,也有越来越多的 Contributor 加入到 Doris 大家庭中。 https://xie.infoq.cn/article/b2250c2d887f69d8519a3f50b
8.unix大杂烩基础型一服务器知识学堂二、 操作系统 UNIX安装与维护、Windows主要特性和一般使用方法。 1、 操作系统检查方法:检查系统出错报告、检查系统日志、检查登录用户、检查异常进程、CPU工作情况。 2、 安装SCO OpenServer 5 所必需的硬件基本配置如何? CPU:80486DX或以上 系统内存:32MB https://server.zol.com.cn/127/1272059.html
9.vscode支持GoVisualStudioCode教程根据每年Go相关的 Survey 和官方数据,Go 社区的新晋用户更倾向于使用VS Code来进行 Go 的编程。于是 Go 团队决定亲自书写一套 Go 的 Language Server,以提升VS Code上 Go 的书写体验。上面我提到的 Language Server 是 SourceGraph 团队实现的,Go 团队可能不会基于它们的代码进行修改,而是重起炉灶。对于官方团队的https://m.w3cschool.cn/vscode/vscode-for-go.html
10.VSCodeWebIDECoder安装及使用其它综合Coder是VS Code的Web IDE,分Code Server 和 Coder,Code Server安装在服务器上,通过浏览器打开地址后可以使用一个web版的VS Code,也就是Coder,但VS Code的插件无法使用。 VSCode:https://code.visualstudio.com/ Code-Server说明:https://coder.com/docs/code-server/latest Coder说明:https://coder.com/docs/https://www.jb51.net/article/231012.htm
11.OneCode一路走来OneCode 第一个版本,以V5版本的引擎为核心,将平台全部600余组件的2/3贡献到社区版本,同时为方便大家构建自身的工程体系,还会同步开源 OneCode V3版本的支持环境VFS(JAVA开发)虚拟存储系统,以及配合V3 部署使用的 OneCode Server 和相关的部署管理插件。V4及V5部分由https://developer.aliyun.com/article/1198565
12.codeserver:codeMIT Code of conduct 保存更改 取消 发行版 暂无发行版 贡献者 (265) 全部 近期动态 1年多前同步了仓库 4年前评论了仓库 4年多前评论了仓库 加载更多 不能加载更多了 马建仓 AI 助手 TypeScript 1 https://gitee.com/mirrors/code-server.git git@gitee.com:mirrors/code-server.git mirrorhttps://gitee.com/mirrors/code-server/
13.在codeserver中使用rootlessdocker腾讯云开发者社区然后就可以在code server中使用docker 了 image-20220427215239438 问题 除了上面提到的问题 $DOCKER_HOST、$XDG_RUNTIME_DIR在 code server terminal中为空 还有一个问题: 在code server terminal中为空无法使用systemctl --user restart|start|status docker.service命令 https://cloud.tencent.com/developer/article/1989164
14.codecode-server在wsl2下使用docker安装 === 第一大步骤:检查wsl本身,并更新到wsl2去 结果我发现我还是1? PSC:\Users\Administrator>wsl-l-v NAMESTATEVERSION *UbuntuRunning1 PSC:\Users\Administrator> PSC:\Users\Administrator> PSC:\Users\Administrator> PSC:\Usershttps://www.douban.com/note/855651222/
15.SpringCloud微服务实战——搭建企业级开发框架(二十三):Gateway+前端登录页面增加判断,默认采用password方式登录,当错误达到一定次数时,必须使用验证码登录 requestFailed (err) { this.isLoginError = true if (err && err.code === 427) { // 密码错误次数超过最大限值,请选择验证码模式登录 if (this.customActiveKey === 'tab_account') { this.grantType = 'captchhttps://www.tulingxueyuan.cn/tlzx/jsp/5102.html
16.把VSCode带到安卓code-server 在 github 发布的版本中是有 arm64 架构的,整个下载后,开终端解压执行就挂了,这个虽然是 arm64,并且带有一个 arm64 的 node,但是是为完整 linux 准备的。也就是说,node 中硬编码了 /usr /lib 等这些路径,并且附带的 node_modules 中也有大量的使用到 linux 特有节点的路径,这些安卓上都没有https://juejin.cn/post/7032548034638675982
17.开始通过WSL使用VSCodeMicrosoftLearn如果计划与其他人协作,或是在开放源代码站点(如 GitHub)上托管项目,则 VS Code 支持使用 Git 进行版本控制。 VS Code 中的“源代码管理”选项卡可跟踪所有更改,并直接在 UI 中内置了常见 Git 命令(add、commit、push、pull)。 若要安装 Git,请参阅设置 Git 以与适用于 Linux 的 Windows 子系统一起使用。https://docs.microsoft.com/zh-cn/windows/wsl/tutorials/wsl-vscode
18.稻草人安装code-server 分享 2022-02-27 vscode online what is vscode online vscode onlie 是在服务器中搭建vscode环境,然后在任何带有浏览器的设备中通过浏览器使用vscode requirements 一台具有公网ip云服务器,服务器最低配置是 内存:1GB, cpu: 2核 使用软件 https://www.bingxl.cn/
19.codeserveradministratorguide快速入门we多开发者? 当前部署方案默认只有一个 code-server,由于它并不支持多用户,所以不合适多开发协同工作的场景。 那么如何才能支持多开发者协作使用 code-server 呢?从宏观上设计,需多开发者使用,每一名开发者分配如下资源即可实现此需求: 单独分配一个宿主机的端口 https://support.websoft9.com/docs/codeserver
20.im客户端使用微信mars连接库,野火IM可能是最适应中国网络国情的即时通讯服务。 支持加速点加速,即可用于全球应用,也可用于政企内外双网复杂的网络环境。 支持阅读回执和在线状态功能,适用于办公环境。 音视频多种解决方案,丰俭由人,可自由选择。 高级音视频功能强大,支持9人以上的群组视频通话,支持1080P视频,支持会议模https://toscode.mulanos.cn/wfchat/im-server