Gonet2 游戏server框架解析之gRPC提高(5)
上一篇blog是关于gRPC框架的基本使用,假设说gRPC仅仅是远程发几个參数,那和一个普通的http请求也没多大区别了。
所以今天我就来学习一下gRPC高级一点的用法。
流!
流能够依据用法,分为单向和双向:
- 单向 
 – Client->Server
 – Server->Client
- 双向 
 – Client<=>Server
以下是一个新的样例,三种服务分别使用了几种流服务方式: 
1. 參数表示一块地。而返回的是这块地上面的建筑。 
2. client不停的发送新的点。最后在服务端构成一个路径,返回。 
3. client发送新的点,服务端在做位置操作后返回新的点。
syntax="proto3";
message Point{}
message Path{}
message Ground{}
message Construction{}
service MapService {
    // Server Side Stream
    rpc ListConstructions(Ground) returns (stream Construction) {}
    // Client Side Stream
    rpc RecordPath(stream Point) returns (Path){}
    // Bidirectional streaming
    rpc Offset(stream Point) returns (stream Point){}
}执行命令生成代码:
protoc --go_out=plugins=grpc:. test.proto生成的代码太长了。一段一段帖吧,首先帖对象定义部分,这里应该略微简单:
package test
import proto "github.com/golang/protobuf/proto"
import (
    context "golang.org/x/net/context"
    grpc "google.golang.org/grpc"
)
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
type Point struct {
}
func (m *Point) Reset()         { *m = Point{} }
func (m *Point) String() string { return proto.CompactTextString(m) }
func (*Point) ProtoMessage()    {}
type Path struct {
}
func (m *Path) Reset()         { *m = Path{} }
func (m *Path) String() string { return proto.CompactTextString(m) }
func (*Path) ProtoMessage()    {}
type Ground struct {
}
func (m *Ground) Reset()         { *m = Ground{} }
func (m *Ground) String() string { return proto.CompactTextString(m) }
func (*Ground) ProtoMessage()    {}
type Construction struct {
}
func (m *Construction) Reset()         { *m = Construction{} }
func (m *Construction) String() string { return proto.CompactTextString(m) }
func (*Construction) ProtoMessage()    {}
// Reference imports to suppress errors if they are not otherwise used.
var _ context.Context
var _ grpc.ClientConn生成的Go语言的几种struct定义,正好相应了在proto文件里的4个message定义。对照上和篇中的样例,除了多几个对象。并没有更复杂。
跳过!
服务端
刚一看到这段代码,高高我又有一点蒙。只是想想之前那句话,“相同的代码。细致看的话,认为难度是5,不细致看,一下就蒙了,那难度可能是8。”所以。根源不是难,而是懒得看。
我在打这上面这段话的时候,发现了一段非常熟悉的代码,位于生成文件的最后一段,就是
var _MapService_serviceDesc = grpc.ServiceDesc{}由于这段就是服务的名称与相应handler的映射嘛,所以。这一下子已经读懂了23行代码了。但有一点不同的是,这一次不是Method数组,而是Streams数组,非常明显,这一次的服务是流的形式。所以gRPC是把服务的方法名,作为流的名称。而为每个流,都相应的生成了一个Handler方法:
- _MapService_ListConstructions_Handler
- _MapService_RecordPath_Handler
- _MapService_Offset_Handler
经过上面的分析得出,大致的结构还是没有变化的。
看生成代码最上面两段代码。已加凝视,就不多附文解释了,相信看过上一篇博客的朋友非常easy懂:
// Server API for MapService service
// 我们要实现的服务方法
type MapServiceServer interface {
    ListConstructions(*Ground, MapService_ListConstructionsServer) error
    RecordPath(MapService_RecordPathServer) error
    Offset(MapService_OffsetServer) error
}
// 把我们实现的服务端对象实例,告诉gRPC框架
func RegisterMapServiceServer(s *grpc.Server, srv MapServiceServer) {
    s.RegisterService(&_MapService_serviceDesc, srv)
}- 服务方法一,Server Side单向流。 
 顾名思义。服务端向client有一个单向的通道,入口在服务端,出口在client,而服务端自然会有一个向这个入口写数据的操作。
