due Game Server Framework
repository·main·Indexed 21 days ago
https://github.com/dobyte/dueA high-performance, distributed game server framework written in Go. It features a modular architecture consisting of Gate (gateway), Node (core business logic), and Mesh (stateless microservices). The framework supports multiple protocols (TCP, KCP, WS), service registries (Consul, Etcd, Nacos), and communication schemes (gRPC, RPCX), utilizing a unified packet format for efficient networking.
What's inside due
- due is a lightweight, high-performance distributed game server framework developed in Go. It uses a modular design inspired by Kratos to provide a standardized and efficient solution for game server development. The framework is designed to handle complex distributed architectures, supporting various protocols (TCP, KCP, WS), service registries (Consul, Etcd, Nacos), and communication schemes (gRPC, RPCX).
Understand the due communication protocol format
mainThe framework uses a unified packet format:
size + header + route + seq + message.Data Packet Structure
- size (4 bytes): Fixed length indicating packet size.
- header (1 byte):
h(1 bit): Heartbeat flag.%x0for data packets,%x1for heartbeat packets.extcode(7 bits): Extended operation code.
- route (1, 2, or 4 bytes): Message routing. Defaults to 2 bytes (configurable via
packet.routeBytes). Heartbeat packets do not have a route. - seq (0, 1, 2, or 4 bytes): Message sequence number. Defaults to 2 bytes (configurable via
packet.seqBytes). Used for request/response confirmation. Heartbeat packets do not have a seq. - message data (n bytes): The actual payload.
Heartbeat Packet Structure
- size (4 bytes)
- header (1 byte)
- extcode (7 bits)
- heartbeat time (8 bytes): Server time in nanoseconds (ns). This is automatically handled by the network layer.
Understand the Gate, Node, and Mesh architecture
mainThe due framework organizes distributed services into three primary roles:
- Gate: The gateway server. It manages client connections, receives routed messages from clients, and dispatches them to the appropriate Node instances.
- Node: The core component of the cluster. It handles the primary business logic. Nodes can be stateful (requiring careful handling during updates/restarts) or stateless (behaving similarly to Mesh services).
- Mesh: Microservices used for stateless business logic. While Nodes can perform Mesh functions, Mesh is specifically optimized for statelessness, allowing for easier scaling and updates.
Install the required toolchains for due
mainDepending on your development needs (e.g., developing Mesh microservices with Protobuf or gRPC), you may need to install several tools.
1. Protobuf Compiler
- Linux:
apt install -y protobuf-compiler - MacOS:
brew install protobuf - Windows: Download from GitHub releases.
2. Go Code Generation Tools
Install these using
go install:# Protobuf Go generator go install google.golang.org/protobuf/cmd/protoc-gen-go@latest # gRPC generator go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest # RPCX generator go install github.com/rpcxio/protoc-gen-rpcx@latest # GORM DAO generator go install github.com/dobyte/gorm-dao-generator@latest # MongoDB DAO generator go install github.com/dobyte/mongo-dao-generator@latest- Linux:
Use the File Configuration Source in Go
mainTo implement file-based configuration in your application, follow these steps:
- Initialize the Global Configurator: In your
init()function, useconfig.SetConfiguratorwith a new configurator that includesfile.NewSource(). - Store/Update Configuration: Use
config.Store(ctx, name, filepath, data)to write or update configuration values. Thedataparameter is amap[string]any. - Retrieve Configuration: Use
config.Get(key, defaultValue)to fetch values. The returned value can be cast to specific types (e.g.,.String()).
Important: When performing rapid updates and reads, allow a small delay (e.g.,
time.Sleep) to ensure file system synchronization/hot-updates are processed.package main import ( "context" "github.com/dobyte/due/v2/config" "github.com/dobyte/due/v2/config/file" "github.com/dobyte/due/v2/log" "time" ) func init() { // Set the global configurator with the file source config.SetConfigurator(config.NewConfigurator(config.WithSources(file.NewSource()))) } func main() { var ( ctx = context.Background() name = file.Name filepath = "config.toml" ) // Update/Store configuration if err := config.Store(ctx, name, filepath, map[string]any{ "timezone": "Local", }); err != nil { log.Errorf("store config failed: %v", err) return } time.Sleep(5 * time.Millisecond) // Read configuration timezone := config.Get("config.timezone", "UTC").String() log.Infof("timezone: %s", timezone) // Update configuration again if err := config.Store(ctx, name, filepath, map[string]any{ "timezone": "UTC", }); err != nil { log.Errorf("store config failed: %v", err) return } time.Sleep(5 * time.Millisecond) // Read updated configuration timezone = config.Get("config.timezone", "UTC").String() log.Infof("timezone: %s", timezone) }- Initialize the Global Configurator: In your
Configure the File-based Configuration Center
mainThe
fileconfiguration source allows you to manage configurations using local files or directories. It supports multiple formats includingjson,yaml,toml, andxml.Key features:
- Supports reading, modifying, and hot-updating configurations.
- Supports watching for file changes.
- Supports different read/write modes.
Note: Hot-updates are not supported across a cluster using this file-based source; they are local to the instance.
To use the file source, configure the
[config.file]section in your configuration:path: The path to the configuration file or directory.mode: The access mode. Options areread-only(default),write-only, orread-write.
# 配置中心 [config] # 文件配置 [config.file] # 配置文件或配置目录路径 path = "./config" # 读写模式。可选:read-only | write-only | read-write,默认为read-only mode = "read-write"Use Consul for configuration management
mainTo use Consul as a configuration source, initialize the global configurator in your
init()function usingconfig.WithSources(consul.NewSource()).Once initialized, you can use
config.Storeto save configurations to Consul andconfig.Getto retrieve them. The Consul source supports hot-reloading, multiple formats (JSON, YAML, TOML, XML), and cluster-wide hot updates.package main import ( "context" "github.com/dobyte/due/config/consul/v2" "github.com/dobyte/due/v2/config" "github.com/dobyte/due/v2/log" "time" ) func init() { // Set the global configurator with Consul as a source config.SetConfigurator(config.NewConfigurator(config.WithSources(consul.NewSource()))) } func main() { var ( ctx = context.Background() file = "config.toml" name = consul.Name ) // Store/Update configuration in Consul if err := config.Store(ctx, name, file, map[string]any{ "timezone": "Local", }); err != nil { log.Errorf("store config failed: %v", err) return } time.Sleep(5 * time.Millisecond) // Read configuration timezone := config.Get("config.timezone", "UTC").String() log.Infof("timezone: %s", timezone) }Install the etcd registry provider
mainTo use etcd as a service registry in your project, install the v2 package using Go modules:
go get github.com/dobyte/due/registry/etcd/v2@latestInstall the Consul Registry v2
mainTo use Consul as a service registry in your project, install the v2 package using
go get.go get github.com/dobyte/due/registry/consul/v2@latestInstall the Consul configuration center
mainTo use Consul as a configuration source in your project, install the
v2package usinggo get.go get -u github.com/dobyte/due/config/consul/v2@latestInstall the etcd configuration center
mainTo use etcd as a configuration source in your project, install the
v2package using Go modules:go get -u github.com/dobyte/due/config/etcd/v2@latestService Instance Metadata Mapping in Consul
mainWhen using the Consul registry, the
registry.ServiceInstancefields are populated from Consul's serviceMetamap. The following mapping is applied:Consul Meta Key ServiceInstance Field metaFieldIDIDmetaFieldKindKindmetaFieldAliasAliasmetaFieldStateStatemetaFieldWeightWeight(converted to int)metaFieldEventsEvents(JSON unmarshaled)metaFieldServicesServices(JSON unmarshaled)metaFieldEndpointEndpointdefaultMetadataPrefix+suffixMetadata[suffix]Note:
Routesare unmarshaled from theMetamap usingunmarshalMetaRoutes.