due Game Server Framework

repository·main·Indexed 21 days ago

https://github.com/dobyte/due

A 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.

Tokens
67.9K
Snippets
272
Records
307
Agent score
75%

What's inside due

  1. Overview of the due framework

    main
    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).
  2. Understand the due communication protocol format

    main

    The 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. %x0 for data packets, %x1 for 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.
  3. Understand the Gate, Node, and Mesh architecture

    main

    The 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.
  4. Install the required toolchains for due

    main

    Depending 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
  5. Use the File Configuration Source in Go

    main

    To implement file-based configuration in your application, follow these steps:

    1. Initialize the Global Configurator: In your init() function, use config.SetConfigurator with a new configurator that includes file.NewSource().
    2. Store/Update Configuration: Use config.Store(ctx, name, filepath, data) to write or update configuration values. The data parameter is a map[string]any.
    3. 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)
    }
  6. Configure the File-based Configuration Center

    main

    The file configuration source allows you to manage configurations using local files or directories. It supports multiple formats including json, yaml, toml, and xml.

    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 are read-only (default), write-only, or read-write.
    # 配置中心
    [config]
        # 文件配置
        [config.file]
            # 配置文件或配置目录路径
            path = "./config"
            # 读写模式。可选:read-only | write-only | read-write,默认为read-only
            mode = "read-write"
  7. Use Consul for configuration management

    main

    To use Consul as a configuration source, initialize the global configurator in your init() function using config.WithSources(consul.NewSource()).

    Once initialized, you can use config.Store to save configurations to Consul and config.Get to 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)
    }
  8. Service Instance Metadata Mapping in Consul

    main

    When using the Consul registry, the registry.ServiceInstance fields are populated from Consul's service Meta map. The following mapping is applied:

    Consul Meta KeyServiceInstance Field
    metaFieldIDID
    metaFieldKindKind
    metaFieldAliasAlias
    metaFieldStateState
    metaFieldWeightWeight (converted to int)
    metaFieldEventsEvents (JSON unmarshaled)
    metaFieldServicesServices (JSON unmarshaled)
    metaFieldEndpointEndpoint
    defaultMetadataPrefix + suffixMetadata[suffix]

    Note: Routes are unmarshaled from the Meta map using unmarshalMetaRoutes.