golang高性能游戏服务器网络框架插件库gnet的使用
golang高性能游戏服务器网络框架插件库gnet的使用
特性
- 多线程、非阻塞
- 默认支持protobuf
- 使用无锁RingBuffer优化接收和分发,某些情况下性能可提升5倍
- 易于实现自定义编码和解码
- 支持RPC
- 支持TCP、WebSocket(ws和wss)
使用示例
运行服务器
// 创建protobuf编解码器
codec := gnet.NewProtoCodec(nil)
// 创建默认连接处理器
handler := gnet.NewDefaultConnectionHandler(codec)
// 注册消息处理函数
handler.Register(gnet.PacketCommand(pb.CmdTest_Cmd_TestMessage), onTestMessage, new(pb.TestMessage))
// 配置监听器
listenerConfig := &gnet.ListenerConfig{
AcceptConfig: gnet.DefaultConnectionConfig,
}
listenerConfig.AcceptConfig.Codec = codec
listenerConfig.AcceptConfig.Handler = handler
// 创建监听器
gnet.GetNetMgr().NewListener(ctx, "localhost:10001", listenerConfig)
运行客户端
// 创建protobuf编解码器
codec := gnet.NewProtoCodec(nil)
// 创建默认连接处理器
handler := gnet.NewDefaultConnectionHandler(codec)
// 注册消息处理函数
handler.Register(gnet.PacketCommand(pb.CmdTest_Cmd_TestMessage), onTestMessage, new(pb.TestMessage))
// 配置连接
connectionConfig := gnet.DefaultConnectionConfig
connectionConfig.Codec = clientCodec
connectionConfig.Handler = clientHandler
// 创建连接器并发送消息
connector := gnet.GetNetMgr().NewConnector(ctx, "localhost:10001", &connectionConfig, nil)
connector.SendPacket(gnet.NewProtoPacket(gnet.PacketCommand(pb.CmdTest_Cmd_TestMessage),
&pb.TestMessage{
Name: "hello",
}))
编码和解码
gnet将TCP流的分包解码分为三层:
- 分包流层:格式为|Length|Data|,接收完整包内容后交给下一层处理
- 数据解码层:对第一层的数据进行解码,如解密、解压等
- protobuf反序列化层:生成proto.Message
使用RingBuffer提升性能
RPC
RPC向目标发送请求并阻塞等待回复,类似于grpc-go,但gnet使用命令ID而不是方法名
request := gnet.NewProtoPacket(cmd, &pb.HelloRequest{
Name: "hello",
})
reply := new(pb.HelloReply)
err := connection.Rpc(request, reply)
if err != nil {
return
}
logger.Info("reply:%v", reply)
Goroutine
示例
- example/helloworld: 使用protobuf消息的简单示例
- example/data_packet: 使用DataPacket的简单示例
- example/custom_packet: 如何扩展自定义包结构
- example/tcp_connection_simple: 不使用RingBuffer的protobuf消息示例
- example/packet_size: 发送大于RingBuffer容量的大包
- example/websocket: WebSocket简单示例
- example/rpc: 如何使用RPC
- example/simulate_game: 游戏应用场景的性能测试
客户端连接库
C#: gnet_csharp
项目
gnet也被用于我们的商业在线游戏项目中
更多关于golang高性能游戏服务器网络框架插件库gnet的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang高性能游戏服务器网络框架插件库gnet的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
gnet - 高性能Go语言游戏服务器网络框架
gnet是一个轻量级、高性能、基于事件驱动的Go语言网络框架,特别适合游戏服务器、即时通讯等需要高并发低延迟的场景。下面我将详细介绍gnet的使用方法。
gnet核心特性
- 基于事件驱动,非阻塞IO
- 支持多线程/多核处理
- 内存池优化,减少GC压力
- 简单易用的API接口
- 高性能,可处理数十万并发连接
基本使用示例
安装gnet
go get -u github.com/panjf2000/gnet
Echo服务器示例
package main
import (
"log"
"github.com/panjf2000/gnet"
)
type echoServer struct {
*gnet.EventServer
}
func (es *echoServer) React(c gnet.Conn) (out []byte, action gnet.Action) {
// 读取客户端数据
data := c.Read()
// 直接回写相同数据
out = data
// 打印日志
log.Printf("Echo: %s", string(data))
return
}
func main() {
echo := new(echoServer)
// 启动服务器
err := gnet.Serve(echo, "tcp://:9000", gnet.WithMulticore(true))
if err != nil {
log.Fatal(err)
}
}
核心API详解
事件处理接口
gnet通过实现以下接口来处理各种网络事件:
type EventServer interface {
// 服务器启动时调用
OnInitComplete(server Server) (action Action)
// 新连接建立时调用
OnOpened(c Conn) (out []byte, action Action)
// 连接关闭时调用
OnClosed(c Conn, err error) (action Action)
// 接收到数据时调用
React(c Conn) (out []byte, action Action)
// 定时器触发时调用
Tick() (delay time.Duration, action Action)
}
完整游戏服务器示例
package main
import (
"log"
"time"
"github.com/panjf2000/gnet"
)
type gameServer struct {
*gnet.EventServer
players map[string]gnet.Conn
}
func (gs *gameServer) OnInitComplete(srv gnet.Server) (action gnet.Action) {
log.Printf("Game server started on %s (multi-core: %t, loops: %d)",
srv.Addr.String(), srv.Multicore, srv.NumEventLoop)
// 初始化玩家列表
gs.players = make(map[string]gnet.Conn)
return
}
func (gs *gameServer) OnOpened(c gnet.Conn) (out []byte, action gnet.Action) {
// 新玩家连接
playerID := c.RemoteAddr().String()
gs.players[playerID] = c
log.Printf("Player connected: %s", playerID)
// 发送欢迎消息
out = []byte("Welcome to the game server!\n")
return
}
func (gs *gameServer) OnClosed(c gnet.Conn, err error) (action gnet.Action) {
// 玩家断开连接
playerID := c.RemoteAddr().String()
delete(gs.players, playerID)
log.Printf("Player disconnected: %s", playerID)
return
}
func (gs *gameServer) React(c gnet.Conn) (out []byte, action gnet.Action) {
data := c.Read()
playerID := c.RemoteAddr().String()
// 处理游戏指令
cmd := string(data)
switch cmd {
case "ping\n":
out = []byte("pong\n")
case "time\n":
out = []byte(time.Now().String() + "\n")
case "players\n":
out = []byte(fmt.Sprintf("Online players: %d\n", len(gs.players)))
default:
out = []byte("Unknown command\n")
}
log.Printf("Player %s sent: %s", playerID, cmd)
return
}
func (gs *gameServer) Tick() (delay time.Duration, action gnet.Action) {
// 每秒广播一次在线玩家数
msg := fmt.Sprintf("Current online players: %d\n", len(gs.players))
for _, conn := range gs.players {
_ = conn.AsyncWrite([]byte(msg))
}
return time.Second, gnet.None
}
func main() {
gs := &gameServer{}
// 启动服务器,配置多核处理
err := gnet.Serve(gs, "tcp://:9000",
gnet.WithMulticore(true),
gnet.WithTCPKeepAlive(time.Minute*5),
gnet.WithCodec(&gnet.LineBasedFrameCodec{}),
)
if err != nil {
log.Fatal(err)
}
}
高级配置选项
gnet提供了多种配置选项来优化性能:
err := gnet.Serve(server, addr,
gnet.WithMulticore(true), // 多核并行处理
gnet.WithReusePort(true), // 端口复用
gnet.WithTCPKeepAlive(time.Minute), // TCP保活
gnet.WithCodec(codec), // 自定义编解码器
gnet.WithSocketRecvBuffer(1024), // 接收缓冲区大小
gnet.WithSocketSendBuffer(1024), // 发送缓冲区大小
gnet.WithTicker(true), // 启用定时器
gnet.WithLogLevel(logging.Info), // 日志级别
)
性能优化建议
- 启用多核处理:
WithMulticore(true)
可以充分利用多核CPU - 使用连接池:复用连接减少GC压力
- 合理设置缓冲区:根据消息大小调整收发缓冲区
- 批量处理消息:合并小包减少系统调用
- 避免内存拷贝:尽量复用内存,减少分配
总结
gnet是一个非常适合游戏服务器开发的Go语言网络框架,它提供了简洁的API和高性能的事件驱动模型。通过合理配置和使用,可以轻松构建出支持数十万并发连接的游戏服务器。相比标准库net和大多数Go网络框架,gnet在性能上有明显优势,特别适合对延迟敏感的游戏服务器场景。
实际项目中,你可以基于gnet构建更复杂的游戏协议处理、房间管理、玩家状态同步等功能,gnet提供的基础设施已经为高性能网络通信打下了良好基础。