【Go 框架开发】Zinx 框架开发笔记

Golang 快速入门:

文档:Golang 轻量级并发服务器框架 Zinx

视频:Zinx Golang 轻量级 TCP 服务器框架

本项目所有版本代码都在:https://gitee.com/szluyu99/my-zinx/tree/master/(不同分支对应不同版本)

本文章没有详细代码,只有疏理的思维导图,代码可以参考 Gitee 或文档。

Zinx 框架开发笔记

Zinx:轻量级的 TCP 服务器框架

  • 基础的 Server
  • 简单的连接封装和业务绑定
  • 基础 Router 模块
  • 全局配置
  • 消息封装
  • 多路由模式
  • 读写协程分离
  • 消息队列及多任务
  • 连接管理
  • 连接 属性配置
  • 基于 Zinx 的应用案例

Zinx 架构

参考:初识 Zinx 框架

Zinx - V0.1 构建最基础的 server

1. Zinx-V0.1 基础 Server

Zinx 的最基本的两个模块 zifaceznet

  • ziface 主要是存放一些 Zinx 框架的全部模块的抽象层接口类。

    Zinx 框架的最基本的服务类接口 iserver,定义在该模块中。

  • znet 模块是 Zinx 框架中网络相关功能的实现,所有网络相关模块定义在这里。

基础的 server
方法
属性
启动服务器 - Start()
停止服务器 - Stop()
运行服务器 - Serve()
初始化服务
NewServer(name string) ziface.IServer
名称 - Name string
IP版本 - IPVersion string
监听的IP - IP string
监听的端口 - Port int
  • 启动服务器:基本的服务器开发
    1. 创建一个 TCP 的 Addr
    2. 监听服务器地址,创建 Listener
    3. 阻塞等待连接,进行业务处理
  • 停止服务器:做一些资源、状态、连接信息的回收操作
  • 运行服务器:调用 Start() 方法,调用后做阻塞,以后可以做功能的拓展

V0.1 版本代码:这个版本的代码忘了上传了,可以看参考文档里作者放出来的。

Zinx - V0.2 连接封装与业务绑定

2. Zinx-V0.2 简单的连接封装与业务绑定

连接 connection
方法
属性
启动连接 - Start()
停止连接 - Stop()
获取连接对象
GetTCPConnection() *net.TCPConn
获取连接ID - GetConnID() uint32
获取客户端的连接地址 - RemoteAddr() net.Addr
发送数据 - Send(data []byte) error
连接绑定的统一处理业务的函数类型
type HandleFunc func(*net.TCPConn, []byte, int) error
socket TCP 套接字 - Conn *net.TCPConn
连接ID - ConnID uint32
连接状态(是否已经关闭) - isClosed bool
与连接绑定的业务方法
handleAPI ziface.HandleFunc
等待连接被动退出的管道 - ExitChan chan bool

V0.2 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.2/(选择 V0.2 分支)

Zinx - V0.3 框架路由模块

参考文档:Zinx 框架基础路由模块

3.1 IRequest 消息请求抽象类

Request 请求封装
将连接和数据绑定在一起
属性
和客户端建立好的连接 - conn ziface.IConnection
客户端请求的数据 - data []byte
方法
得到当前连接 - GetConnection() IConnection
得到当前数据 - GetData() []byte

3.2 IRouter 路由配置抽象类

Router 模块
抽象的 IRouter
处理 conn 业务之前的方法 - PreHandle(request IRequest)
处理 conn 业务的主方法 - Handle(request IRequest)
处理 conn 业务之后的方法 - PostHandle(request IRequest)
具体的 BaseRouter
处理 conn 业务之前的方法
func (b *BaseRouter) PreHandle(request ziface.IRequest) {}
处理 conn 业务的主方法
func (b *BaseRouter) Handle(request ziface.IRequest) {}
处理 conn 业务之后的方法
func (b *BaseRouter) PostHandle(request ziface.IRequest) {}

3.3 Zinx-V0.3 集成简单路由功能

Zinx 集成 router 模块
IServer 增添路由添加功能 - AddRouter(router IRouter)
Server 类增添 Router 成员 - 取代之前的 HandleAPI
Connection 类绑定一个 Router 成员
在 Connection 调用已经注册的 Router 处理业务

3.5 使用 Zinx-V0.3 完成应用程序

使用 ZinxV0.3开发应用程序
开发步骤
1 创建一个 server 句柄, 使用 Zinx 的 api
2 给当前 zinx 框架添加一个自定义的 router
3 启动 server
自定义的 router 需要继承 BaseRouter,实现 PreHandle、Handle、PostHandle 方法

V0.3 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.3/(选择 V0.3 分支)

Zinx - V0.4 全局配置

四、Zinx 的全局配置

这里使用 JSON 作为配置文件,zinx.json 中的配置信息:

{
    
    
  "Name":"demo server",
  "Host":"127.0.0.1",
  "TcpPort":7777,
  "MaxConn":3
}
Zinx V0.4 全局配置
配置文件 /conf/zinx.json(用户编写)
创建一个 Zinx 全局配置模块 utils/globalobj.go
在 init() 中 读取用户配置的 zinx.json 到 globalObj 对象中
将 Zinx 框架中全部的硬编码,使用 globalObj 中的参数进行替换
使用 Zinx V0.4 开发应用程序

