主题
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 中依次读取一个完整请求,包含两步:
readRequestHeader:调用cc.ReadHeader(&h)读取请求头,解析出ServiceMethod(如"Greeter.SayHello")和Seq(请求序号)。findService:根据ServiceMethod按"."分割为服务名和方法名,从serviceMap中查找对应的service和methodType。- 通过
mtype.newArgv()/mtype.newReplyv()利用反射创建参数和返回值实例,然后调用cc.ReadBody(argvi)反序列化请求体到argv中。 - 若读取出错且
req == nil(连接级错误,如 EOF),则break退出循环;若是请求级错误,则回写错误响应后continue继续处理下一个请求。
go handleRequest(req):每个请求在独立的 goroutine 中并行处理,
wg.Add(1)保证关闭前等待所有请求完成。处理逻辑如下:- 调用
req.svc.call(req.mtype, req.argv, req.replyv),通过反射执行实际的服务方法。 - 若设置了
HandleTimeout,则在一个子 goroutine 中执行call,主 goroutine 通过select监听calledchannel 和time.After(timeout),超时则直接回写超时错误响应。 - 执行成功后调用
sendResponse回写响应。
- 调用
sendResponse() [加锁]:多个
handleRequestgoroutine 可能同时完成,但响应必须完整写入连接(Header + Body 不能交叉)。因此通过sending互斥锁(sync.Mutex)保证同一时刻只有一个 goroutine 在执行cc.Write(h, body),避免响应数据错乱。
3. 优雅关闭
- wg.Wait():当
readRequest因 EOF 或连接错误退出循环后,主 goroutine 调用wg.Wait()阻塞等待所有仍在处理中的handleRequestgoroutine 执行完毕,确保不会丢弃任何正在处理的请求。 - 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.Mutex | sending 互斥锁保证写入不交叉 |
| 等待完成 | sync.WaitGroup | 所有请求完成后才关闭连接 |
与 net/rpc 标准库的对比
| 特性 | net/rpc | miniRPC |
|---|---|---|
| 编解码 | 仅 Gob | 可插拔(Gob/Json/...) |
| 协商机制 | 无 | Option 握手 |
| 超时控制 | 无 | HandleTimeout(Day 7) |
| HTTP 支持 | 有 | 有(Day 5) |
| 服务发现 | 无 | XClient(Day 6) |