NSQ 源碼分析之NSQD--TCPServer

今天來說說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的具體實現

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章