gslang背景知识
gslang是采用go语言编写的RPC描述语言,最初它被用来描述gscluster网络服之间的接口调用契约;而gscluster项目本身是我在tap4fun时开发的一个游戏
服务端框架,所以能够看到gscluster里面有很多与游戏相关的概念。但是框架本身是为mico-service
而生的,所以你可以将它应用到任何你觉得合适的地方,不必局限于游戏服务器。
gslang是一个完整的RPC描述语言,它与thrift定位相同。没有直接采用thrift的原因是:
- 在开发gslang的时候,thrift对golang的支持并不完善;
- 根据项目需要,需要hack代码生成的过程例如:profile,debug 代码;
gslang前端解析器在github上的位置:这里
语言特性
gslang支持的语言特性有:true/false,byte/sbyte,int16/uint16,int32/uint32,int64/uint64,
float32/float64,table,struct,enum,contract,annotation
基本数据类型
byte/sbyte,int16/uint16,int32/uint32,int64/uint64,float32/float64
自定义数据类型
table/struct被用来定义用户定义数据类型,table与struct的区别在于table可以被用来定义annotation
示例:
// The cluster message type
struct Message {
ID byte; //message Type
Content []byte; //content data
}
进一步示例请参考:这里
注意:gslang所有数据类型与网络传输格式无关,它只关心接口语言的定义
枚举类型
gslang支持枚举类型,并且可以指定枚举底层数据类型,可选择的数据类型有:byte/sbyte,int16/uint16,int32/uint32
enum Status(uint16){
Closed(0),
Disconnected(1),
Connecting(2),
InConnected(3),
OutConnected(4),
Online(5),
Offline(6),
Unreachable(7)
}
服务契约、方法
服务通过contract关键字定义:
contract SampleActor {
SayHello(string) -> (bool);
}
- SampleActor :服务名
- SayHello: 方法名,该方法有一个string类型输入参数和一个bool类型返回值
多参数:gslang方法支持多输入参数、多返回参数
服务接口集成:服务接口支持继承其它服务器接口,这个和java/C#等语言类似
类型注释(annotation/attribute)
gslang通过类型注释增强了DSL的表达能力,这点与C#/java类似。这个应该是gslang的杀手级特性,它可以被用来指导后端代码生成器生成个性化代码,而不用修改gslang前端编译器部分。在tap4fun的一个内部版本里,通过自定义annotation来决定是否对某个服务器方法生成profile代码。
后端代码生成
如前所述,gslang并不关心底层的序列化方式,也就更不关心底层的网络传输方式。这部分协议由gslang的后端代码生成器决定,一个已知的代码生成后端是gs2go—golang代码生成后端,并且生成代码遵循的协议由gscluster定义。
需要指出的是:gscluster的RPC调用底层数据包本身也是由gslang来描述的,具体情况可以参看这个文件。
构建一个环境
- 安装gsmake,gsmake这个工具的介绍可以看这里
- git clone https://github.com/gsdocker/gscluster
- cd ${gscluster directory}
- gsmake gotest -bench . ./test2
通过上面几步,就构建了一个基本的gsdocker/rpc测试环境。你可以观察生成的*.gs.go文件来了解代码
生成的细节;
gscluster RPC协议细节
struct/table序列化
直接将字段数据按定义顺序递归序列化写入二进制流,没有tag/元数据信息;它的序列化/反序列化完全依赖
于gslang脚本;
基本数据类型序列化
- interger类型 : 按照小端序写入
- string类型: [两字节,后续内容长度] + [UTF-8字符内容]
- 浮点类型:按IEEE-754内存表示序列化
RPC数据包
客户端/mico-service节点之间的rpc调用都被封装成一个一个RPC协议包传输,它的格式定义由gslang
描述为下面形式:
// The gscluster rpc package code types
enum Code {
WhoAmI(0),
Accept(1),
Reject(2),
DHExchange(3),
Call(4),
Return(5),
CER(6),
ErrReturn(7)
}
// The gscluster prc package type define
struct Message {
Code Code; //message Type
Content []byte; //content data
}
struct DHExchange {
ID string;
Content string;
}
struct CER {
Add bool;
Type string; // service type name
Name string; // service name
ID uint32; // srevice id
}
struct Param{
Content []byte;
}
struct Return {
ID uint16;
Service uint32;
Params []Param;
}
struct ErrReturn {
ID uint16;
Service uint32;
UUID []byte;
Code int32;
}
struct Call {
ID uint16;
Method uint16;
Service uint32;
Params []Param;
}
再次强调:gscluster的rpc协议封包本身是由gslang来直接定义的
结构体Call/Return分别用来实现RPC调用请求/应答:
- Call#ID : RPC包序列号,用来匹配应答包;
- Call#Service : Contract协议号
- Call#Method : Method协议号
- Call#Params : 参数列表
Service/Contract协议号与名字的映射关系由底层框架确定:
- 服务器之间的RPC调用,由调用两端通过协商握手协议确定;
- 网关与客户端之间的RPC调用,由于gscluster只支持GS/GW/GC三类服务契约——分别对应服务器/网关/客户端RPC接口——服务协议号被简单的手动分配。
gscluster
gscluster是一个golang实现的mico-service框架:
- 服务端采用基于角色的并发模型,每个客户端在服务器端都有一个actor角色对象与之一一对应。
- 服务节点/客户端之间,通过RPC协议进行通信;
- 内建网关服务,支持基于SLB之类的技术进行接入层的负载均衡;
- 总体上gscluster类似于云风设计的skynet,只不过gscluster在实现服务的时候更方便一些——内建goroutine,而不是开很多lua vm