Skip to content

Day 2 技术设计文档 — 服务端架构

请求处理流程

流程详解

整个请求处理流程可分为三个阶段:连接建立与协议协商请求循环处理优雅关闭

1. 连接建立与协议协商

  • TCP Connect:客户端通过 net.Dial 向服务端发起 TCP 连接。
  • Accept 循环Server.Accept 在一个 for 循环中持续监听,每收到一个新连接就启动一个独立的 goroutine 调用 ServeConn(conn) 处理,实现连接级别的并发。
  • Option 协商ServeConn 首先通过 json.NewDecoder(conn).Decode(&opt) 从连接中读取 Option 结构体,完成协议握手。这一步会校验两个关键字段:
    • MagicNumber:用于标识这是一个 miniRPC 请求,防止非法连接。
    • CodecType:客户端指定的编解码方式(如 Gob、JSON),服务端据此从 NewCodecFuncMap 中查找对应的 Codec 构造函数来创建编解码器。
  • 创建 Codec:协商成功后,调用 f(conn) 创建具体的 Codec 实例,后续的 Header/Body 读写都通过该实例完成。

2. 请求循环处理

serveCodec 进入一个 for 无限循环,串行读取、并行处理请求:

  • readRequest():在主 goroutine 中串行执行,每次从 Codec 中依次读取一个完整请求,包含两步:

    1. readRequestHeader:调用 cc.ReadHeader(&h) 读取请求头,解析出 ServiceMethod(如 "Greeter.SayHello")和 Seq(请求序号)。
    2. findService:根据 ServiceMethod"." 分割为服务名和方法名,从 serviceMap 中查找对应的 servicemethodType
    3. 通过 mtype.newArgv() / mtype.newReplyv() 利用反射创建参数和返回值实例,然后调用 cc.ReadBody(argvi) 反序列化请求体到 argv 中。
    4. 若读取出错且 req == nil(连接级错误,如 EOF),则 break 退出循环;若是请求级错误,则回写错误响应后 continue 继续处理下一个请求。
  • go handleRequest(req):每个请求在独立的 goroutine 中并行处理,wg.Add(1) 保证关闭前等待所有请求完成。处理逻辑如下:

    1. 调用 req.svc.call(req.mtype, req.argv, req.replyv),通过反射执行实际的服务方法。
    2. 若设置了 HandleTimeout,则在一个子 goroutine 中执行 call,主 goroutine 通过 select 监听 called channel 和 time.After(timeout),超时则直接回写超时错误响应。
    3. 执行成功后调用 sendResponse 回写响应。
  • sendResponse() [加锁]:多个 handleRequest goroutine 可能同时完成,但响应必须完整写入连接(Header + Body 不能交叉)。因此通过 sending 互斥锁(sync.Mutex)保证同一时刻只有一个 goroutine 在执行 cc.Write(h, body),避免响应数据错乱。

3. 优雅关闭

  • wg.Wait():当 readRequest 因 EOF 或连接错误退出循环后,主 goroutine 调用 wg.Wait() 阻塞等待所有仍在处理中的 handleRequest goroutine 执行完毕,确保不会丢弃任何正在处理的请求。
  • cc.Close():所有请求处理完成后,关闭 Codec(底层连接),释放资源。

Server 核心结构

go
type Server struct {
    serviceMap sync.Map  // 服务名 -> *service
}

type request struct {
    h            *codec.Header
    argv, replyv reflect.Value
    mtype        *methodType
    svc          *service
}

并发安全分析

操作保护方式说明
读取请求串行(单 goroutine)serveCodec 中顺序调用 readRequest
处理请求并行(多 goroutine)每个请求独立的 goroutine
发送响应sync.Mutexsending 互斥锁保证写入不交叉
等待完成sync.WaitGroup所有请求完成后才关闭连接

与 net/rpc 标准库的对比

特性net/rpcminiRPC
编解码仅 Gob可插拔(Gob/Json/...)
协商机制Option 握手
超时控制HandleTimeout(Day 7)
HTTP 支持有(Day 5)
服务发现XClient(Day 6)