introduce

What is the Nano?

Golang’s lightweight, convenient, high-performance game server framework.

The Nano is a lightweight server framework that is best suited for web games, social games, and mobile games. It’s not just games, of course. The Nano is also ideal for developing high real-time Web applications.

Most importantly, you can develop the Golang game server framework through this entry

The sample warehouse

cloud-native-game-server

Use Nano to quickly build a Chat Room

One sentence describes the Nano term

  • Components (Component) :nanoThe functionality of the application is loosely coupledComponentOf eachComponentAccomplish some functionality.
  • Handler: It is defined inComponentTo handle the concrete business logic.
  • Routing (Route) : Identifies aThe specific serviceOr the client receives a push message from the serverlocation.
  • Session (Session) : After the client connects to the server, a session is set up to hold some context information during the connection. Release after the connection is disconnected.
  • Group (Group) :GroupYou can view it as aSessionFor scenarios where push messages need to be broadcast.
  • Request (RequestResponse (),Response), notify (Notify), push (Push) :NanoFour message types in the.

The lifecycle of the component

type DemoComponent struct{}

func (c *DemoComponent) Init()           {}
func (c *DemoComponent) AfterInit()      {}
func (c *DemoComponent) BeforeShutdown() {}
func (c *DemoComponent) Shutdown()       {}
Copy the code
  • Init: called when the component is initialized.
  • AfterInit: Called after the component has been initialized.
  • BeforeShutdown: The component is called before it is destroyed.
  • Shutdown: called when the component is destroyed.

The entire component lifecycle looks pretty clear.

Describe the business in one sentence

  • Users can join specific rooms
  • The user can see all members in the room
  • Users can send messages in the current room

