You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
7.9 KiB
7.9 KiB
自动解析和自动编码设计
用户需求
-
自动解析接收到的数据:
- 服务端在给定的协议下自动解析数据
- 自动提取header(如果有)和struct(数据部分)
- Handler中能够直接使用解析后的结构数据
-
自动编码写出的数据:
- 服务端写数据到客户端时能够自动编码
- Handler返回数据后自动进行编码和协议封装
设计思路
1. 自动解析流程
1. 数据到达 → Connection.Read()
↓
2. 协议识别 → Protocol.Identify() (根据配置或数据特征)
↓
3. 协议解码 → Protocol.Decode() → Header + Data
↓
4. 编解码器识别 → Codec.Identify() (根据配置或数据特征)
↓
5. 数据解码 → Codec.Decode() → Go Struct
↓
6. 创建Request对象 → 包含Header和Data
↓
7. 路由匹配 → 根据Header或Data匹配
↓
8. Handler执行 → 直接使用解析后的数据
2. 自动编码流程
1. Handler返回数据 → Response.Write(struct)
↓
2. 编解码器编码 → Codec.Encode() → []byte
↓
3. 协议编码 → Protocol.Encode() → Header + Data
↓
4. 写入连接 → Connection.Write()
新的设计
Request设计
type Request interface {
// Header 获取协议帧头(已解析)
Header() FrameHeader
// Data 获取数据部分(已解码为Go对象)
Data() interface{}
// Bind 绑定到指定的结构体(如果Data是map,可以绑定到struct)
Bind(v interface{}) error
// Raw 获取原始字节(如果需要)
Raw() []byte
}
Response设计
type Response interface {
// Write 写入数据(自动编码和协议封装)
Write(data interface{}) error
// WriteWithHeader 写入数据和协议帧头
WriteWithHeader(data interface{}, header FrameHeader) error
// Header 获取/设置协议帧头
Header() FrameHeader
SetHeader(header FrameHeader)
}
Handler签名
// 方式1:使用Context(推荐)
type Handler func(ctx Context) error
// 方式2:使用Request和Response(更明确)
type Handler func(req Request, resp Response) error
// 方式3:使用具体类型(最方便)
type Handler func(header FrameHeader, body MyStruct, resp Response) error
实现方案
方案1:在服务器层面自动解析(推荐)
在服务器接收到数据后,立即进行解析:
// 服务器接收到数据
func (h *eventHandler) OnTraffic(c gnet.Conn) {
// 1. 读取原始数据
data, _ := c.Peek(-1)
// 2. 识别协议(根据配置或数据特征)
protocol := h.identifyProtocol(data)
// 3. 协议解码
header, bodyBytes, err := protocol.Decode(data)
if err != nil {
// 处理错误
return
}
// 4. 识别编解码器(根据配置或数据特征)
codec := h.identifyCodec(bodyBytes)
// 5. 数据解码(需要知道目标类型,可以从路由配置中获取)
var body interface{}
if targetType := h.getTargetType(header, bodyBytes); targetType != nil {
body = reflect.New(targetType).Interface()
codec.Decode(bodyBytes, body)
} else {
// 如果没有目标类型,保持为原始字节
body = bodyBytes
}
// 6. 创建Request对象
req := NewRequest(header, body, data)
// 7. 创建Response对象
resp := NewResponse(protocol, codec, c)
// 8. 创建Context
ctx := NewContext(req, resp, c)
// 9. 路由匹配和执行
handler := h.router.Match(ctx)
handler(ctx)
}
方案2:延迟解析(按需解析)
在Handler中按需解析:
// Request对象延迟解析
type Request struct {
rawData []byte
protocol Protocol
codec Codec
header FrameHeader
body interface{}
parsed bool
}
func (r *Request) Data() interface{} {
if !r.parsed {
r.parse()
}
return r.data
}
func (r *Request) parse() {
// 协议解码
r.header, bodyBytes, _ := r.protocol.Decode(r.rawData)
// 数据解码(需要目标类型)
r.data = r.decodeBody(bodyBytes)
r.parsed = true
}
方案3:基于路由配置的自动解析(最佳)
在路由注册时指定目标类型,服务器自动解析:
// 注册路由时指定类型
type UserRequest struct {
UserID int `json:"user_id"`
}
type UserResponse struct {
Code int `json:"code"`
Data string `json:"data"`
}
// 注册路由
router.Register(
matcher,
handler,
WithRequestType(&UserRequest{}), // 指定请求类型
WithResponseType(&UserResponse{}), // 指定响应类型(可选)
)
// Handler签名
func handler(ctx Context) error {
req := ctx.Request()
// 直接使用解析后的数据
userReq := req.Data().(*UserRequest)
userID := userReq.UserID
// 返回响应(自动编码)
return ctx.Response().Write(UserResponse{
Code: 200,
Data: "success",
})
}
推荐实现:方案3(基于路由配置)
路由注册
// 定义请求和响应类型
type LoginRequest struct {
Username string `json:"username"`
Password string `json:"password"`
}
type LoginResponse struct {
Code int `json:"code"`
Token string `json:"token"`
}
// 注册路由
router.RegisterString(
"login",
loginHandler,
router.WithRequestType(&LoginRequest{}),
)
// Handler实现
func loginHandler(ctx Context) error {
req := ctx.Request()
resp := ctx.Response()
// 直接使用解析后的数据
loginReq := req.Data().(*LoginRequest)
// 处理业务逻辑
token := authenticate(loginReq.Username, loginReq.Password)
// 返回响应(自动编码)
return resp.Write(LoginResponse{
Code: 200,
Token: token,
})
}
服务器实现
// 在路由匹配时自动解析
func (r *router) Match(input MatchInput, ctx Context) (Handler, error) {
// 1. 匹配路由
route := r.findRoute(data, ctx)
if route == nil {
return nil, ErrRouteNotFound
}
// 2. 如果路由指定了请求类型,自动解析
if route.RequestType() != nil {
req := ctx.Request()
// 协议解码
header, bodyBytes, err := req.Protocol().Decode(data)
if err != nil {
return nil, err
}
req.SetHeader(header)
// 数据解码
body := reflect.New(route.RequestType()).Interface()
codec := req.Codec()
if err := codec.Decode(bodyBytes, body); err != nil {
return nil, err
}
req.SetData(body)
}
return route.Handler(), nil
}
完整的数据流程
读数据(自动解析)
1. 数据到达 → Connection.Read()
↓
2. 创建Context和Request/Response
↓
3. 路由匹配 → 找到对应的路由
↓
4. 检查路由配置 → 是否有RequestType
↓
5. 协议解码 → Protocol.Decode() → Header + DataBytes
↓
6. 数据解码 → Codec.Decode() → Go Struct
↓
7. 设置到Request → Request.SetData(struct)
↓
8. Handler执行 → 直接使用req.Data().(*MyStruct)
写数据(自动编码)
1. Handler调用 → resp.Write(struct)
↓
2. 编解码器编码 → Codec.Encode() → []byte
↓
3. 协议编码 → Protocol.Encode() → Header + Data
↓
4. 写入连接 → Connection.Write()
优势
- 自动化:无需手动解析,数据自动解析为结构体
- 类型安全:使用强类型,编译时检查
- 简洁:Handler代码简洁,直接使用结构体
- 灵活:支持有帧头和无帧头协议
- 可配置:通过路由配置指定类型
实现步骤
- 更新路由接口,支持指定请求/响应类型
- 实现自动解析逻辑
- 更新Request和Response接口
- 更新服务器代码
- 编写测试用例