综述
http2Client实现自ClientTransport接口
http2Server实现自ServerTransport接口
https://github.com/messixukej…
在liangzhiyang/annotate-grpc-go基础上补充了部分注释
客户端:http客户端连接创建
原理(引自http2-spec/HTTP2中英对照版(06-29).md at master · fex-team/http2-spec · GitHub.md)):
Upon establishment of a TCP connection and determination that HTTP/2 will be used by both peers, each endpoint MUST send a connection preface as a final confirmation and to establish the initial SETTINGS parameters for the HTTP/2 connection.
在建立TCP连接并且检测到HTTP/2会被各个对等端使用后,每个端点必须发送一个连接序言最终确认并作为建立HTTP/2连接的初始设置参数。
The client connection preface starts with a sequence of 24 octets, which in hex notation are:
客户端连接序言以24个字节的序列开始,以十六进制表示是:
0x505249202a20485454502f322e300d0a0d0a534d0d0a0d0a
(the string PRI * HTTP/2.0rnrnSMrnrn). This sequence is followed by a SETTINGS frame (Section 6.5). The SETTINGS frame MAY be empty.
(字符串PRI * HTTP/2.0rnrnSMrnrn)。这个序列后跟着一个设置帧,其可为空帧。
The server connection preface consists of a potentially empty SETTINGS frame (Section 6.5) that MUST be the first frame the server sends in the HTTP/2 connection.
服务端连接序言包含一个有可能是空的设置(SETTING)帧(章节6.5),它必须在HTTP/2连接中首个发送。
To avoid unnecessary latency, clients are permitted to send additional frames to the server immediately after sending the client connection preface, without waiting to receive the server connection preface. It is important to note, however, that the server connection preface SETTINGS frame might include parameters that necessarily alter how a client is expected to communicate with the server. Upon receiving the SETTINGS frame, the client is expected to honor any parameters established.
为了避免不必要的延迟,允许客户端在发送客户端连接序言之后立即发送其他额外的帧,不需要等待收到服务端连接序言。不过需要注意的是,服务端连接序言设置(SETTINGS)帧可能包含一些关于期望客户端如何与服务端通信的所必须修改的参数。在收到这些设置(SETTINGS)帧之后,客户端应当遵守所有设置的参数。
Clients and servers MUST terminate the TCP connection if either peer does not begin with a valid connection preface. A GOAWAY frame (Section 6.8) can be omitted if it is clear that the peer is not using HTTP/2.
如果任何一个端点没有以一个有效的连接序言开头,客户端和服务端必须终止TCP连接。如果端点并没有使用HTTP/2此时可以省略超时(GOAWAY)帧(章节6.8)。
newHTTP2Client
{
conn=dial()
t=基于conn建立http2Client
framer 基于conn建立newFramer(conn),
go t.reader() //循环读取所有帧,并分发到对应流中
t.conn.Write(clientPreface) //发送preface帧给服务端
t.framer.writeSettings //发送setting帧。疑问:与writeWindowUpdate的关系。
t.framer.writeWindowUpdate
go t.controller()//用于发送控制消息给服务端
}
客户端writeWindowUpdate两个时机,最终作用到server端对应的handleWindowUpdate:
1、newHTTP2Client->framer.writeWindowUpdate
2、io.ReadFull(s1, p)->http2Client.updateWindow->t.framer.writeWindowUpdate
客户端:创建客户端数据流
函数:NewStream
流程:
1、创建stream:newStream
2、fc inFlow用于接收流控
3、dec=recvBufferReader用于接收数据缓存
4、将新创建的stream加入到activeStreams
客户端:写数据
函数:Write
流程:
1、从s.sendQuotaPool.acquire()、t.sendQuotaPool.acquire()获取发送数据权限
2、从t.writableChan获取写权限。(writableChan用于控制write串行化。写入表示释放锁,读取表示获取锁。)
3、t.framer.writeData向服务端发送数据
客户端:读数据
函数:reader
流程:
1、获取setting帧
2、循环获取所有帧,并分发给到相应的stream上。
服务端
与客户端代码基本类似不再细讲。主要差异是HandleStreams,该方法用于接收客户端请求。
数据流处理:
1、handleData接收数据->数据通过Stream.write写入Stream.buf
2、operateHeaders handle参数为对应的数据接收处理策略。读取Stream.buf 中写入的数据进行相应处理。
其中server.go中实现是:
2.1、processUnaryRPC 处理流程: 接收消息p.recvMsg -> 处理md.Handler -> 发送响应s.sendResponse -> 回复状态t.WriteStatus
2.2、processStreamingRPC处理流程:建立serverStream -> sd.Handler -> t.WriteStatus
3、步骤2中的Handler是RegisterService注入的。