Go 语言中的 gRPC 基础入门(下)

简介: Go 语言中的 gRPC 基础入门(下)

05

创建客户端


在本部分中,我们将研究为 RouteGuide 服务创建 Go 客户端。

您可以在 grpc-go/examples/route_guide/client/client.go 中看到我们完整的示例客户端代码。


创建客户端存根


要调用服务方法,我们首先需要创建一个 gRPC 通道来与服务器通信。我们通过将服务器地址和端口号传递给 grpc.Dial() 来创建它,如下所示:


var opts []grpc.DialOption
...
conn, err := grpc.Dial(*serverAddr, opts...)
if err != nil {
  ...
}
defer conn.Close()


当服务需要它们时,可以使用 DialOptions 在 grpc.Dial 中设置身份验证凭据(例如TLS,GCE凭据或JWT凭据)。RouteGuide 服务不需要任何凭据。


设置 gRPC 通道后,我们需要一个客户端存根来执行 RPC。我们使用从示例 .proto 文件生成的 pb 包提供的 NewRouteGuideClient 方法获取它。


client := pb.NewRouteGuideClient(conn)


调用服务方法


现在,让我们看看我们如何调用我们的服务方法。请注意,在 gRPC-Go 中,RPC 在阻塞/同步模式下运行,这意味着 RPC 调用等待服务器响应,并且将返回响应或错误。


简单的 RPC


调用简单的 RPC GetFeature 几乎与调用本地方法一样简单。


feature, err := client.GetFeature(context.Background(), &pb.Point{409146138, -746188906})
if err != nil {
  ...
}


如您所见,我们在先前获得的存根上调用该方法。在我们的方法参数中,我们创建并填充一个请求 protocol buffer 对象(在本例中为 Point)。我们还会传递一个 context.Context 对象,该对象可让我们在必要时更改 RPC 的行为,例如 time-out/cancel 运行中的 RPC。如果调用没有返回错误,那么我们可以从服务器的第一个返回值中读取响应信息。


log.Println(feature)


服务器端流式 RPC


我们在这里调用服务器端流方法 ListFeatures,该方法返回地理要素流。如果您已经阅读了创建服务器的内容,那么其中的一些内容可能看起来非常熟悉-流式 RPC 在两侧都以类似的方式实现。


rect := &pb.Rectangle{ ... }  // initialize a pb.Rectangle
stream, err := client.ListFeatures(context.Background(), rect)
if err != nil {
  ...
}
for {
    feature, err := stream.Recv()
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatalf("%v.ListFeatures(_) = _, %v", client, err)
    }
    log.Println(feature)
}


就像在简单的 RPC 中一样,我们为该方法传递一个上下文和一个请求。但是,我们没有取回响应对象,而是取回

RouteGuide_ListFeaturesClient 的实例。

客户端可以使用 RouteGuide_ListFeaturesClient 流读取服务器的响应。


我们使用

RouteGuide_ListFeaturesClient 的 Recv() 方法重复读取服务器对响应 protocol buffer 对象(在本例中为 Feature)的响应,直到没有更多消息为止:客户端需要检查每次返回后从 Recv() 返回的错误 err。如果为 nil,则流仍然良好,并且可以继续读取;如果是 io.EOF,则消息流已结束;否则,必须存在 RPC 错误,该错误会通过 err 传递。



客户端流式 RPC


客户端流方法 RecordRoute 与服务器端方法相似,不同之处在于,我们仅向该方法传递上下文,并获取回

RouteGuide_RecordRouteClientClient 流,我们可以使用该流来写入和读取消息。


// Create a random number of random points
r := rand.New(rand.NewSource(time.Now().UnixNano()))
pointCount := int(r.Int31n(100)) + 2 // Traverse at least two points
var points []*pb.Point
for i := 0; i < pointCount; i++ {
  points = append(points, randomPoint(r))
}
log.Printf("Traversing %d points.", len(points))
stream, err := client.RecordRoute(context.Background())
if err != nil {
  log.Fatalf("%v.RecordRoute(_) = _, %v", client, err)
}
for _, point := range points {
  if err := stream.Send(point); err != nil {
    log.Fatalf("%v.Send(%v) = %v", stream, point, err)
  }
}
reply, err := stream.CloseAndRecv()
if err != nil {
  log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)
}
log.Printf("Route summary: %v", reply)


RouteGuide_RecordRouteClient 具有一个 Send() 方法,可用于将请求发送到服务器。使用 Send() 完成将客户的请求写入流中后,我们需要在流上调用 CloseAndRecv() ,以使 gRPC 知道我们已完成写入并期望收到响应。我们从 CloseAndRecv() 返回的错误中获取 RPC 状态。如果状态为 nil,则 CloseAndRecv() 的第一个返回值将是有效的服务器响应。


双向流式 RPC


最后,让我们看一下双向流式 RPC RouteChat() 。

与 RecordRoute 一样,我们只向方法传递一个上下文对象,然后获取可用于写入和读取消息的流。但是,这一次我们在服务器仍将消息写入消息流的同时,我们还通过方法的流返回值。


stream, err := client.RouteChat(context.Background())
waitc := make(chan struct{})
go func() {
  for {
    in, err := stream.Recv()
    if err == io.EOF {
      // read done.
      close(waitc)
      return
    }
    if err != nil {
      log.Fatalf("Failed to receive a note : %v", err)
    }
    log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude)
  }
}()
for _, note := range notes {
  if err := stream.Send(note); err != nil {
    log.Fatalf("Failed to send a note: %v", err)
  }
}
stream.CloseSend()
<-waitc


