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