// Server Side Stream
// rpc ListConstructions(Ground) returns (stream Construction) {}
func _MapService_ListConstructions_Handler(srv interface{}, stream grpc.ServerStream) error {
    m := new(Ground)
    if err := stream.RecvMsg(m); err != nil {
        return err
    }
    return srv.(MapServiceServer).ListConstructions(m, &mapServiceListConstructionsServer{stream})
}
type MapService_ListConstructionsServer interface {
    Send(*Construction) error
    grpc.ServerStream
}
type mapServiceListConstructionsServer struct {
    grpc.ServerStream
}
func (x *mapServiceListConstructionsServer) Send(m *Construction) error {
    return x.ServerStream.SendMsg(m)
}首先_MapService_ListConstructions_Handler方法,在服务端接收到请求时调用,将数据解析出来,然后生成一个mapServiceListConstructionsServer,提供服务。
mapServiceListConstructionsServer实现了MapService_ListConstructionsServer接口,包括了一个grpc封装好的ServerStream。这是通道的入口,和一个用于发送消息的Send方法。
创建Server Side单向流服务:
type mapServiceServer struct {
        ...
}
func (s *mapServiceServer) ListConstructions(ground *Ground, stream MapService_ListConstructionsServer) error {
    var constructions:= constructionsInGround(ground)
    for _, construction := range constructions {
        stream.Send(building)
    }
    return nil
}- 服务方法二,Client Side单向流。 
 相同在Client & Server之间有一条通道,而这一次服务端要接收client发来的Point数据。
func _MapService_RecordPath_Handler(srv interface{}, stream grpc.ServerStream) error {
    return srv.(MapServiceServer).RecordPath(&mapServiceRecordPathServer{stream})
}
type MapService_RecordPathServer interface {
    SendAndClose(*Path) error
    Recv() (*Point, error)
    grpc.ServerStream
}
type mapServiceRecordPathServer struct {
    grpc.ServerStream
}
func (x *mapServiceRecordPathServer) SendAndClose(m *Path) error {
    return x.ServerStream.SendMsg(m)
}
func (x *mapServiceRecordPathServer) Recv() (*Point, error) {
    m := new(Point)
    if err := x.ServerStream.RecvMsg(m); err != nil {
        return nil, err
    }
    return m, nil
}Handler用于接收client的请求。生成服务对象来做详细的处理。
服务的定义包函一个流对象。接收方法,用来接收client不停传来的Point数据。最后返回路径,由于是一次性返回,因此命名为SendAndClose。
创建Client Side单向流服务:
func (s *mapServiceServer) RecordPath(stream MapService_RecordPathServer) error {
    for {
        point, err := stream.Recv()
        if err == io.EOF {
            return stream.SendAndClose(path)
        } else {
            path.append(point)
        }
    }
    return nil
}- 双向流
func _MapService_Offset_Handler(srv interface{}, stream grpc.ServerStream) error {
    return srv.(MapServiceServer).Offset(&mapServiceOffsetServer{stream})
}
type MapService_OffsetServer interface {
    Send(*Point) error
    Recv() (*Point, error)
    grpc.ServerStream
}
type mapServiceOffsetServer struct {
    grpc.ServerStream
}
func (x *mapServiceOffsetServer) Send(m *Point) error {
    return x.ServerStream.SendMsg(m)
}
func (x *mapServiceOffsetServer) Recv() (*Point, error) {
    m := new(Point)
    if err := x.ServerStream.RecvMsg(m); err != nil {
        return nil, err
    }
    return m, nil
}经过上面对单向流和双向流的代码解读之后,这一部分,似乎是一看就懂了!
来创建一个双向流的服务方法:
func (s *mapServiceServer) Offset(stream MapService_OffsetServer) error {
    for {
        point, err := stream.Recv()
        if err == io.EOF {
            return nil
        }
        if err != nil {
            return err
        }
        offsetPoint := offset(point)
        if err := stream.Send(offsetPoint); err != nil {
            return err
        }
    }
}和上一篇一样。这篇blog主要在于对生成代码的解析,以上代码。除了proto生成go文件是真实的,以下的代码我都没跑过。用番茄扔我吧!
用了两篇Blog,基本对gRPC框架有了一个了解。下一篇,就要回到gonet2框架了!
看看在框架里,是怎样使用gRPC的吧!
Gonet2 游戏server框架解析之gRPC提高(5)的更多相关文章
- Gonet2 游戏server框架解析之Agent(3)
		客户端消息在Agent中的预处理流程. Agent定义好的三种请求: //api.go var RCode = map[int16]string{ 0: "heart_beat_req&qu ... 
- Leaf - 一个由 Go 语言编写的开发效率和执行效率并重的开源游戏服务器框架
		转自:https://toutiao.io/posts/0l7l7n/preview Leaf 游戏服务器框架简介 Leaf 是一个由 Go 语言(golang)编写的开发效率和执行效率并重的开源游戏 ... 