除了在完成调用后使用流的 CloseSend() 方法外,此处的读写语法与我们的客户端流方法非常相似。尽管双方总是会按照对方的写入顺序来获取对方的消息,但是客户端和服务器都可以以任意顺序进行读取和写入-流完全独立地运行。


06

运行程序


从 examples/route_guide 目录执行以下命令:

  1. 运行服务器:
$ go run server/server.go
  1. 从另一个终端,运行客户端:
$ go run client/client.go


输出内容:


Getting feature for point (409146138, -746188906)
name:"Berkshire Valley Management Area Trail, Jefferson, NJ, USA" location:<latitude:409146138 longitude:-746188906 >
Getting feature for point (0, 0)
location:<>
Looking for features within lo:<latitude:400000000 longitude:-750000000 > hi:<latitude:420000000 longitude:-730000000 >
name:"Patriots Path, Mendham, NJ 07945, USA" location:<latitude:407838351 longitude:-746143763 >
...
name:"3 Hasta Way, Newton, NJ 07860, USA" location:<latitude:410248224 longitude:-747127767 >
Traversing 56 points.
Route summary: point_count:56 distance:497013163
Got message First message at point(0, 1)
Got message Second message at point(0, 2)
Got message Third message at point(0, 3)
Got message First message at point(0, 1)
Got message Fourth message at point(0, 1)
Got message Second message at point(0, 2)
Got message Fifth message at point(0, 2)
Got message Third message at point(0, 3)
Got message Sixth message at point(0, 3)



注意:

我们已从本页显示的客户端和服务器跟踪输出中省略了时间戳。


07

总结


本文开篇先介绍了为什么要使用 gRPC,接着简述了使用 gRPC 需要做的准备工作,然后通过 gRPC 官方 Go 示例代码介绍了如何在 .proto 文件中定义服务,如何使用 protoc 编译器生成客户端和服务器代码,如何根据 protoc 编译器生成的客户端和服务器代码创建服务器和客户端的 4 种形式。





目录
相关文章
|
9天前
|
存储 监控 算法
员工上网行为监控中的Go语言算法:布隆过滤器的应用
在信息化高速发展的时代,企业上网行为监管至关重要。布隆过滤器作为一种高效、节省空间的概率性数据结构,适用于大规模URL查询与匹配,是实现精准上网行为管理的理想选择。本文探讨了布隆过滤器的原理及其优缺点,并展示了如何使用Go语言实现该算法,以提升企业网络管理效率和安全性。尽管存在误报等局限性,但合理配置下,布隆过滤器为企业提供了经济有效的解决方案。
45 8
员工上网行为监控中的Go语言算法:布隆过滤器的应用
|
28天前
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
29天前
|
程序员 Go
go语言中结构体(Struct)
go语言中结构体(Struct)
100 71
|
28天前
|
存储 Go 索引
go语言中的数组(Array)
go语言中的数组(Array)
105 67
|
3天前
|
算法 安全 Go
Go 语言中实现 RSA 加解密、签名验证算法
随着互联网的发展,安全需求日益增长。非对称加密算法RSA成为密码学中的重要代表。本文介绍如何使用Go语言和[forgoer/openssl](https://github.com/forgoer/openssl)库简化RSA加解密操作,包括秘钥生成、加解密及签名验证。该库还支持AES、DES等常用算法,安装简便,代码示例清晰易懂。
29 16
|
6天前
|
监控 算法 安全
解锁企业计算机监控的关键:基于 Go 语言的精准洞察算法
企业计算机监控在数字化浪潮下至关重要,旨在保障信息资产安全与高效运营。利用Go语言的并发编程和系统交互能力,通过进程监控、网络行为分析及应用程序使用记录等手段,实时掌握计算机运行状态。具体实现包括获取进程信息、解析网络数据包、记录应用使用时长等,确保企业信息安全合规,提升工作效率。本文转载自:[VIPShare](https://www.vipshare.com)。
19 0
|
20天前
|
Go 数据安全/隐私保护 UED
优化Go语言中的网络连接:设置代理超时参数
优化Go语言中的网络连接:设置代理超时参数
【Go语言入门100题】026 I Love GPLT (5 分) Go语言 | Golang
L1-026 I Love GPLT (5 分) Go语言|Golang 这道超级简单的题目没有任何输入。 你只需要把这句很重要的话 —— “I Love GPLT”——竖着输出就可以了。 所谓“竖着输出”,是指每个字符占一行(包括空格),即每行只能有1个字符和回车。
612 0
|
存储 Go
【Go语言入门100题】023 输出GPLT (20 分) Go语言 | Golang
L1-023 输出GPLT (20 分) Go语言|Golang 给定一个长度不超过10000的、仅由英文字母构成的字符串。请将字符重新调整顺序,按GPLTGPLT....这样的顺序输出,并忽略其它字符。当然,四种字符(不区分大小写)的个数不一定是一样多的,若某种字符已经输出完,则余下的字符仍按GPLT的顺序打印,直到所有字符都被输出。 下面给出甲、乙两人的酒量(最多能喝多少杯不倒)和划拳记录,请你判断两个人谁先倒。
172 0
|
29天前
|
存储 Go 索引
go语言中数组和切片
go语言中数组和切片
39 7