今天來說說NSQD中的TCP 服務,TCP服務的任務包括接收/處理/響應發佈、消費、延時、消費確認等命令。
詳細流程參考 https://blog.csdn.net/H_L_S/article/details/104709619 中的邏輯流程圖。
主要代碼文件:
1.internal/protocol/tcp_server.go,該文件主要是啓動一個服務監聽,循環接收客戶端連接,並且將conn,交由一個goroutine ,執行指定Handle 。
func TCPServer(listener net.Listener, handler TCPHandler, logf lg.AppLogFunc) error {
var wg sync.WaitGroup
...
for {
//接收新連接
clientConn, err := listener.Accept()
....
wg.Add(1)
//啓動goroutin 處理clientConn
go func() {
handler.Handle(clientConn)
wg.Done()
}()
}
wg.Wait()
return nil
}
2.nsqd/tcp.go,該文件主要處理conn的連接。Handle函數主要兩件事,一是協議版本驗證,二是如果協議沒有問題,啓動IOLoop,處理客戶端請求,比如發佈,消費,消費確認等。
type tcpServer struct {
ctx *context //NSQD 實例
conns sync.Map //併發安全的MAP
}
func (p *tcpServer) Handle(clientConn net.Conn) {
buf := make([]byte, 4)
_, err := io.ReadFull(clientConn, buf) //版本爲4個字節
....
protocolMagic := string(buf)
....
var prot protocol.Protocol
switch protocolMagic { //驗證版本協議
case " V2":
prot = &protocolV2{ctx: p.ctx}
default: //版本協議錯誤,返回錯誤,關閉連接
protocol.SendFramedResponse(clientConn, frameTypeError, []byte("E_BAD_PROTOCOL"))
clientConn.Close()
return
}
//記錄正在處理IP
p.conns.Store(clientConn.RemoteAddr(), clientConn)
//循環處理客戶端請求,包括訂閱,發佈,消費確認等命令。
err = prot.IOLoop(clientConn)
if err != nil {
p.ctx.nsqd.logf(LOG_ERROR, "client(%s) - %s", clientConn.RemoteAddr(), err)
}
//處理結束,請求IP 記錄
p.conns.Delete(clientConn.RemoteAddr())
}
3.internal/protocol/protocol.go, 該文件主要是定義了IOLoop的接口,同時提供了二個回覆客戶端消息的函數。
type Protocol interface {
IOLoop(conn net.Conn) error
}
//發送的消息的格式:len(4個字節)+body(消息體)
func SendResponse(w io.Writer, data []byte) (int, error) {
err := binary.Write(w, binary.BigEndian, int32(len(data)))
if err != nil {
return 0, err
}
n, err := w.Write(data)
if err != nil {
return 0, err
}
return (n + 4), nil
}
//發送帶有frameType(可以認爲是消息類型,包括錯誤響應,正常消息響應等)
//格式:len(4字節)+frameType(4個字節)+body(消息體)
func SendFramedResponse(w io.Writer, frameType int32, data []byte) (int, error) {
beBuf := make([]byte, 4)
size := uint32(len(data)) + 4
binary.BigEndian.PutUint32(beBuf, size)
n, err := w.Write(beBuf)
if err != nil {
return n, err
}
binary.BigEndian.PutUint32(beBuf, uint32(frameType))
n, err = w.Write(beBuf)
if err != nil {
return n + 4, err
}
n, err = w.Write(data)
return n + 8, err
}
4.nsqd/protocol_v2.go是IOLoop,的具體實現。負責接收請求,處理請求,topic/channel的主要處理邏輯都在這實現,是整個tcpServer 的核心。 因爲比較複雜,會在下一篇分析。
總結
1.internal/protocol/tcp_server.go 主要是用於接收客戶的連接請求,同時將conn交於指定的Handle處理。
2.nsqd/tcp.go 實現tcp_server.go中的Handle,主要是驗證協議版本,同時啓動IOLoop 處理請求。
3.internal/protocol/protocol.go 定義了IOLoop 接口 和 實現了兩個回覆客戶端消息的函數。
4.nsqd/protocol_v2.go 是IOLoop的具體實現,主要負責接收請求,處理請求,響應客戶端。
下次分享:nsqd 中 TCPServer 的IOLoop的具體實現