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.

345 lines
7.9 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 自动解析和自动编码设计
## 用户需求
1. **自动解析接收到的数据**
- 服务端在给定的协议下自动解析数据
- 自动提取header如果有和struct数据部分
- Handler中能够直接使用解析后的结构数据
2. **自动编码写出的数据**
- 服务端写数据到客户端时能够自动编码
- 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设计
```go
type Request interface {
// Header 获取协议帧头(已解析)
Header() FrameHeader
// Data 获取数据部分已解码为Go对象
Data() interface{}
// Bind 绑定到指定的结构体如果Data是map可以绑定到struct
Bind(v interface{}) error
// Raw 获取原始字节(如果需要)
Raw() []byte
}
```
### Response设计
```go
type Response interface {
// Write 写入数据(自动编码和协议封装)
Write(data interface{}) error
// WriteWithHeader 写入数据和协议帧头
WriteWithHeader(data interface{}, header FrameHeader) error
// Header 获取/设置协议帧头
Header() FrameHeader
SetHeader(header FrameHeader)
}
```
### Handler签名
```go
// 方式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在服务器层面自动解析推荐
在服务器接收到数据后,立即进行解析:
```go
// 服务器接收到数据
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中按需解析
```go
// 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基于路由配置的自动解析最佳
在路由注册时指定目标类型,服务器自动解析:
```go
// 注册路由时指定类型
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基于路由配置
### 路由注册
```go
// 定义请求和响应类型
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,
})
}
```
### 服务器实现
```go
// 在路由匹配时自动解析
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()
```
## 优势
1. **自动化**:无需手动解析,数据自动解析为结构体
2. **类型安全**:使用强类型,编译时检查
3. **简洁**Handler代码简洁直接使用结构体
4. **灵活**:支持有帧头和无帧头协议
5. **可配置**:通过路由配置指定类型
## 实现步骤
1. 更新路由接口,支持指定请求/响应类型
2. 实现自动解析逻辑
3. 更新Request和Response接口
4. 更新服务器代码
5. 编写测试用例