Business specific analysis

  • Users can join specific rooms
    • Request to join (Request) – >RequestThe correspondingnanoA message type
    • Required response (Response-> Allow to join ->ResponseThe correspondingnanoA message type
  • The user can see all members in the room
    • Server active push (Push) All members of the roomMembers -> PushThe correspondingnanoA message type
    • Server active broadcast 📢(Push) Other members of the room, there are new people to joinNew user
  • Users can send messages in the current room
    • User sent (Notify) message to current room ->NotifyThe correspondingnanoA type of message that does not require the server to respond to it
    • The server will message 📢(Push) To the rest of the room

So far, we’ve seen the business, and from the business we’ve seen the four messaging types of applications for the Nano.

Demo source parsing

demo/1-nano-chat

type (
  // The definition of a room
	Room struct {
    // Manage all sessions in the room
		group *nano.Group
	}

  // RoomManager represents a component containing a bunch of rooms. It is a Nano component that hooks logic during its lifetime
	RoomManager struct {
    // Inherit the Nano component and have a full life cycle
    component.Base
    // After the component is initialized, do some scheduled tasks
    timer *scheduler.Timer
    // Multiple rooms, key-value storage
		rooms map[int]*Room
	}

  // represents a message definition sent by a user
	UserMessage struct {
		Name    string `json:"name"`
		Content string `json:"content"`
	}

  // Receive a new user message (broadcast) when a new user joins the room
	NewUser struct {
		Content string `json:"content"`
	}

	// Contains the UID of all members
	AllMembers struct {
		Members []int64 `json:"members"`
	}

	// indicates the response result of joining the room server
	JoinResponse struct {
		Code   int    `json:"code"`
		Result string `json:"result"`
	}

  // Traffic statistics
	Stats struct {
    // Inherit the Nano component and have a full life cycle
    component.Base
    // After the component is initialized, do some scheduled tasks
    timer         *scheduler.Timer
    // Egress traffic statistics
    outboundBytes int
    // Statistics on incoming traffic
		inboundBytes  int})// Count the outbound traffic, which will define the pipeline to nano
func (stats *Stats) outbound(s *session.Session, msg *pipeline.Message) error {
	stats.outboundBytes += len(msg.Data)
	return nil
}

// Count the entry traffic, which will define the pipeline to nano
func (stats *Stats) inbound(s *session.Session, msg *pipeline.Message) error {
	stats.inboundBytes += len(msg.Data)
	return nil
}

// When the component is initialized, it is called
// The flow rate at the lower exit and inlet is printed per minute
func (stats *Stats) AfterInit(a) {
	stats.timer = scheduler.NewTimer(time.Minute, func(a) {
		println("OutboundBytes", stats.outboundBytes)
		println("InboundBytes", stats.outboundBytes)
	})
}

func (st *Stats) Nil(s *session.Session, msg []byte) error {
	return nil
}

const (
  // Test the room ID
  testRoomID = 1
  // Test room key
	roomIDKey  = "ROOM_ID"
)

// Initialize RoomManager
func NewRoomManager(a) *RoomManager {
	return &RoomManager{
		rooms: map[int]*Room{},
	}
}

// RoomManager will be called when the initialization is complete
func (mgr *RoomManager) AfterInit(a) {
  // This will be called when the user disconnects
  // Remove it from the room
	session.Lifetime.OnClosed(func(s *session.Session) {
		if! s.HasKey(roomIDKey) {return
		}
    room := s.Value(roomIDKey).(*Room)
    // Remove this session
		room.group.Leave(s)
  })
  
  // A scheduled task to print the number of members in the room per minute
	mgr.timer = scheduler.NewTimer(time.Minute, func(a) {
		for roomId, room := range mgr.rooms {
			println(fmt.Sprintf("UserCount: RoomID=%d, Time=%s, Count=%d",
				roomId, time.Now().String(), room.group.Count()))
		}
	})
}

// Add business logic to the room
func (mgr *RoomManager) Join(s *session.Session, msg []byte) error {
	// Note: The demo only adds testRoomID
	room, found := mgr.rooms[testRoomID]
	if! found { room = &Room{ group: nano.NewGroup(fmt.Sprintf("room-%d", testRoomID)),
		}
		mgr.rooms[testRoomID] = room
	}

	fakeUID := s.ID() // This is just a simulation of the UID with the sessionId
	s.Bind(fakeUID)   // Bind the UID to the session
  s.Set(roomIDKey, room) // Set the room to which the current session is associated
  // Push all members of the room to the current session
  s.Push("onMembers", &AllMembers{Members: room.group.Members()})
	// Other members of the broadcast room, there are new people to join
  room.group.Broadcast("onNewUser", &NewUser{Content: fmt.Sprintf("New user: %d", s.ID())})
	// Add the session to the room group
  room.group.Add(s)
  // Reply that the current user joined successfully
	return s.Response(&JoinResponse{Result: "success"})}// Synchronize the latest news to all members of the room
func (mgr *RoomManager) Message(s *session.Session, msg *UserMessage) error {
	if! s.HasKey(roomIDKey) {return fmt.Errorf("not join room yet")
	}
  room := s.Value(roomIDKey).(*Room)
  / / radio
	return room.group.Broadcast("onMessage", msg)
}

func main(a) {
  // Create a new component container instance
  components := &component.Components{}
  // Register the component
	components.Register(
    // Component instance
    NewRoomManager(),
    // Override the component name
    component.WithName("room"),
    // Override the handler name of the component, in lowercase
		component.WithNameFunc(strings.ToLower),
	)
	// Traffic statistics
	pip := pipeline.New()
  var stats = &stats{}
  // Join Outbound pipeline
  pip.Outbound().PushBack(stats.outbound)
  // Enter the Inbound pipeline
	pip.Inbound().PushBack(stats.inbound)
	// Register the traffic statistics component
	components.Register(stats, component.WithName("stats"))
  // Set the log print format
  log.SetFlags(log.LstdFlags | log.Llongfile)
  // Web static resource processing
	http.Handle("/web/", http.StripPrefix("/web/", http.FileServer(http.Dir("web"))))
  / / start the nano
	nano.Listen(": 3250"./ / the port number
		nano.WithIsWebsocket(true), // Whether to use Websocket
		nano.WithPipeline(pip), // Whether to use pipeline
		nano.WithCheckOriginFunc(func(_ *http.Request) bool { return true }), // Allow cross-domain
		nano.WithWSPath("/nano"), // WebSocket connection address
		nano.WithDebugMode(),  // Enable the debug mode
		nano.WithSerializer(json.NewSerializer()), // Use the JSON serializer
		nano.WithComponents(components), // Load the component)}Copy the code

The front-end code is very simple, so you can look directly at cloud-native Game-server

Docker builds a development debugging environment

Dockerfile

Dockerfile.dev

FROM Golang: 1.14

WORKDIR /workspace

# ali cloud
RUN go env -w GO111MODULE=on
RUN go env -w GOPROXY=https://mirrors.aliyun.com/goproxy/,direct

# debug
RUN go get github.com/go-delve/delve/cmd/dlv

# live reload
RUN go get -u github.com/cosmtrek/air

# nano
RUN go mod init cloud-native-game-server
RUN go get github.com/lonng/nano@master
Copy the code

Building the Image:

docker build -f Dockerfile.dev -t cloud-native-game-server:dev .
Copy the code

docker-compose.yaml

version: "3.4"
services:

  demo:
    image: cloud-native-game-server:dev
    command: > bash -c "cp ./go.mod ./go.sum app/ && cd app/demo/${DEMO} && ls -la && air -c .. /.. /.air.toml -d"    volumes:
    - ./:/workspace/app
    ports:
      - 3250: 3250
  
  demo-debug:
    image: cloud-native-game-server:dev
    command: > bash -c "cp ./go.mod ./go.sum app/ && cd app/demo/${DEMO} && ls -la && dlv debug main.go --headless --log -l 0.0.0.0:2345 - API - version = 2"    volumes:
    - ./:/workspace/app
    ports:
      - 3250: 3250
      - 2345: 2345
    security_opt:
      - "seccomp:unconfined"
Copy the code

Start the development environment (Live Reload support)

If I want to develop 1-nano-chat
DEMO=1-nano-chat docker-compose up demo
Copy the code

Go to localhost:3250/web/ to see the effect.

Start the mode environment

If I want to debug 1-nano-chat
DEMO=1-nano-chat docker-compose up demo-debug
Copy the code

reference

  • The official making
  • How to Build your first Nano App
  • The official Demo – starx – chat – Demo