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.3 KiB

架构重新设计 - 基于最佳实践

设计原则

  1. 关注点分离Separation of Concerns

    • Context状态管理和流程控制
    • Request数据读取和解码
    • Response数据编码和写入
    • Connection底层网络读写
    • Protocol协议帧头处理
    • Codec数据序列化
  2. 现代化API设计

    • 精简、直观、类型安全
    • 参考Gin、Fiber等框架的设计理念
    • 支持链式调用和流畅的API
  3. 灵活性

    • 支持有帧头协议如nnet和无帧头协议如\n分割
    • 支持自定义协议和编解码器
    • 支持协议版本管理

核心架构

1. Context上下文

职责: 状态管理和流程控制,不直接处理数据读写

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请求

职责: 数据读取、协议解码、编解码器解码

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响应

职责: 数据编码、协议编码、数据写入

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协议帧头

职责: 协议帧头的数据结构

type FrameHeader interface {
    // 获取帧头字段
    Get(key string) interface{}
    Set(key string, value interface{})
    
    // 编码为字节
    Encode() ([]byte, error)
    
    // 从字节解码
    Decode(data []byte) error
}

5. Protocol协议

职责: 协议帧头的编码和解码

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实现

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设计示例

基本使用

// 处理器
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)
}

自定义协议帧头

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})
}

无帧头协议

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)
}

指定编解码器

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")
}

配置设计

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. 最佳实践:参考了主流框架的设计理念