cherry

repository·master·Indexed 21 days ago

https://github.com/cherry-game/cherry

A high-performance, distributed game server framework written in Go that utilizes the Actor model for scalability and ease of development. It includes features for cluster node discovery (supporting default, nats, and etcd modes), a simplified network packet parser, and support for Protocol Buffers.

Tokens
12.6K
Snippets
54
Records
66
Agent score
73%

What's inside cherry

  1. Configure and manage Pomelo heartbeats

    master

    Heartbeat packets have a length of 0 and an empty body.

    • Interval: The server can configure a heartbeat interval. After the handshake, the client initiates the first heartbeat. Both sides send a heartbeat packet after the specified interval.
    • Timeout: The timeout threshold is 2x the heartbeat interval.
    • Behavior: If a timeout occurs, the server does not automatically disconnect the client. The client is responsible for deciding whether to disconnect based on its own strategy.
  2. Decode generic maps into Go structures with mapstructure

    master

    Use mapstructure to decode generic map[string]interface{} values into specific native Go structures. This is particularly useful when dealing with data streams (like JSON or Gob) where the exact structure of the data is unknown until certain fields (e.g., a type field) are inspected.

    Instead of performing multiple passes over a data stream, you can decode the initial data into a map, inspect the necessary keys, and then use mapstructure.Decode to populate the final target structure.

    // Conceptual workflow:
    // 1. Decode raw data (e.g., JSON) into a map[string]interface{}
    // 2. Inspect the map to determine the target type
    // 3. Use mapstructure.Decode(mapData, &targetStruct) to populate the struct
  3. How the Actor Model works

    master

    Every Actor runs in its own dedicated goroutine and processes messages serially from three independent FIFO queues:

    1. Local: Handles requests from clients to the Actor.
    2. Remote: Handles RPC calls between Actors across different nodes.
    3. Event: Handles decoupled system notifications.

    Actors can also create Child Actors which share the parent's lifecycle and route messages through the parent. Additionally, Actors can register Timers and cron jobs that are guaranteed to execute on the Actor's specific goroutine.

    type MyActor struct {
        capp.ActorLogger
    }
    
    func (p *MyActor) OnInit() {
        p.Local().Register("myHandler", p.handle)
        p.Remote().Register("myRemote", p.remote)
        p.EventRegister("eventName", p.onEvent)
    }
    
    func (p *MyActor) handle(session *cproto.Session, req *pb.MyReq) {
        p.Response(session, &pb.MyResp{Value: "ok"})
    }
  4. How AppBuilder and the lifecycle work

    master

    Cherry uses a Builder API to chain component registration, serialization settings, and Actor definitions before starting the service.

    Lifecycle Flow: RegisterSetInitOnAfterInit → (Running) → OnBeforeStopOnStop

    Components are stopped in the reverse order of their registration. The application supports graceful shutdown triggered by SIGINT, SIGQUIT, or SIGTERM signals.

    cherry.Configure("etc/profile/dev.json", "game-1", true, cherry.Cluster).
        Register(myComponent).
        SetSerializer(cherryFacade.NewProtobuf()).
        AddActors(myActor).
        Startup()
  5. Understand the Pomelo message header and flag bits

    master

    The message layer encapsulates the message header, which includes a flag, message id, and route.

    The Flag Byte

    The flag is the first byte of the message header. It uses 4 bits:

    • Message Type (3 bits): Identifies the message category (0-7). Common types are request, notify, response, and push (0-3).
    • Route Compression (1 bit): The last bit indicates if the route is compressed (1) or uncompressed (0).

    Message Types and Header Structures

    • Request: Includes flag, message id, and route.
    • Response: Includes flag, message id, and route.
    • Notify: Includes flag and route (no message id).
    • Push: Includes flag and route (no message id).

    Note: message id uses varints 128 variable-length encoding (0-5 bytes). route length varies from 0-255 bytes.

  6. Configure Discovery modes

    master

    Cherry's Discovery service manages cluster node discovery and membership. You can choose between three modes depending on your environment:

    • default: Used for single-process development or testing. It reads node information directly from the profile configuration.
    • nats: Used for multi-node production environments. It uses a Master/Worker pattern via NATS.
    • etcd: Used for multi-node production environments requiring etcd (distributed lease + watch).
    | Mode | Value | Use Case |
    |------|---------|-----------|
    | `default` | `default` | Single-process development/testing |
    | `nats` | `nats` | Multi-node production (NATS master/worker) |
    | `etcd` | `etcd` | Multi-node production (etcd lease + watch) |
  7. Understand the Pomelo binary protocol layers

    master

    The Pomelo binary protocol consists of two distinct encoding layers: package and message.

    1. package layer: Encapsulates data for connection-oriented binary streams (like TCP). It handles the handshake process, heartbeats, and data transmission encoding. The resulting package can be transmitted via TCP, WebSocket, etc.
    2. message layer: Encapsulates the message header, including the route and message id (requestId). It implements route compression and Protobuf compression. The output of the message layer is passed to the package layer for transmission.

    Note: The message layer encoding is optional and can be replaced with other binary formats without affecting the package layer's ability to encode and send data.

  8. Use route compression in Pomelo messages

    master

    The route field can be compressed or uncompressed, controlled by the last bit of the flag byte.

    • Uncompressed (flag bit = 0): The route starts with a uInt8 byte representing the length of the UTF-8 encoded route string (max 256 bytes).
    • Compressed (flag bit = 1): The route is a uInt16 representing a dictionary index. The actual route string must be looked up in the dictionary provided during the handshake.
  9. Understand the simple network packet structure

    master

    The simple parser implements a simplified network packet structure inspired by zinx. A packet is constructed using a Message ID (MID), the data length, and the actual data payload. This structure is useful for custom network protocol implementations.

    Packet Format:

    • MID: uint32 (4 bytes)
    • DataLen: uint32 (4 bytes)
    • Data: n bytes
  10. How AppBuilder and Lifecycle work

    master

    Cherry uses a builder API to chain component registration, serialization settings, and actor definitions before starting the server.

    Lifecycle Stages: RegisterSetInitOnAfterInit(running)OnBeforeStopOnStop.

    Components are stopped in the reverse order of their registration. The framework supports graceful shutdown via SIGINT, SIGQUIT, or SIGTERM signals.

    cherry.Configure("etc/profile/dev.json", "game-1", true, cherry.Cluster).
        Register(myComponent).
        SetSerializer(cherryFacade.NewProtobuf()).
        AddActors(myActor).
        Startup()
  11. Install Go for cherry development

    master

    Cherry requires Go (version 1.18 or higher recommended). You can install it using one of the following methods:

    After installation, verify it by running go version. To check your environment parameters, run go env.

    Configure Proxy: To ensure reliable dependency downloads, set your GOPROXY:

    go env -w GOPROXY=https://goproxy.cn,direct
    go version
    go env -w GOPROXY=https://goproxy.cn,direct
  12. Install protoc-gen-go for Protocol Buffers

    master

    To use Protocol Buffers with Go in the cherry project, you must install the protoc-gen-go plugin. This allows the protoc compiler to generate Go code from .proto definition files.

    Run the following command to install the latest version via Go:

    go install google.golang.org/protobuf/cmd/protoc-gen-go@latest