due 是一款基于Go语言开发的轻量级、高性能分布式游戏服务器框架。 其中,模块设计方面借鉴了kratos的模块设计思路,旨在为游戏服务器开发提供完善、高效、优雅、标准化的解决方案。 框架自创建至今已在多个企业级游戏项目中上线实践过,稳定性有充分的保障。

在due交流群中经常有小伙伴提及到Gate、Node、Mesh之间到底是个什么关系,这里就做一个统一的解答
在due框架中,通信协议统一采用size+header+route+seq+message的格式:
1.数据包
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+---------------------------------------------------------------+-+-------------+-------------------------------+-------------------------------+
| size |h| extcode | route | seq |
+---------------------------------------------------------------+-+-------------+-------------------------------+-------------------------------+
| message data ... |
+-----------------------------------------------------------------------------------------------------------------------------------------------+
2.心跳包
0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7
+---------------------------------------------------------------+-+-------------+---------------------------------------------------------------+
| size |h| extcode | heartbeat time (ns) |
+---------------------------------------------------------------+-+-------------+---------------------------------------------------------------+
size: 4 bytes
header: 1 bytes
h: 1 bit
extcode: 7 bit
route: 1 bytes | 2 bytes | 4 bytes
seq: 0 bytes | 1 bytes | 2 bytes | 4 bytes
message data: n bytes
heartbeat time: 8 bytes
1.安装protobuf编译器(使用场景:开发mesh微服务)
$ apt install -y protobuf-compiler
$ protoc --version # Ensure compiler version is 3+
$ brew install protobuf
$ protoc --version # Ensure compiler version is 3+
2.安装protobuf go代码生成工具(使用场景:开发mesh微服务)
go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
3.安装grpc代码生成工具(使用场景:使用GRPC组件开发mesh微服务)
go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
4.安装rpcx代码生成工具(使用场景:使用RPCX组件开发mesh微服务)
go install github.com/rpcxio/protoc-gen-rpcx@latest
5.安装gorm dao代码生成工具(使用场景:使用GORM作为数据库orm)
go install github.com/dobyte/gorm-dao-generator@latest
6.安装mongo dao代码生成工具(使用场景:使用MongoDB作为数据库orm)
go install github.com/dobyte/mongo-dao-generator@latest
1.功能介绍
配置中心主要定位于业务的配置管理,提供快捷灵活的配置方案。支持完善的读取、修改、删除、热更新等功能。
2.支持组件
1.功能介绍
注册中心用于集群实例的服务注册和发现。支撑整个集群的无感知停服、重启、动态扩容等功能。
2.支持组件
1.功能介绍
网络模块主要以组件的形式集成于网关模块,为网关提供灵活的网络通信支持。
2.支持组件
下面我们就通过两段简单的代码来体验一下due的魅力,Let's go~~
1.启动组件
docker-compose up
docker-compose.yaml文件已在docker目录中备好,可以直接取用
2.获取框架
go get -u github.com/dobyte/due/v2@latest
go get -u github.com/dobyte/due/locate/redis/v2@latest
go get -u github.com/dobyte/due/network/ws/v2@latest
go get -u github.com/dobyte/due/registry/consul/v2@latest
go get -u github.com/dobyte/due/transport/rpcx/v2@latest
3.构建Gate服务器
package main
import (
"github.com/dobyte/due/locate/redis/v2"
"github.com/dobyte/due/network/ws/v2"
"github.com/dobyte/due/registry/consul/v2"
"github.com/dobyte/due/v2"
"github.com/dobyte/due/v2/cluster/gate"
)
func main() {
// 创建容器
container := due.NewContainer()
// 创建服务器
server := ws.NewServer()
// 创建用户定位器
locator := redis.NewLocator()
// 创建服务发现
registry := consul.NewRegistry()
// 创建网关组件
component := gate.NewGate(
gate.WithServer(server),
gate.WithLocator(locator),
gate.WithRegistry(registry),
)
// 添加网关组件
container.Add(component)
// 启动容器
container.Serve()
}
4.启动Gate服务器
$ go run main.go
____ __ ________
/ __ \/ / / / ____/
/ / / / / / / __/
/ /_/ / /_/ / /___
/_____/\____/_____/
┌──────────────────────────────────────────────────────┐
| [Website] https://github.com/dobyte/due |
| [Version] v2.1.0 |
└──────────────────────────────────────────────────────┘
┌────────────────────────Global────────────────────────┐
| PID: 27159 |
| Mode: debug |
└──────────────────────────────────────────────────────┘
┌─────────────────────────Gate─────────────────────────┐
| Name: gate |
| Link: 172.22.243.151:46545 |
| Server: [ws] 0.0.0.0:3553 |
| Locator: redis |
| Registry: consul |
└──────────────────────────────────────────────────────┘
5.构建Node服务器
package main
import (
"fmt"
"github.com/dobyte/due/locate/redis/v2"
"github.com/dobyte/due/registry/consul/v2"
"github.com/dobyte/due/v2"
"github.com/dobyte/due/v2/cluster/node"
"github.com/dobyte/due/v2/codes"
"github.com/dobyte/due/v2/log"
"github.com/dobyte/due/v2/utils/xtime"
)
const greet = 1
func main() {
// 创建容器
container := due.NewContainer()
// 创建用户定位器
locator := redis.NewLocator()
// 创建服务发现
registry := consul.NewRegistry()
// 创建节点组件
component := node.NewNode(
node.WithLocator(locator),
node.WithRegistry(registry),
)
// 初始化监听
initListen(component.Proxy())
// 添加节点组件
container.Add(component)
// 启动容器
container.Serve()
}
// 初始化监听
func initListen(proxy *node.Proxy) {
proxy.Router().AddRouteHandler(greet, false, greetHandler)
}
type greetReq struct {
Message string `json:"message"`
}
type greetRes struct {
Code int `json:"code"`
Message string `json:"message"`
}
func greetHandler(ctx node.Context) {
req := &greetReq{}
res := &greetRes{}
defer func() {
if err := ctx.Response(res); err != nil {
log.Errorf("response message failed: %v", err)
}
}()
if err := ctx.Parse(req); err != nil {
log.Errorf("parse request message failed: %v", err)
res.Code = codes.InternalError.Code()
return
}
log.Info(req.Message)
res.Code = codes.OK.Code()
res.Message = fmt.Sprintf("I'm server, and the current time is: %s", xtime.Now().Format(xtime.DateTime))
}
6.启动Node服务器
$ go run main.go
____ __ ________
/ __ \/ / / / ____/
/ / / / / / / __/
/ /_/ / /_/ / /___
/_____/\____/_____/
┌──────────────────────────────────────────────────────┐
| [Website] https://github.com/dobyte/due |
| [Version] v2.1.0 |
└──────────────────────────────────────────────────────┘
┌────────────────────────Global────────────────────────┐
| PID: 27390 |
| Mode: debug |
└──────────────────────────────────────────────────────┘
┌─────────────────────────Node─────────────────────────┐
| Name: node |
| Link: 172.22.243.151:37901 |
| Codec: json |
| Locator: redis |
| Registry: consul |
| Encryptor: - |
| Transporter: - |
└──────────────────────────────────────────────────────┘
7.构建测试客户端
```go package main
import ( "fmt" "github.com/dobyte/due/eventbus/nats/v2" "github.com/dobyte/due/network/ws/v2" "github.com/dobyte/due/v2" "github.com/dobyte/due/v2/cluster" "github.com/dobyte/due/v2/cluster/client" "github.com/dobyte/due/v2/eventbus" "github.com/dobyte/due/v2/log" "github.com/dobyte/due/v2/utils/xtime" "time" )
const greet = 1
func main() { // 初始化事件总线 eventbus.SetEventbus(nats.NewEventbus()) // 创建容器 container := due.NewContainer() // 创建客户端组件 component := client.NewClient( client.WithClient(ws.NewClient()), ) // 初始化监听 initListen(component.Proxy()) // 添加客户端组件 container.Add(component) // 启动容器 container.Serve() }
// 初始化监听 func initListen(proxy *client.Proxy) { // 监听组件启动 proxy.AddHookListener(cluster.Start, startHandler) // 监听连接建立 proxy.AddEventListener(cluster.Connect, connectHandler) // 监听消息回复 proxy.AddRouteHandler(greet, greetHandler) }
// 组件启动处理器 func startHandler(proxy *client.Proxy) { if _, err := proxy.Dial(); err != nil { log.Errorf("gate connect failed: %v", err) return } }
// 连接建立处理器 func connectHandler(conn *client.Conn) { doPushMessage(conn) }
// 消息回复处理器 func greetHandler(ctx *client.Context) { res := &greetRes{}
if err := ctx.Parse(res); err != nil { log.Errorf("invalid response message, err: %v", err) return }
if res.Code != 0 { log.Errorf("node response failed, code: %d", res.Code) return }
log.Info(res.Message)
time.AfterFunc(time.Second, func() { doPushMessage(ctx.Conn()) }) }
// 推送消息 func doPushMessage(conn *client.Conn) { err := conn.Push(&cluster.Message{ Route: 1, Data: &greetReq{ Message: fmt.Sprintf("I'm client, and the current time is: %s", xtime.Now().Format(xtime.DateTime