V0.4 版本的代码:https://gitee.com/szluyu99/my-zinx/tree/V0.4/(选择 V0.4 分支)

Zinx - V0.5 消息封装

五、Zinx 的消息封装

定义一个消息的结构 Message:5.1 创建消息封装类型

消息结构 Message
属性
方法
消息ID - Id uint32
消息长度 - DataLen uint32
消息内容 - []byte
获取消息ID - GetMsgId() uint32
获取消息长度 - GetMsgLen() uint32
获取消息内容 - GetData() []byte
设置消息ID - SetMsgId(uint32)
设置消息长度 - SetData([]byte)
设置消息内容 - SetDataLen(uint32)

使用 TLV (Type-Len-Value) 封包格式解决 TCP 粘包问题5.2 消息的封包和拆包

由于 Zinx 也是 TCP 流的形式传播数据,难免会出现消息 1 和消息 2 一同发送,那么 Zinx 就需要有能力区分两个消息的边界,所以 Zinx 此时应该提供一个统一的拆包和封包的方法。

  • 封包:在发包之前打包成如上图这种格式的有 head 和 body 的两部分的包
  • 拆包:在收到数据的时候分两次进行读取,先读取固定长度的 head 部分,得到后续 Data 的长度,再根据 DataLen 读取之后的 body。
封包拆包模块
针对 Message 进行 TVL 格式的封装
func (dp *DataPack) Pack(msg ziface.IMessage) ([]byte, error)
针对 Message 进行 TLV 格式的拆包
func (dp *DataPack) Unpack(binaryData []byte) (ziface.IMessage, error)
写 Message 的长度
写 Message 的 ID
写 Message 的 内容
先读固定长度的 Head:
消息内容的长度、消息的类型
再根据消息内容的长度,
进行一次读写,从 conn 中读取消息内容

测试拆包封包功能:https://gitee.com/szluyu99/my-zinx/blob/V0.5/znet/datapack_test.go

将封包、拆包功能集成到 Zin 中:5.3 Zinx - V0.5 代码实现

集成到 Zinx
将 Message 添加到 Request 属性中
修改连接读取数据的机制:
将之前单纯的读取 byte 改成拆包按照 TLV 形式读取
给连接提供一个发包机制:将发送的消息进行打包再,发送

使用 Zinx V0.5 开发应用:5.4 使用 Zinx - V0.5 完成应用程序

V0.5 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.5/(选择 V0.5 分支)

Zinx - V0.6 多路由模式

参考:六、Zinx 的多路由模式

6.1 创建消息管理模块

消息管理模块 MsgHandler
(支持多路由业务API调度管理)
属性
MsgID 和对应的 router
Apis map[uint32]ziface.IRouter
方法
根据 MsgID 来索引调度路由方法
func (mh *MsgHandle) DoMsgHandler(request ziface.IRequest)
添加路由方法到 Apis 集合中
func (mh *MsgHandle) AddRouter(msgID uint32, router ziface.IRouter)

6.2 Zinx - V0.6 代码实现

消息管理模块集成到 Zinx
1 将 server 模块的 Router 属性成 MsgHandler
2 将 server 模块的 AddRouter 修改为调用 MsgHandler 的 AddRouter
AddRouter(msgID uint32, router IRouter)
3 将 connection 模块的 Router 属性替换成 MsgHandler
修改初始化方法
4 将 connection 模块调度 Router 的业务替换成 MsgHandler 调度
修改 StartRouter 方法

V0.6 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.6/(选择 V0.6 分支)

Zinx - V0.7 读写协程分离

参考:七、Zinx的读写分离模型

读写协程分离
1 添加一个 Reader 和 Writer 之间通信的 channel
2 添加一个 Writer Goroutine
3 Reader 由之前直接发送给客户端改成
发送给通信 channel
4 启动 Reader 和 Writer 一同工作

7.2 使用 Zinx-V0.7 完成应用程序

V0.7 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.7/(选择 V0.7 分支)

Zinx - V0.8 消息队列及多任务机制

参考:八、Zinx 的消息队列及多任务机制

设计架构:

消息队列及多任务:

  1. 8.1 创建消息队列
增加属性
增加属性
1 创建一个消息队列
MsgHandler 消息管理模块中
消息队列 - TaskQueue []chan ziface.IRequest
worker 工作池的数量 - WorkerPoolSize uint32
在全局配置中获取,也可以让用户在配置文件设置
  1. 8.2 创建及启动 Worker 工作池
2 创建多任务 worker 的工作池并且启动
创建一个 worker 的工作池
func (mh *MsgHandle) StartWorkerPool()
1 根据 workerPoolSize 的数量去创建 worker
func (mh *MsgHandle) StartOneWorker(workerID int, taskQueue chan ziface.IRequest)
2 每个 worker 应该用一个 goroutinie 去承载
阻塞的等待与当前 worker 对应的 channel 的消息
一旦有消息,worker 处理当前消息对应的业务,调用 DoMessage()
  1. 将之前的发送消息,全部改成:把消息发送给 消息队列 和 worker 工作池来处理 8.3 发送消息给消息队列
