nsq源码分析(2):nsqlookup之tcp服务
本章涉及db的读写操作,请参考 nsqlookup之RegistrationDB数据库
通信协议
本章内容涉及tcp协议的封包解包内容,请参考 nsq tcp协议规范
[ ][ ][V][1]
client连接后,客户端必须发送一个4 字节的 “magic” 标识码来选择通讯协议的版本。
启动tcp服务
nsqlookupd/nsqlookupd.go
// 启动tcp服务
tcpListener, err := net.Listen("tcp", l.opts.TCPAddress)
if err != nil {
l.logf("FATAL: listen (%s) failed - %s", l.opts.TCPAddress, err)
os.Exit(1)
}
l.Lock()
l.tcpListener = tcpListener
l.Unlock()
tcpServer := &tcpServer{ctx: ctx}
// 封装的waitGroup,内部使用goroutine启动该服务,使用waitGroup守护改协程直到退出
l.waitGroup.Wrap(func() {
protocol.TCPServer(tcpListener, tcpServer, l.opts.Logger)
})
接收client连接并回调handle方法
internal/protocol/tcp_server.go
type TCPHandler interface {
Handle(net.Conn)
}
func TCPServer(listener net.Listener, handler TCPHandler, l app.Logger) {
l.Output(2, fmt.Sprintf("TCP: listening on %s", listener.Addr()))
// 接收client连接并回调handle方法
for {
clientConn, err := listener.Accept()
if err != nil {
if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
l.Output(2, fmt.Sprintf("NOTICE: temporary Accept() failure - %s", err))
// runtime.Gosched让出cpu时间片,让另一时间片处理
runtime.Gosched()
continue
}
// theres no direct way to detect this error because it is not exposed
if !strings.Contains(err.Error(), "use of closed network connection") {
l.Output(2, fmt.Sprintf("ERROR: listener.Accept() - %s", err))
}
break
}
// 这里的handle方法虽然是TCPHandler的接口类型,实际回调的是nsqlookupd/tcp.go中Handle方法
go handler.Handle(clientConn)
}
l.Output(2, fmt.Sprintf("TCP: closing %s", listener.Addr()))
}
解析协议版本和命令
解析协议版本
nsqlookupd/tcp.go
// 回调handle:解析协议版本
func (p *tcpServer) Handle(clientConn net.Conn) {
p.ctx.nsqlookupd.logf("TCP: new client(%s)", clientConn.RemoteAddr())
// The client should initialize itself by sending a 4 byte sequence indicating
// the version of the protocol that it intends to communicate, this will allow us
// to gracefully upgrade the protocol away from text/line oriented to whatever...
// 读取tcp流协议的前四个字节,用于选择使用哪种协议
buf := make([]byte, 4)
_, err := io.ReadFull(clientConn, buf)
if err != nil {
p.ctx.nsqlookupd.logf("ERROR: failed to read protocol version - %s", err)
return
}
protocolMagic := string(buf)
p.ctx.nsqlookupd.logf("CLIENT(%s): desired protocol magic '%s'"