|
|
# 架构重新设计 - 问题解答
|
|
|
|
|
|
## 问题1:Context不应该包含数据写入读取功能
|
|
|
|
|
|
**您的思考:** ✅ **完全正确**
|
|
|
|
|
|
Context应该专注于状态管理和流程控制,数据读写应该交给Request、Response或Connection完成。
|
|
|
|
|
|
**新的设计:**
|
|
|
|
|
|
```go
|
|
|
// Context只负责状态管理
|
|
|
type Context interface {
|
|
|
context.Context
|
|
|
Request() Request // 获取Request对象
|
|
|
Response() Response // 获取Response对象
|
|
|
Connection() Connection // 获取Connection对象
|
|
|
Set(key string, value interface{})
|
|
|
Get(key string) interface{}
|
|
|
}
|
|
|
|
|
|
// Request负责数据读取
|
|
|
type Request interface {
|
|
|
Raw() []byte // 原始数据
|
|
|
Data() []byte // 协议解码后的数据
|
|
|
Decode(v interface{}) error // 解码为Go对象
|
|
|
Header() FrameHeader // 协议帧头
|
|
|
}
|
|
|
|
|
|
// Response负责数据写入
|
|
|
type Response interface {
|
|
|
Write(data interface{}) error // 写入数据(自动编码)
|
|
|
WriteBytes(data []byte) error // 写入原始字节
|
|
|
SetHeader(header FrameHeader) // 设置协议帧头
|
|
|
Send() error // 发送响应
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 问题2:协议帧 = 帧头(协议处理)+ 帧数据(Codec处理)
|
|
|
|
|
|
**您的理解:** ✅ **完全正确**
|
|
|
|
|
|
协议帧的结构:
|
|
|
```
|
|
|
[帧头] + [数据]
|
|
|
↓ ↓
|
|
|
Protocol Codec
|
|
|
```
|
|
|
|
|
|
**设计实现:**
|
|
|
|
|
|
```go
|
|
|
// Protocol处理帧头
|
|
|
type Protocol interface {
|
|
|
HasHeader() bool // 是否有帧头
|
|
|
Encode(data []byte, header FrameHeader) ([]byte, error) // 添加帧头
|
|
|
Decode(data []byte) (FrameHeader, []byte, error) // 提取帧头和数据
|
|
|
}
|
|
|
|
|
|
// Codec处理数据序列化
|
|
|
type Codec interface {
|
|
|
Encode(v interface{}) ([]byte, error) // 序列化
|
|
|
Decode(data []byte, v interface{}) error // 反序列化
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**数据流程:**
|
|
|
|
|
|
读数据:
|
|
|
```
|
|
|
原始字节 → Protocol.Decode() → 帧头 + 数据 → Codec.Decode() → Go对象
|
|
|
```
|
|
|
|
|
|
写数据:
|
|
|
```
|
|
|
Go对象 → Codec.Encode() → 数据 → Protocol.Encode() → 帧头 + 数据 → 原始字节
|
|
|
```
|
|
|
|
|
|
## 问题3:写数据时希望可以连同帧头一起写入
|
|
|
|
|
|
**您的需求:** ✅ **已支持**
|
|
|
|
|
|
**实现方式:**
|
|
|
|
|
|
### 方式1:自动处理(推荐)
|
|
|
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 设置协议帧头
|
|
|
header := resp.Header()
|
|
|
if header != nil {
|
|
|
header.Set("message_type", 0x01)
|
|
|
header.Set("sequence", 123)
|
|
|
}
|
|
|
|
|
|
// 写入数据(自动添加帧头)
|
|
|
return resp.Write(MyResponse{Code: 200})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 方式2:手动构建
|
|
|
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 手动构建帧头
|
|
|
header := NewFrameHeader()
|
|
|
header.Set("message_type", 0x01)
|
|
|
resp.SetHeader(header)
|
|
|
|
|
|
// 写入数据
|
|
|
return resp.Write(MyResponse{Code: 200})
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 方式3:直接写入原始字节
|
|
|
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 手动构建完整的协议帧
|
|
|
frameHeader := []byte{0x01, 0x02, 0x03} // 自定义帧头
|
|
|
data := []byte("your data")
|
|
|
frame := append(frameHeader, data...)
|
|
|
|
|
|
// 直接写入(绕过自动编码)
|
|
|
return resp.WriteBytes(frame)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 问题4:无帧头协议支持(如\n分割)
|
|
|
|
|
|
**您的需求:** ✅ **已支持**
|
|
|
|
|
|
**实现方式:**
|
|
|
|
|
|
### 无帧头协议实现
|
|
|
|
|
|
```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
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 使用示例
|
|
|
|
|
|
```go
|
|
|
func handler(ctx context.Context) error {
|
|
|
req := ctx.Request()
|
|
|
resp := ctx.Response()
|
|
|
|
|
|
// 无帧头协议,直接读取数据
|
|
|
rawData := req.Data() // 已经是去除分隔符的数据
|
|
|
|
|
|
// 处理数据
|
|
|
result := processData(rawData)
|
|
|
|
|
|
// 写入响应(协议会自动添加\n)
|
|
|
return resp.WriteString(result)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
## 问题5:综合考虑路由、协议选择、协议版本等功能
|
|
|
|
|
|
**您的需求:** ✅ **已考虑**
|
|
|
|
|
|
### 路由集成
|
|
|
|
|
|
路由可以在不同层次进行匹配:
|
|
|
|
|
|
1. **协议层匹配**:根据协议帧头匹配
|
|
|
```go
|
|
|
// 根据帧头的message_type字段匹配
|
|
|
router.RegisterFrameHeader("message_type", "==", 0x01, handler)
|
|
|
```
|
|
|
|
|
|
2. **数据层匹配**:根据解码后的数据匹配
|
|
|
```go
|
|
|
// 根据JSON数据的path字段匹配
|
|
|
router.RegisterFrameData("path", "==", "/api/user", handler)
|
|
|
```
|
|
|
|
|
|
3. **原始数据匹配**:根据原始字节匹配(无帧头协议)
|
|
|
```go
|
|
|
// 根据原始字符串匹配
|
|
|
router.RegisterString("get_user\n", handler)
|
|
|
```
|
|
|
|
|
|
### 协议选择
|
|
|
|
|
|
```go
|
|
|
// 配置协议
|
|
|
cfg := &Config{
|
|
|
Protocol: ProtocolConfig{
|
|
|
Name: "nnet", // 协议名称
|
|
|
Version: "1.0", // 协议版本
|
|
|
HasHeader: true, // 是否有帧头
|
|
|
},
|
|
|
}
|
|
|
|
|
|
// 服务器自动选择协议
|
|
|
server := NewServer(cfg)
|
|
|
```
|
|
|
|
|
|
### 协议版本管理
|
|
|
|
|
|
```go
|
|
|
// 协议管理器支持多版本
|
|
|
protocolManager.Register(nnetProtocolV1) // 版本1.0
|
|
|
protocolManager.Register(nnetProtocolV2) // 版本2.0
|
|
|
|
|
|
// 根据数据自动识别版本
|
|
|
protocol, version := protocolManager.Identify(data)
|
|
|
```
|
|
|
|
|
|
## 问题6:API设计要精简、现代化
|
|
|
|
|
|
**您的需求:** ✅ **已实现**
|
|
|
|
|
|
### API特点
|
|
|
|
|
|
1. **简洁直观**
|
|
|
```go
|
|
|
// 读取数据
|
|
|
req.Decode(&data)
|
|
|
|
|
|
// 写入数据
|
|
|
resp.Write(data)
|
|
|
```
|
|
|
|
|
|
2. **类型安全**
|
|
|
```go
|
|
|
// 使用接口和泛型保证类型安全
|
|
|
req.Decode(&MyRequest{})
|
|
|
resp.Write(MyResponse{})
|
|
|
```
|
|
|
|
|
|
3. **链式调用**
|
|
|
```go
|
|
|
// 支持流畅的API
|
|
|
resp.Header().Set("type", 0x01).Write(data)
|
|
|
```
|
|
|
|
|
|
4. **自动处理**
|
|
|
```go
|
|
|
// 自动编码/解码
|
|
|
req.Decode(&data) // 自动进行protocol decode + codec decode
|
|
|
resp.Write(data) // 自动进行codec encode + protocol encode
|
|
|
```
|
|
|
|
|
|
## 完整的数据流程
|
|
|
|
|
|
### 读数据流程
|
|
|
|
|
|
```
|
|
|
1. Connection.Read() → []byte (原始字节)
|
|
|
↓
|
|
|
2. Protocol.Decode() → FrameHeader + []byte (帧头 + 数据)
|
|
|
↓
|
|
|
3. Request.Data() → []byte (数据部分)
|
|
|
↓
|
|
|
4. Request.Decode() → interface{} (Go对象,经过codec decode)
|
|
|
↓
|
|
|
5. 路由匹配(可以根据帧头或数据匹配)
|
|
|
↓
|
|
|
6. 处理器使用解码后的数据
|
|
|
```
|
|
|
|
|
|
### 写数据流程
|
|
|
|
|
|
```
|
|
|
1. Response.Write(interface{}) → Go对象
|
|
|
↓
|
|
|
2. Codec.Encode() → []byte (序列化后的数据)
|
|
|
↓
|
|
|
3. Protocol.Encode() → []byte (添加帧头)
|
|
|
↓
|
|
|
4. Connection.Write() → 发送到客户端
|
|
|
```
|
|
|
|
|
|
## 实现状态
|
|
|
|
|
|
### ✅ 已完成
|
|
|
- [x] 接口定义(Context、Request、Response、Protocol、FrameHeader)
|
|
|
- [x] 设计文档
|
|
|
- [x] API设计文档
|
|
|
|
|
|
### 🚧 进行中
|
|
|
- [ ] Request和Response的具体实现
|
|
|
- [ ] 协议实现的更新
|
|
|
- [ ] 服务器代码的更新
|
|
|
|
|
|
### 📋 待实现
|
|
|
- [ ] 测试用例
|
|
|
- [ ] 性能优化
|
|
|
- [ ] 文档更新
|
|
|
|
|
|
## 总结
|
|
|
|
|
|
新的架构设计完全满足您的所有需求:
|
|
|
|
|
|
1. ✅ Context不包含数据读写功能
|
|
|
2. ✅ 协议帧 = 帧头(Protocol)+ 数据(Codec)
|
|
|
3. ✅ 支持连同帧头一起写入
|
|
|
4. ✅ 支持无帧头协议
|
|
|
5. ✅ 综合考虑路由、协议选择、协议版本
|
|
|
6. ✅ API设计精简、现代化
|
|
|
|
|
|
接下来可以开始实现具体的功能。
|
|
|
|