上一篇blog是关于gRPC框架的基本使用,假设说gRPC仅仅是远程发几个參数,那和一个普通的http请求也没多大区别了。

所以今天我就来学习一下gRPC高级一点的用法。

流!

流能够依据用法,分为单向和双向:

  • 单向

    – Client->Server

    – Server->Client
  • 双向

    – Client<=>Server

以下是一个新的样例,三种服务分别使用了几种流服务方式:

1. 參数表示一块地。而返回的是这块地上面的建筑。

2. client不停的发送新的点。最后在服务端构成一个路径,返回。

3. client发送新的点,服务端在做位置操作后返回新的点。

  1. syntax="proto3";
  2. message Point{}
  3. message Path{}
  4. message Ground{}
  5. message Construction{}
  6. service MapService {
  7. // Server Side Stream
  8. rpc ListConstructions(Ground) returns (stream Construction) {}
  9. // Client Side Stream
  10. rpc RecordPath(stream Point) returns (Path){}
  11. // Bidirectional streaming
  12. rpc Offset(stream Point) returns (stream Point){}
  13. }

执行命令生成代码:

  1. protoc --go_out=plugins=grpc:. test.proto

生成的代码太长了。一段一段帖吧,首先帖对象定义部分,这里应该略微简单:

  1. package test
  2. import proto "github.com/golang/protobuf/proto"
  3. import (
  4. context "golang.org/x/net/context"
  5. grpc "google.golang.org/grpc"
  6. )
  7. // Reference imports to suppress errors if they are not otherwise used.
  8. var _ = proto.Marshal
  9. type Point struct {
  10. }
  11. func (m *Point) Reset() { *m = Point{} }
  12. func (m *Point) String() string { return proto.CompactTextString(m) }
  13. func (*Point) ProtoMessage() {}
  14. type Path struct {
  15. }
  16. func (m *Path) Reset() { *m = Path{} }
  17. func (m *Path) String() string { return proto.CompactTextString(m) }
  18. func (*Path) ProtoMessage() {}
  19. type Ground struct {
  20. }
  21. func (m *Ground) Reset() { *m = Ground{} }
  22. func (m *Ground) String() string { return proto.CompactTextString(m) }
  23. func (*Ground) ProtoMessage() {}
  24. type Construction struct {
  25. }
  26. func (m *Construction) Reset() { *m = Construction{} }
  27. func (m *Construction) String() string { return proto.CompactTextString(m) }
  28. func (*Construction) ProtoMessage() {}
  29. // Reference imports to suppress errors if they are not otherwise used.
  30. var _ context.Context
  31. var _ grpc.ClientConn

生成的Go语言的几种struct定义,正好相应了在proto文件里的4个message定义。对照上和篇中的样例,除了多几个对象。并没有更复杂。

跳过!

服务端

刚一看到这段代码,高高我又有一点蒙。只是想想之前那句话,“相同的代码。细致看的话,认为难度是5,不细致看,一下就蒙了,那难度可能是8。”所以。根源不是难,而是懒得看。

我在打这上面这段话的时候,发现了一段非常熟悉的代码,位于生成文件的最后一段,就是

  1. var _MapService_serviceDesc = grpc.ServiceDesc{}

由于这段就是服务的名称与相应handler的映射嘛,所以。这一下子已经读懂了23行代码了。但有一点不同的是,这一次不是Method数组,而是Streams数组,非常明显,这一次的服务是流的形式。所以gRPC是把服务的方法名,作为流的名称。而为每个流,都相应的生成了一个Handler方法:

  • _MapService_ListConstructions_Handler
  • _MapService_RecordPath_Handler
  • _MapService_Offset_Handler

经过上面的分析得出,大致的结构还是没有变化的。

看生成代码最上面两段代码。已加凝视,就不多附文解释了,相信看过上一篇博客的朋友非常easy懂:

  1. // Server API for MapService service
  2. // 我们要实现的服务方法
  3. type MapServiceServer interface {
  4. ListConstructions(*Ground, MapService_ListConstructionsServer) error
  5. RecordPath(MapService_RecordPathServer) error
  6. Offset(MapService_OffsetServer) error
  7. }
  8. // 把我们实现的服务端对象实例,告诉gRPC框架
  9. func RegisterMapServiceServer(s *grpc.Server, srv MapServiceServer) {
  10. s.RegisterService(&_MapService_serviceDesc, srv)
  11. }
  • 服务方法一,Server Side单向流。

    顾名思义。服务端向client有一个单向的通道,入口在服务端,出口在client,而服务端自然会有一个向这个入口写数据的操作。

  1. // Server Side Stream
  2. // rpc ListConstructions(Ground) returns (stream Construction) {}
  3. func _MapService_ListConstructions_Handler(srv interface{}, stream grpc.ServerStream) error {
  4. m := new(Ground)
  5. if err := stream.RecvMsg(m); err != nil {
  6. return err
  7. }
  8. return srv.(MapServiceServer).ListConstructions(m, &mapServiceListConstructionsServer{stream})
  9. }
  10. type MapService_ListConstructionsServer interface {
  11. Send(*Construction) error
  12. grpc.ServerStream
  13. }
  14. type mapServiceListConstructionsServer struct {
  15. grpc.ServerStream
  16. }
  17. func (x *mapServiceListConstructionsServer) Send(m *Construction) error {
  18. return x.ServerStream.SendMsg(m)
  19. }

