leaf游戏服务器开发系列,Golang 游戏leaf系列(一) 概述与示例

一、概述

1.Leaf 服务器的设计

Leaf专注于游戏服务器,因此与一些Web服务器开发的设计和考虑有所不同。

在一些游戏服务器中,采用的是分布式架构,即服务器整体被划分为不同的模块,各个模块承担不同的功能,而模块之间通过TCP进行交互。这样的架构能够保证服务器能够在多台机器上部署,单点故障不至于让服务整体崩溃。但是这种服务器有其自身的开发难度,而且有时候做好模块划分并不容易。

Leaf是一体式的框架,连最外围的接入服务器也被整合在一起。虽然Leaf中间也划分了不同模块,但是他们是通过InnerRpc进行通讯的。介于现在手游兴起,单机性能提升,不少游戏服务器所需要的性能并不十分苛刻,所以Leaf在这方面的简洁与易于开发有很大的优势。

一个 Leaf 开发的游戏服务器由多个模块组成(例如 LeafServer),模块有以下特点:

每个模块运行在一个单独的 goroutine 中

模块间通过一套轻量的 RPC 机制通讯(leaf/chanrpc)

Leaf 不建议在游戏服务器中设计过多的模块。无论封装多么精巧,跨 goroutine 的调用总不能像直接的函数调用那样简单直接,因此除非必要我们不要构建太多的模块,模块间不要太频繁的交互。模块在 Leaf 中被设计出来最主要是用于划分功能而非利用多核,Leaf 认为在模块内按需使用 goroutine 才是多核利用率问题的解决之道

游戏服务器在启动时进行模块的注册,例如:

leaf.Run(

game.Module,

gate.Module,

login.Module,

)

这里按顺序注册了 game、gate、login 三个模块。每个模块都需要实现接口:

type Module interface {

OnInit()

OnDestroy()

Run(closeSig chan bool)

}

Leaf 首先会在同一个 goroutine 中按模块注册顺序执行模块的 OnInit 方法,等到所有模块 OnInit 方法执行完成后则为每一个模块启动一个 goroutine 并执行模块的 Run 方法。最后,游戏服务器关闭时(Ctrl + C 关闭游戏服务器)将按模块注册相反顺序在同一个 goroutine 中执行模块的 OnDestroy 方法。

与一些Web服务器不同,Leaf运行的数据绝大部分都在内存里面,虽然提供了Mongo模块,但是做实时交互的数据一般是保存在内存中的。Mongo只是为了持久化一些用户数据。这与有些无状态,靠数据库做数据交互的Web服务器有很大不同。

二、官方示例

1.消息处理

首先定义一个 JSON 格式的消息(protobuf 类似)。Processor 为消息的处理器(可由用户自定义),这里我们使用 Leaf 默认提供的 JSON 消息处理器并尝试添加一个名字为 Hello 的消息:

//LeafServer msg/msg.go

package msg

import (

"github.com/name5566/leaf/network/json"

)

// 使用默认的 JSON 消息处理器(默认还提供了 protobuf 消息处理器)

var Processor = json.NewProcessor()

func init() {

// 这里我们注册了一个 JSON 消息 Hello

Processor.Register(&Hello{})

}

// 一个结构体定义了一个 JSON 消息的格式

// 消息名为 Hello

type Hello struct {

Name string

}

客户端发送到游戏服务器的消息需要通过 gate 模块路由,简而言之,gate 模块决定了某个消息具体交给内部的哪个模块来处理。这里,我们将 Hello 消息路由到 game 模块中。打开 LeafServer gate/router.go,敲入如下代码:

package gate

import (

"server/game"

"server/msg"

)

func init() {

// 这里指定消息 Hello 路由到 game 模块

// 模块间使用 ChanRPC 通讯,消息路由也不例外

msg.Processor.SetRouter(&msg.Hello{}, game.ChanRPC)

}

一切就绪,我们现在可以在 game 模块中处理 Hello 消息了。打开 LeafServer game/internal/handler.go,敲入如下代码:

package internal

import (

"github.com/name5566/leaf/log"

"github.com/name5566/leaf/gate"

"reflect"

"server/msg"

)

func init() {

// 向当前模块(game 模块)注册 Hello 消息的消息处理函数 handleHello

handler(&msg.Hello{}, handleHello)

}

func handler(m interface{}, h interface{}) {

skeleton.RegisterChanRPC(reflect.TypeOf(m), h)

}

func handleHello(args []interface{}) {

// 收到的 Hello 消息

m := args[0].(*msg.Hello)

// 消息的发送者

a := args[1].(gate.Agent)

// 输出收到的消息的内容

log.Debug("hello %v", m.Name)

// 给发送者回应一个 Hello 消息

a.WriteMsg(&msg.Hello{

Name: "client",

})

}

到这里,一个简单的范例就完成了。为了更加清楚的了解消息的格式,我们从 0 编写一个最简单的测试客户端。

Leaf 中,当选择使用 TCP 协议时,在网络中传输的消息都会使用以下格

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值