3 定义一个方法,将消息发送给消息队列工作池
func (mh *MsgHandle) SendMsgToTaskQueue(request ziface.IRequest)
1 保证每个 worker 收到 request 任务均衡(平均分配),
让哪个 worker 去处理,只需要将 request 发给对应的 taskQueue 即可
2 将消息直接发送给对应 channel

将消息队列集成到 Zinx 框架:8.4 Zinx-V0.8 代码实现

Zinx V0.8
1 开启并调用消息队列及 worker 工作池
(在 server listen 之前开启)
2 将从客户端处理的消息,发送给当前的 worker 工作池来处理
(在已经处理完拆包,得到 request 请求,交给工作池处理)

8.5 使用 Zinx-V0.8 完成应用程序

配置文件新增 WorkerPoolSize 为 10

{
    
    
 "Name": "Zinx V0.8 demoServerApp",
 "Host": "127.0.0.1",
 "TcpPort": 8999,
 "MaxConn": 3,
 "WorkerPoolSize": 10
}

V0.8 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.8/(选择 V0.8 分支)

Zinx - V0.9 连接管理

九、Zinx 的连接管理

Zinx V0.9 连接管理
创建一个连接管理模块(定义、属性、方法)--- ConnManager
将连接管理模块集成到 Zinx 框架中
给 Zinx 框架提供 创建连接之后 / 销毁连接之前 要处理的业务
提供给用户能够注册 Hook 函数
使用 Zinx V0.9 进行开发应用程序

9.1 创建连接管理模块:ConnManager

ConnManager
属性
已经创建的连接集合
connections map[uint32]ziface.IConnection
针对 map 的互斥锁
connLock sync.RWMutex
方法
添加连接
Add(conn IConnection)
删除连接
Remove(conn IConnection)
根据连接 ID 查找对应的连接
Get(connID uint32) (IConnection, error)
总连接个数
Len() int
清理全部的连接
ClearConn()

9.2 连接管理模块集成到 Zinx 中

将连接管理集成到 Zinx 框架
将 ConnManager 加入 Server 模块
给 server 添加一个 ConnMgr 属性
修改 NewServer 方法,加入 ConnMgr 初始化
判断当前连接数量是否超出最大值 MaxConn
当 server 停止的时候,清除所有连接
Stop() 方法中加入 ConnMgr.ClearConn()
每次与客户端建立连接后,添加连接到 ConnManager 中
在 NewConnection 的时候将新的 conn 加入到 ConnMgr 中
需要给 Connection 加入隶属的 Server 属性
给 Server 提供一个 GetConnMgr 方法
每次与客户端连接断开后,将连接从 ConnManager 删除
在 Conn.Stop() 方法中,将当前的连接从 ConnMgr 删除即可
当 Server 停止的时候应该清除所有的连接
Stop() 方法中加入 ConnMgr.ClearConn()

9.4 注册链接启动/停止自定义 Hook 方法功能

给 ZInx 框架提供:创造连接之后 / 销毁连接之前 所要处理的一些业务,提供给用户能够注册的 Hook 函数

提供 Hook 函数
添加的属性
该 Server 创建连接之后自动调用 Hook 函数
OnConnStart func(conn ziface.IConnection)
该 Server 销毁连接之前自动调用 Hook 函数
OnConnStop func(conn ziface.IConnection)
添加的方法
注册 OnConnStart 钩子函数的方法
SetOnConnStart(func(conn IConnection))
调用 OnConnStart 钩子函数的方法
CallOnConnStart(conn IConnection)
注册 OnConnStop 钩子函数的方法
SetOnConnStop(func(conn IConnection))
调用 OnConnStop 钩子函数的方法
CallOnConnStop(conn IConnection)
Conn 创建之后调用 OnConnStart
在 conn.Start() 中调用
Conn 销毁之前调用 OnConnStop
在 conn.Stop() 中调用

9.5 使用 Zinx-V0.9 完成应用程序

添加
使用 Zinx V0.9 进行开发
注册钩子函数的步骤

V0.9 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V0.9/(选择 V0.9 分支)

Zinx - V1.0 连接属性配置

十、Zinx 的连接属性设置

10.1 给连接添加连接配置接口

10.2 连接属性方法实现

新增
新增
给 Connection 模块添加可以配置属性的功能
属性
连接属性集合 - property map[string]interface{}
保护连接属性的锁 - propertyLock sync.RWMutex
方法
设置连接属性 - SetProperty(key string, value interface{})
获取连接属性 - GetProperty(key string) (interface{}, error)
修改连接属性 - RemoveProperty(key string)

10.3 连接属性 Zinx-V0.10 单元测试

V1.0 版本的代码https://gitee.com/szluyu99/my-zinx/tree/V1.0/(选择 V1.0 分支)

猜你喜欢

转载自blog.csdn.net/weixin_43734095/article/details/123237145