首先_MapService_ListConstructions_Handler方法,在服务端接收到请求时调用,将数据解析出来,然后生成一个mapServiceListConstructionsServer,提供服务。

mapServiceListConstructionsServer实现了MapService_ListConstructionsServer接口,包括了一个grpc封装好的ServerStream。这是通道的入口,和一个用于发送消息的Send方法。

创建Server Side单向流服务:

  1. type mapServiceServer struct {
  2. ...
  3. }
  4. func (s *mapServiceServer) ListConstructions(ground *Ground, stream MapService_ListConstructionsServer) error {
  5. var constructions:= constructionsInGround(ground)
  6. for _, construction := range constructions {
  7. stream.Send(building)
  8. }
  9. return nil
  10. }
  • 服务方法二,Client Side单向流。

    相同在Client & Server之间有一条通道,而这一次服务端要接收client发来的Point数据。
  1. func _MapService_RecordPath_Handler(srv interface{}, stream grpc.ServerStream) error {
  2. return srv.(MapServiceServer).RecordPath(&mapServiceRecordPathServer{stream})
  3. }
  4. type MapService_RecordPathServer interface {
  5. SendAndClose(*Path) error
  6. Recv() (*Point, error)
  7. grpc.ServerStream
  8. }
  9. type mapServiceRecordPathServer struct {
  10. grpc.ServerStream
  11. }
  12. func (x *mapServiceRecordPathServer) SendAndClose(m *Path) error {
  13. return x.ServerStream.SendMsg(m)
  14. }
  15. func (x *mapServiceRecordPathServer) Recv() (*Point, error) {
  16. m := new(Point)
  17. if err := x.ServerStream.RecvMsg(m); err != nil {
  18. return nil, err
  19. }
  20. return m, nil
  21. }

Handler用于接收client的请求。生成服务对象来做详细的处理。

服务的定义包函一个流对象。接收方法,用来接收client不停传来的Point数据。最后返回路径,由于是一次性返回,因此命名为SendAndClose。

创建Client Side单向流服务:

  1. func (s *mapServiceServer) RecordPath(stream MapService_RecordPathServer) error {
  2. for {
  3. point, err := stream.Recv()
  4. if err == io.EOF {
  5. return stream.SendAndClose(path)
  6. } else {
  7. path.append(point)
  8. }
  9. }
  10. return nil
  11. }
  • 双向流
  1. func _MapService_Offset_Handler(srv interface{}, stream grpc.ServerStream) error {
  2. return srv.(MapServiceServer).Offset(&mapServiceOffsetServer{stream})
  3. }
  4. type MapService_OffsetServer interface {
  5. Send(*Point) error
  6. Recv() (*Point, error)
  7. grpc.ServerStream
  8. }
  9. type mapServiceOffsetServer struct {
  10. grpc.ServerStream
  11. }
  12. func (x *mapServiceOffsetServer) Send(m *Point) error {
  13. return x.ServerStream.SendMsg(m)
  14. }
  15. func (x *mapServiceOffsetServer) Recv() (*Point, error) {
  16. m := new(Point)
  17. if err := x.ServerStream.RecvMsg(m); err != nil {
  18. return nil, err
  19. }
  20. return m, nil
  21. }

经过上面对单向流和双向流的代码解读之后,这一部分,似乎是一看就懂了!

来创建一个双向流的服务方法:

  1. func (s *mapServiceServer) Offset(stream MapService_OffsetServer) error {
  2. for {
  3. point, err := stream.Recv()
  4. if err == io.EOF {
  5. return nil
  6. }
  7. if err != nil {
  8. return err
  9. }
  10. offsetPoint := offset(point)
  11. if err := stream.Send(offsetPoint); err != nil {
  12. return err
  13. }
  14. }
  15. }

和上一篇一样。这篇blog主要在于对生成代码的解析,以上代码。除了proto生成go文件是真实的,以下的代码我都没跑过。用番茄扔我吧!

用了两篇Blog,基本对gRPC框架有了一个了解。下一篇,就要回到gonet2框架了!

