编写一个go gRPC的服务
前置条件:
获取 gRPC-go 源码
$ go get google.golang.org/grpc
简单例子的源码位置:
$ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld
复杂些例子的源码位置:
$ cd $GOPATH/src/google.golang.org/grpc/examples/route_guide
写一个gRPC的服务,一般分下面几步:
- 在一个 .proto 文件内定义服务。
- 用 protocol buffer 编译器生成服务器和客户端代码。
- 使用 gRPC 的 Go API 为你的服务实现一个简单的客户端和服务器。
定义服务
在一个 .proto 文件中定义服务
简单的例子服务定义在: examples/helloworld/helloworld/helloworld.proto
route_guide 的定义在 : examples/route_guide/routeguide/route_guide.proto
要定义一个服务,你必须在你的 .proto 文件中指定 service:
然后在你的服务中定义 rpc 方法,指定请求的和响应类型。
gRPC 允许你定义4种类型的 service 方法,这些都在 RouteGuide 服务中使用到了:
简单RPC
一个 简单 RPC , 客户端发送带参请求到服务器并等待响应返回,就像平常的函数调用一样。
服务器端流式 RPC
一个 服务器端流式 RPC , 客户端发送请求到服务器,拿到一个流去读取返回的消息序列。 客户端读取返回的流,直到里面没有任何消息。从例子中可以看出,通过在 响应 类型前插入 stream 关键字,可以指定一个服务器端的流方法。
客户端流式 RPC
一个 客户端流式 RPC , 客户端写入一个消息序列并将其发送到服务器,同样也是使用流。一旦客户端完成写入消息,它等待服务器完成读取返回它的响应。通过在 请求 类型前指定 stream 关键字来指定一个客户端的流方法。
双向流式 RPC
一个 双向流式 RPC 是双方使用读写流去发送一个消息序列。两个流独立操作,因此客户端和服务器可以以任意喜欢的顺序读写:比如, 服务器可以在写入响应前等待接收所有的客户端消息,或者可以交替的读取和写入消息,或者其他读写的组合。 每个流中的消息顺序被预留。你可以通过在请求和响应前加 stream 关键字去制定方法的类型。
消息类型
上面看起来像函数参数、返回值得,由于要涉及到跨服务器调用,这些其实传递的是消息。我们的 .proto 文件也包含了所有请求的 protocol buffer 消息类型定义以及在服务方法中使用的响应类型——比如,下面的Point消息类型:
生成服务器和客户端代码
我们需要通过 protocol buffer 的编译器 protoc 以及一个特殊的 gRPC Go 插件来完成用 protocol buffer 编译器生成服务器和客户端代码。
简单期间,有个 bash 脚本可以帮我们生成合适的代码 codegen.sh (https://github.com/grpc/grpc-go/blob/master/codegen.sh)
运行 codegen.sh route_guide.proto 就可以在当前目录下产生 route_guide.pb.go 文件。
在这个产生的文件中, 既有 routeGuideClient 客户端代码部分, 也有 routeGuideRouteChatServer 服务器段代码部分。
创建服务器
这部分的源码在: grpc-go/examples/route_guide/server/server.go
让 RouteGuide 服务工作有两个部分:
- 实现我们服务定义的生成的服务接口:做我们的服务的实际的“工作”。
- 运行一个 gRPC 服务器,监听来自客户端的请求并返回服务的响应。
实现RouteGuide
在源码中,我们可以看到实现了接口RouteGuideServer的routeGuideServer数据结构。 这个接口是在route_guide.pb.go中自动产生的。
简单RPC
GetFeature,它从客户端拿到一个 Point 对象,然后从返回包含从数据库拿到的feature信息的 Feature.
该方法传入了 RPC 的上下文对象,以及客户端的 Point 参数。它返回了Feature 响应信息和error信息。
在方法中我们遍历所有服务器端保存的信息,找到位置信息匹配的,然后将其和一个nil错误一起返回给客户端。
服务器端流式 RPC
ListFeatures 是一个服务器端的流式 RPC,我们将多个 Feature 发回给客户端。
这里的请求参数是一个 Rectangle,客户端期望返回多个 Feature,这次我们使用了一个请求对象和一个特殊的RouteGuide_ListFeaturesServer来写入我们的响应,而不是得到方法参数中的入参和返回值。
在这个方法中,我们填充了尽可能多的 Feature 对象去返回,用steam的 Send() 方法把它们写入 RouteGuide_ListFeaturesServer。
最后,我们返回了一个 nil 错误告诉 gRPC 响应的写入已经完成。
如果在调用过程中发生任何错误,我们会返回一个非 nil 的错误;
客户端流式 RPC
客户端流方法 RecordRoute,我们通过它可以从客户端拿到一个 Point 的流,其中包括我们需要的Point信息。
这次这个方法用了一个 RouteGuide_RecordRouteServer 流,服务器可以用它来同时读 和 写消息——它可以用自己的 Recv() 方法接收客户端消息并且用 SendAndClose() 方法返回它的单个响应。
在方法体中,我们使用 RouteGuide_RecordRouteServer 的 Recv() 方法去反复读取客户端的请求到一个请求对象(在这个场景下是 Point),直到没有更多的消息。
服务器需要在每次调用后检查 Read() 返回的错误。如果返回值为 nil,流依然完好,可以继续读取;
如果返回值为 io.EOF,消息流结束,服务器可以返回它的 RouteSummary。
如果它还有其它值,我们原样返回错误,gRPC 层会把它转换为 RPC 状态。
双向流式 RPC
双向流式 RPC RouteChat()。
这里读写的语法和客户端流方法相似,除了服务器会使用流的 Send() 方法而不是 SendAndClose(),因为它需要写多个响应。虽然客户端和服务器端总是会拿到对方写入时顺序的消息,它们可以以任意顺序读写——流的操作是完全独立的。
启动服务器
一旦我们实现了所有的方法,我们还需要启动一个gRPC服务器,这样客户端才可以使用服务。
为了构建和启动服务器,我们需要:
- 使用 lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) 指定我们期望客户端请求的监听端口。
- 使用grpc.NewServer()创建 gRPC 服务器的一个实例。
- 在 gRPC 服务器注册我们的服务实现。
- 用服务器 Serve() 方法以及我们的端口信息区实现阻塞等待,直到进程被杀死或者 Stop() 被调用。
创建客户端
建立跟服务器的连接
为了调用服务方法,我们首先创建一个 gRPC conn。我们通过给 grpc.Dial() 传入服务器地址和端口号做到这点,如下:
你可以使用 DialOptions 在 grpc.Dial 中设置授权认证(如, TLS,GCE认证,JWT认证),如果服务有这样的要求的话 —— 但是对于 RouteGuide 服务,我们不用这么做。
一旦 gRPC conn 建立起来,我们需要一个client去执行 RPC。我们通过 .proto 生成的 pb 包提供的 NewRouteGuideClient 方法来完成。
调用服务器方法
简单RPC
调用简单 RPC GetFeature 几乎是和调用一个本地方法一样直观。
服务器端流式 RPC
我们给方法传入一个上下文和请求。然而,我们得到返回的是一个 RouteGuide_ListFeaturesClient
实例,而不是一个应答对象。客户端可以使用 RouteGuide_ListFeaturesClient
流去读取服务器的响应。
我们使用 RouteGuide_ListFeaturesClient
的 Recv()
方法去反复读取服务器的响应到一个响应 protocol buffer 对象(在这个场景下是Feature
)直到消息读取完毕:每次调用完成时,客户端都要检查从 Recv()
返回的错误 err
。如果返回为 nil
,流依然完好并且可以继续读取;如果返回为 io.EOF
,则说明消息流已经结束;否则就一定是一个通过 err
传过来的 RPC 错误。
客户端流式 RPC
RouteGuide_RecordRouteClient 有一个 Send() 方法,我们可以用它来给服务器发送请求。一旦我们完成使用 Send() 方法将客户端请求写入流,就需要调用流的 CloseAndRecv()方法,让 gRPC 知道我们已经完成了写入同时期待返回应答。我们从 CloseAndRecv() 返回的 err 中获得 RPC 的状态。如果状态为nil,那么CloseAndRecv()的第一个返回值将会是合法的服务器应答。
双向流式 RPC
我们只给函数传入一个上下文对象,拿到可以用来读写的流。
运行例子
服务器端:
$ go run server/server.go
客户端:
$ go run client/client.go
参考资料:
中文 gRPC 基础 Go http://doc.oschina.net/grpc?t=60133
英文 gRPC 基础 Go http://www.grpc.io/docs/tutorials/basic/go.html
编写一个go gRPC的服务的更多相关文章
- 编写一个简单的TCP服务端和客户端
下面的实验环境是linux系统. 效果如下: 1.启动服务端程序,监听在6666端口上 2.启动客户端,与服务端建立TCP连接 3.建立完TCP连接,在客户端上向服务端发送消息 4.断开连接 实现 ...
- go: 如何编写一个正确的udp服务端
udp的服务端有一个大坑,即如果收包不及时,在系统缓冲写满后,将大量丢包. 在网上通常的示例中,一般在for循环中执行操作逻辑.这在生产环境将是一个隐患.是的,俺就翻车了. go强大简易的并发能力可以 ...
- Viper 微服务框架 编写一个hello world 插件-02
1.Viper是什么? Viper 是.NET平台下的Anno微服务框架的一个示例项目.入门简单.安全.稳定.高可用.全平台可监控.底层通讯可以随意切换thrift grpc. 自带服务发现.调用链追 ...
- 网络爬虫:使用Scrapy框架编写一个抓取书籍信息的爬虫服务
上周学习了BeautifulSoup的基础知识并用它完成了一个网络爬虫( 使用Beautiful Soup编写一个爬虫 系列随笔汇总 ), BeautifulSoup是一个非常流行的Python网 ...
- 从头开始编写一个Orchard网上商店模块(6) - 创建购物车服务和控制器
原文地址: http://skywalkersoftwaredevelopment.net/blog/writing-an-orchard-webshop-module-from-scratch-pa ...
- 用 Go 编写一个简单的 WebSocket 推送服务
用 Go 编写一个简单的 WebSocket 推送服务 本文中代码可以在 github.com/alfred-zhong/wserver 获取. 背景 最近拿到需求要在网页上展示报警信息.以往报警信息 ...
- Java基础---Java---网络编程---TCP的传输、客户端和服务端的互访、建立一个文本转换器、编写一个聊天程序
演示TCP的传输的客户端和服务端的互访 需求:客户端给服务端发送数据,服务端收到后,给客户端反馈信息. 客户端: 1.建立Socket服务,指定要连接方朵和端口 2.获取Socket流中的输出流,将数 ...
- .NET Core使用gRPC打造服务间通信基础设施
一.什么是RPC rpc(远程过程调用)是一个古老而新颖的名词,他几乎与http协议同时或更早诞生,也是互联网数据传输过程中非常重要的传输机制. 利用这种传输机制,不同进程(或服务)间像调用本地进程中 ...
- Golang gRPC微服务01: 介绍
gRPC 是什么 gRPC是goole开源的一个RPC框架和库,支持多语言之间的通信.底层通信采用的是 HTTP2 协议.gRPC在设计上使用了 ProtoBuf 这种接口描述语言.这种IDL语言可以 ...
随机推荐
- oracle数据字典-权限-角色
每个数据库都提供了各自的数据字典的方案,虽然形式不同,但是目的和作用是一样的,比如在mysql里数据字典是在information_schema 里表现的,sqlserver则是在sys这个系统sch ...
- docker nodejs 基本应用
1. 安装docker 环境 2. nodejs 应用布局 package.json { "name": "docker-centos-hello", &qu ...
- How to send Email using C#
try { MailMessage mail = new MailMessage(); SmtpClient SmtpServer = new SmtpClient("smtp.gmail. ...
- python3图片裁剪+转换pdf+压缩
本地大量长图,要发送给别人,所以要对图片进行裁剪+转换pdf+压缩 import zipfile import os from concurrent.futures import ThreadPool ...
- python数据类型之str用法
1.首字母大写 语法:S.capitalize() -> str title = "today is a good day" title_ca = title.capital ...
- Windows 8.1 系统上用Oracle VM VirtualBox 安装windows 2008 R2 SP1 的虚拟机 出现 Error Code: 0x000000C4
Windows 8.1 本来可以安装Hyper-v来安装虚拟机,但是我现在需要使用Oracle VM VirtualBox来安装虚拟机, 所以必须先卸载Hyper-v VirtualBox 安装的虚拟 ...
- [nginx]Nginx禁止访问robots.txt防泄漏web目录
关于robots.txt文件:搜索引擎通过一种程序robot(又称spider),自动访问互联网上的网页并获取网页信 息.您可以在您的网站中创建一个纯文本文件robots.txt,在这个文件中声明该网 ...
- WCF 遇到 由于线程退出或应用程序请求,已放弃 I/O 操作 ListenerContextInputStream
异常类型:IOException 异常消息:An exception has been thrown when reading the stream. 异常信息: at System.ServiceM ...
- LeetCode "Count of Smaller Number After Self"
Almost identical to LintCode "Count of Smaller Number before Self". Corner case needs to b ...
- IntelliJ IDEA配置svn
Settings→Version Control→General 输入svn客户端命令所在的路径 来自为知笔记(Wiz)