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.

330 lines
7.3 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. **关注点分离Separation of Concerns**
- Context状态管理和流程控制
- Request数据读取和解码
- Response数据编码和写入
- Connection底层网络读写
- Protocol协议帧头处理
- Codec数据序列化
2. **现代化API设计**
- 精简、直观、类型安全
- 参考Gin、Fiber等框架的设计理念
- 支持链式调用和流畅的API
3. **灵活性**
- 支持有帧头协议如nnet和无帧头协议如\n分割
- 支持自定义协议和编解码器
- 支持协议版本管理
## 核心架构
### 1. Context上下文
**职责:** 状态管理和流程控制,不直接处理数据读写
```go
type Context interface {
context.Context
// 获取Request和Response对象
Request() Request
Response() Response
Connection() Connection
// 状态管理
Set(key string, value interface{})
Get(key string) interface{}
MustGet(key string) interface{}
}
```
### 2. Request请求
**职责:** 数据读取、协议解码、编解码器解码
```go
type Request interface {
// 原始数据(未解码的字节)
Raw() []byte
// 协议解码后的数据(去除帧头后的字节)
Data() []byte
// 解码为Go对象经过protocol decode + codec decode
Decode(v interface{}) error
DecodeWithCodec(v interface{}, codecName string) error
// 协议帧头(如果有)
Header() FrameHeader
// 协议信息
Protocol() Protocol
ProtocolVersion() string
}
```
### 3. Response响应
**职责:** 数据编码、协议编码、数据写入
```go
type Response interface {
// 写入数据(自动编码)
Write(data interface{}) error
WriteWithCodec(data interface{}, codecName string) error
// 写入原始字节(绕过编码)
WriteBytes(data []byte) error
WriteString(s string) error
// 协议帧头操作(如果有)
SetHeader(header FrameHeader) error
Header() FrameHeader
// 发送响应(如果使用缓冲模式)
Send() error
}
```
### 4. FrameHeader协议帧头
**职责:** 协议帧头的数据结构
```go
type FrameHeader interface {
// 获取帧头字段
Get(key string) interface{}
Set(key string, value interface{})
// 编码为字节
Encode() ([]byte, error)
// 从字节解码
Decode(data []byte) error
}
```
### 5. Protocol协议
**职责:** 协议帧头的编码和解码
```go
type Protocol interface {
Name() string
Version() string
// 编码(添加帧头)
Encode(data []byte, header FrameHeader) ([]byte, error)
// 解码(提取帧头和数据)
Decode(data []byte) (FrameHeader, []byte, error)
// 是否有帧头无帧头协议返回false
HasHeader() bool
}
```
## 数据流程
### 读数据流程
```
1. Connection.Read() → []byte (原始字节)
2. Protocol.Decode() → FrameHeader + []byte (帧头 + 数据)
3. Codec.Decode() → interface{} (Go对象)
4. Request.Decode() → 提供给处理器使用
```
### 写数据流程
```
1. Response.Write(interface{}) → Go对象
2. Codec.Encode() → []byte (序列化后的数据)
3. Protocol.Encode() → []byte (添加帧头)
4. Connection.Write() → 发送到客户端
```
## 无帧头协议支持
对于无帧头协议(如\n分割Protocol实现
```go
type NoHeaderProtocol struct{}
func (p *NoHeaderProtocol) HasHeader() bool {
return false // 无帧头
}
func (p *NoHeaderProtocol) Encode(data []byte, header FrameHeader) ([]byte, error) {
// 无帧头协议,直接返回数据,可能添加分隔符
return append(data, '\n'), nil
}
func (p *NoHeaderProtocol) Decode(data []byte) (FrameHeader, []byte, error) {
// 无帧头协议,直接返回数据(去除分隔符)
data = bytes.TrimSuffix(data, []byte{'\n'})
return nil, data, nil // 返回nil header
}
```
## API设计示例
### 基本使用
```go
// 处理器
func handler(ctx context.Context) error {
req := ctx.Request()
resp := ctx.Response()
// 读取请求(自动解码)
var requestData MyRequest
if err := req.Decode(&requestData); err != nil {
return err
}
// 处理业务逻辑
responseData := MyResponse{
Code: 200,
Data: "success",
}
// 写入响应(自动编码)
return resp.Write(responseData)
}
```
### 自定义协议帧头
```go
func handler(ctx context.Context) error {
req := ctx.Request()
resp := ctx.Response()
// 读取协议帧头
header := req.Header()
if header != nil {
messageType := header.Get("message_type")
// 使用帧头信息
}
// 读取数据
var data MyData
req.Decode(&data)
// 设置响应帧头
respHeader := NewFrameHeader()
respHeader.Set("message_type", 0x01)
resp.SetHeader(respHeader)
// 写入响应
return resp.Write(MyResponse{Code: 200})
}
```
### 无帧头协议
```go
func handler(ctx context.Context) error {
req := ctx.Request()
resp := ctx.Response()
// 无帧头协议,直接读取原始数据
rawData := req.Data() // 或 req.Raw()
// 处理数据
result := processData(rawData)
// 写入响应(协议会自动添加\n
return resp.WriteString(result)
}
```
### 指定编解码器
```go
func handler(ctx context.Context) error {
req := ctx.Request()
resp := ctx.Response()
// 使用指定的编解码器解码
var data MyData
req.DecodeWithCodec(&data, "msgpack")
// 使用指定的编解码器编码
return resp.WriteWithCodec(MyResponse{Code: 200}, "msgpack")
}
```
## 配置设计
```go
type Config struct {
// 协议配置
Protocol struct {
Name string // 协议名称
Version string // 协议版本
HasHeader bool // 是否有帧头
}
// 编解码器配置
Codec struct {
Default string // 默认编解码器
}
}
```
## 路由集成
路由匹配可以在不同层次进行:
1. **协议层匹配**:根据协议帧头匹配(如果有)
2. **数据层匹配**:根据解码后的数据匹配
3. **原始数据匹配**:根据原始字节匹配(无帧头协议)
## 实现计划
### 第一阶段:核心接口定义
1. 定义Context、Request、Response接口
2. 定义FrameHeader接口
3. 更新Protocol接口
### 第二阶段实现Request和Response
1. 实现Request的读取和解码功能
2. 实现Response的编码和写入功能
3. 集成协议和编解码器
### 第三阶段更新Context
1. 移除Context中的数据读写方法
2. 添加Request和Response对象
3. 更新所有使用Context的地方
### 第四阶段:更新服务器
1. 更新服务器中的数据流程
2. 实现协议解码逻辑
3. 实现协议编码逻辑
### 第五阶段:测试和优化
1. 编写测试用例
2. 性能优化
3. 文档更新
## 优势
1. **清晰的职责分离**:每个组件职责明确
2. **灵活的扩展性**:支持各种协议和编解码器
3. **现代化的API**:简洁、直观、类型安全
4. **向后兼容**:可以逐步迁移现有代码
5. **最佳实践**:参考了主流框架的设计理念