看看在框架里,是怎样使用gRPC的吧!

Gonet2 游戏server框架解析之gRPC提高(5)的更多相关文章

  1. Gonet2 游戏server框架解析之Agent(3)

    客户端消息在Agent中的预处理流程. Agent定义好的三种请求: //api.go var RCode = map[int16]string{ 0: "heart_beat_req&qu ...

  2. Leaf - 一个由 Go 语言编写的开发效率和执行效率并重的开源游戏服务器框架

    转自:https://toutiao.io/posts/0l7l7n/preview Leaf 游戏服务器框架简介 Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏 ...

  3. 游戏server之server优化思路

    本文仅仅是提供一些游戏server优化思路,当中一些思路是用在不同场合的,不是同个架构的.须要依据应用场景选用合适方式. 本文的引用的文章都是在自己写的在本博客内的.也都是上线开几百个服的成熟项目的. ...

  4. AlexeyAB DarkNet YOLOv3框架解析与应用实践(六)

    AlexeyAB DarkNet YOLOv3框架解析与应用实践(六) 1. Tiny Darknet 听过很多人谈论SqueezeNet. SqueezeNet很酷,但它只是优化参数计数.当大多数高 ...

  5. [转载]iOS 10 UserNotifications 框架解析

    活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  6. iOS 10 UserNotifications 框架解析

    摘自:https://onevcat.com/2016/08/notification/ iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ...

  7. Poco::TCPServer框架解析

    Poco::TCPServer框架解析 POCO C++ Libraries提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程.文件.流,网络协议包括:HTTP.FTP.SMTP ...

  8. Scut游戏server引擎Unity3d访问

    Scut提供Unity3d Sdk包.便利的高速发展和Scut游戏server对接: 看Unity3d示为以下的比率: 启动Unity3d项目 打开Scutc.svn\SDK\Unity3d\Asse ...

  9. 游戏UI框架设计(三) : 窗体的层级管理

    游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ...

随机推荐

  1. 【转载】CPU架构、指令集与指令集体系结构(ISA)

    最近学习计算机系统基础,了解到指令集体系结构. 对CPU架构.指令集和指令集体系结构的关系不清楚,特此记录. 指令集体系结构(ISA)包括 指令集.指令集编码.基本数据类型等. CPU架构 实现了 指 ...

  2. Linux 程序包管理-RPM

    程序简介:  POSIX(Portable Openratin System)跨平台系统:不同操作系统平台的标准C库(glibc)都是遵循POSIX规范的,这样基于标准库开发程序的源代码可以夸平台编译 ...

  3. Docker yum 安装

      [liwm@Eren ~]$ sudo su[root@Eren liwm]# yum install -y docker 已加载插件:fastestmirror, langpacks, prod ...

  4. spring中IOC的简单使用

    spring的ioc主要就是依赖注入,有基于构造器的依赖注入还有通过设值注入,这里我只简单的实现设值注入的方法,通过spring的依赖管理,我们可以很方便的了解各层之间的依赖关系,降低了各层之间的耦合 ...

  5. 题解 洛谷 P4047 【[JSOI2010]部落划分】

    我觉得几乎就是一道最小生成树模板啊... 题解里许多大佬都说选第n-k+1条边,可我觉得要这么讲比较容易理解 (虚边为能选的边,实边为最小生成树) 令n=5,k=2,(1,3)<(1,2)< ...

  6. FZU 1980 AbOr's story

    AbOr's story Time Limit: 1000ms Memory Limit: 32768KB This problem will be judged on FZU. Original I ...

  7. 【转】一天学会PHP(转)

    [转]一天学会PHP(转) 只需要一天,只要你用心去看和学,一定行. - 这里希望大家需要明白一点,这只是在讲如何快速入门,更好的认识PHP!也能初级掌握PHP基础知识!PHP语言博大精深!并不是一两 ...

  8. Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程

    2.Lucene3.6.2包介绍,第一个Lucene案例介绍,查看索引信息的工具lukeall介绍,Luke查看的索引库内容,索引查找过程 2014-12-07 23:39 2623人阅读 评论(0) ...

  9. UVA 11020 Efficient Solutions+multiset的应用

    题目链接:点击进入 首先来讲,非常easy看到我们事实上仅仅要维护优势人群的集合:假设增加一个新的人,我们首先看一下优势人群中是否有人会让这个人失去优势,假设没有,则将这个人插入集合中.但要注意到这个 ...

  10. 使用AFNetworking第三方下载类

    AFNetworking 眼下使用比較多得一个下载库 眼下一直在维护更新,使用的是很easy 不须要加入不论什么关联的库  1.带block形式 内部是任务队列进行下载  就是对operation的一 ...