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

架构重新设计 - 问题解答

问题1Context不应该包含数据写入读取功能

您的思考: 完全正确

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综合考虑路由、协议选择、协议版本等功能

您的需求: 已考虑

路由集成

路由可以在不同层次进行匹配:

  1. 协议层匹配:根据协议帧头匹配
// 根据帧头的message_type字段匹配
router.RegisterFrameHeader("message_type", "==", 0x01, handler)
  1. 数据层匹配:根据解码后的数据匹配
// 根据JSON数据的path字段匹配
router.RegisterFrameData("path", "==", "/api/user", handler)
  1. 原始数据匹配:根据原始字节匹配(无帧头协议)
// 根据原始字符串匹配
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)

问题6API设计要精简、现代化

您的需求: 已实现

API特点

  1. 简洁直观
// 读取数据
req.Decode(&data)

// 写入数据
resp.Write(data)
  1. 类型安全
// 使用接口和泛型保证类型安全
req.Decode(&MyRequest{})
resp.Write(MyResponse{})
  1. 链式调用
// 支持流畅的API
resp.Header().Set("type", 0x01).Write(data)
  1. 自动处理
// 自动编码/解码
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的具体实现
  • 协议实现的更新
  • 服务器代码的更新

📋 待实现

  • 测试用例
  • 性能优化
  • 文档更新

总结

新的架构设计完全满足您的所有需求:

  1. Context不包含数据读写功能
  2. 协议帧 = 帧头Protocol+ 数据Codec
  3. 支持连同帧头一起写入
  4. 支持无帧头协议
  5. 综合考虑路由、协议选择、协议版本
  6. API设计精简、现代化

接下来可以开始实现具体的功能。