Nano
一个易于使用、快速、轻量的游戏服务器网络库
参考自:
nano开源库学习 –by 这一切没有想象那么糟
探索 Golang 云原生游戏服务器开发,5 分钟上手 Nano 游戏服务器框架
github Nano
注: github的官方例子貌似跑不了 它导的包是github.com/lonnng/nano
而不是github.com/lonng/nano
, 有些方法也不存在了, 作者可能是改了一次名, 并对原有有包进行了更新, 但教程并未跟进, 使用上则没有影响
名词介绍
Component:
1
2
3
4
5
6
7
8
9
10
11
// Component 组件
type Component interface {
// 初始化函数
Init ()
// 初始化之后运行的函数
AfterInit ()
// 销毁之前将被调用
BeforeShutdown ()
// 销毁时被调用
Shutdown ()
}
nano
应用的功能就是由一些松散耦合的 Component
组成的, 它的接口用于控制它的生命周期 除此之外它应该实现某些具体的功能
Session:
1
2
3
4
5
6
7
8
9
10
// Session instance related to the client will be passed to Handler method as the first
type Session struct {
sync . RWMutex // protect data
id int64 // session global unique id
uid int64 // binding user id
lastTime int64 // last heartbeat time
entity NetworkEntity // low-level network entity
data map [ string ] interface {} // session data store
router * Router
}
客户端连接服务器后建立Session
, 它可以存储一些临时数据, 连接断开时释放所有数据
Group:
1
2
3
4
5
6
7
8
// Group represents a session group which used to manage a number of
// sessions, data send to the group will send to all session in it.
type Group struct {
mu sync . RWMutex
status int32 // channel current status
name string // channel name
sessions map [ int64 ] * session . Session // session id map to session instance
}
Session
组的封装, 对Group
发信息会广播给所有其中的Session
四种消息类型:
Request(请求)
和Response(响应)
: 客户端发起Request
到服务器端,服务器端处理后会给其返回响应Response
Notify
: 客户端发给服务端的通知, 也就是不需要服务端给予回复的Request
Push
: 服务端主动给客户端推送消息的类型
简单示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package main
import (
"fmt"
"net/http"
"github.com/lonng/nano"
"github.com/lonng/nano/component"
"github.com/lonng/nano/serialize/json"
"github.com/lonng/nano/session"
)
const (
// 测试房间 id
testRoomID = 1
// 测试房间 key
roomIDKey = "ROOM_ID"
)
type Room struct {
Name string
Id int
Group * nano . Group
}
type RoomManager struct {
component . Base
Rooms map [ int ] * Room
}
// RoomManager 初始化完成后将被调用
func ( mgr * RoomManager ) AfterInit () {
// 用户断开连接后将会被调用
// 将它从房间中移除
session . Lifetime . OnClosed ( func ( s * session . Session ) {
if ! s . HasKey ( roomIDKey ) {
return
}
room := s . Value ( roomIDKey ).( * Room )
// 移除这个会话
room . Group . Leave ( s )
})
}
// 加入房间的业务逻辑处理
func ( mgr * RoomManager ) Join ( s * session . Session , msg [] byte ) error {
// 注意:这里 demo 仅仅只是加入 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 () // 这里仅仅是用 sessionId 模拟下 uid
s . Bind ( fakeUID ) // 绑定 uid 到 session
s . Set ( roomIDKey , room ) // 设置一下当前 session 关联到的房间
// 推送房间所有成员到当前的 session
s . Push ( "onMembers" , & struct {
Members [] int64 `json:"members"`
}{ Members : room . Group . Members ()})
// 广播房间内其它成员,有新人加入
room . Group . Broadcast ( "onNewUser" , & struct {
Content string `json:"content"`
}{ Content : fmt . Sprintf ( "New user: %d" , s . ID ())})
// 将 session 加入到房间 group 统一管理
room . Group . Add ( s )
fmt . Println ( s . Value ( roomIDKey ).( * Room ))
// 回应当前用户加入成功
return s . Response ( & struct {
Code int `json:"code"`
Result string `json:"result"`
}{ Result : "success" })
}
// 同步最新的消息给房间内所有成员
func ( mgr * RoomManager ) Message ( s * session . Session , msg * struct {
Name string `json:"name"`
Content string `json:"content"`
}) error {
fmt . Printf ( "msg: %v\n" , msg )
if ! s . HasKey ( roomIDKey ) {
return fmt . Errorf ( "not join room yet" )
}
room := s . Value ( roomIDKey ).( * Room )
// 广播
return room . Group . Broadcast ( "onMessage" , msg )
}
func main () {
components := & component . Components {}
// 组件注册 前端能发现注册后的组件
components . Register (
// 组件实例
& RoomManager {
Rooms : map [ int ] * Room {},
},
// 重写组件名字
component . WithName ( "room" ),
)
http . Handle ( "/web/" , http . StripPrefix ( "/web/" , http . FileServer ( http . Dir ( "web" ))))
// 启动 nano
nano . Listen ( ":3250" , // 端口号
nano . WithIsWebsocket ( true ), // 是否使用 websocket
// nano.WithPipeline(pip), // 是否使用 pipeline
nano . WithCheckOriginFunc ( func ( _ * http . Request ) bool { return true }), // 允许跨域
nano . WithWSPath ( "/nano" ), // websocket 连接地址
nano . WithDebugMode (), // 开启 debug 模式
nano . WithSerializer ( json . NewSerializer ()), // 使用 json 序列化器
nano . WithComponents ( components ), // 加载组件
)
}
前端