- 游戏server之server优化思路
		本文仅仅是提供一些游戏server优化思路,当中一些思路是用在不同场合的,不是同个架构的.须要依据应用场景选用合适方式. 本文的引用的文章都是在自己写的在本博客内的.也都是上线开几百个服的成熟项目的. ... 
- AlexeyAB DarkNet YOLOv3框架解析与应用实践(六)
		AlexeyAB DarkNet YOLOv3框架解析与应用实践(六) 1. Tiny Darknet 听过很多人谈论SqueezeNet. SqueezeNet很酷,但它只是优化参数计数.当大多数高 ... 
- [转载]iOS 10 UserNotifications 框架解析
		活久见的重构 - iOS 10 UserNotifications 框架解析 TL;DR iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ... 
- iOS 10 UserNotifications 框架解析
		摘自:https://onevcat.com/2016/08/notification/ iOS 10 中以前杂乱的和通知相关的 API 都被统一了,现在开发者可以使用独立的 UserNotifica ... 
- Poco::TCPServer框架解析
		Poco::TCPServer框架解析 POCO C++ Libraries提供一套 C++ 的类库用以开发基于网络的可移植的应用程序,功能涉及线程.文件.流,网络协议包括:HTTP.FTP.SMTP ... 
- Scut游戏server引擎Unity3d访问
		Scut提供Unity3d Sdk包.便利的高速发展和Scut游戏server对接: 看Unity3d示为以下的比率: 启动Unity3d项目 打开Scutc.svn\SDK\Unity3d\Asse ... 
- 游戏UI框架设计(三) : 窗体的层级管理
		游戏UI框架设计(三) ---窗体的层级管理 UI框架中UI窗体的"层级管理",最核心的问题是如何进行窗体的显示管理.窗体(预设)的显示我们前面定义了三种类型: 普通.隐藏其他.反 ... 
随机推荐
- springMVC小项目实例
			一.什么是 Spring MVC Spring MVC 属于 SpringFrameWork 的后续产品,已经融合在 Spring Web Flow 里面,是一个强大灵活的 Web 框架.Spring ... 
- Docker中免去sudo的设置方法
			Add the docker group if it doesn't already exist: sudo groupadd docker Add the connected user " ... 
- Remember the Word UVALive - 3942  DP_字典树
			每个小单词的长度都是小于等于100的,这是个重要的突破口. Code: #include <cstdio> #include <algorithm> #include < ... 
- HDU-4221  Greedy? 贪心 从元素的相对位置开始考虑
			题目链接:https://cn.vjudge.net/problem/HDU-4221 题意 给n个活动,每个活动需要一段时间C来完成,并且有一个截止时间D 当完成时间t大于截止时间完成时,会扣除t- ... 
- 《Linux 进程间通信》命名管道:FIFO
			命名管道的主要用途:不相关的进程之间交换数据. 命令行上创建命名管道: $ mkfifo filename 程序中创建命名管道: #include <sys/types.h> #incl ... 
- wordcontent结对编程
			合作者:201631062625 201631062127 代码地址:https://gitee.com/yzpdegit/ts 本次作业链接:https://www.cnblogs.com/yang ... 
- 一线 | 中国联通宣布首批5G手机到位
			腾讯<一线> 作者郭晓峰 据中国联通相关人士今日透露,中国联通用于 5G 友好体验的首批合作 5G 手机全部到位.有 12 个品牌共 15 款 5G 手机及 5G CPE,包括.华为. O ... 
- Vue过渡与动画
			通过 Vue.js 的过渡系统,可以在元素从 DOM 中插入或移除时自动应用过渡效果.Vue.js 会在适当的时机为你触发 CSS 过渡或动画,你也可以提供相应的 JavaScript 钩子函数在过渡 ... 
- 紫书 例题8-9 UVa 1451 (数形结合)
			这道题用了数形结合, 真的牛逼, 完全想到不到还可以这么做 因为题目求的是平均值, 是总数除以个数, 这个时候就可以联系 到斜率, 也就是说转化为给你一堆点, 让你求两点之间的最大斜率 要做两个处理 ... 
- 推断扫描后的内容是否是URL
			扫描的明明是Url.竟然当文本给处理了,看来正则没有通过. 扫描二维码后,我參考了QQ的效果.分了三种:网页地址.文件下载地址,文本信息:为了实现这样的效果.我 发现有非常多url非常奇葩.所以就想找 ... 
