NSQ的TCP逻辑都是这样的,调用internal/protocol/tcp_server.go中的TCPServer:
func TCPServer(listener net.Listener, handler TCPHandler, l app.Logger)接受客户端连接,在这里客户端为consumer,调用TCPHandler.Handle处理业务逻辑,TCPHandler.Handle是个接口,让不同的业务逻辑自己实现这个接口。
这里在tcp.go文件中实现:
func (p *tcpServer) Handle(clientConn net.Conn) {
p.ctx.nsqd.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...
buf := make([]byte, 4)
//从流中读取4个字节的数据到buf,被读取的数据,会从流中截取掉
_, err := io.ReadFull(clientConn, buf)
if err != nil {
p.ctx.nsqd.logf("ERROR: failed to read protocol version - %s", err)
return
}
protocolMagic := string(buf)
p.ctx.nsqd.logf("CLIENT(%s): desired protocol magic '%s'",
clientConn.RemoteAddr(), protocolMagic)
var prot protocol.Protocol
switch protocolMagic {
case " V2":
prot = &protocolV2{ctx: p.ctx}
default:
//如果不是" V2"协议,报错,该goroutine停止
protocol.SendFramedResponse(clientConn, frameTypeError, []byte("E_BAD_PROTOCOL"))
clientConn.Close()
p.ctx.nsqd.logf("ERROR: client(%s) bad protocol magic '%s'",
clientConn.RemoteAddr(), protocolMagic)
return
}
//接口,实际调用的是protocolV2.IOLoop
err = prot.IOLoop(clientConn)
if err != nil {
p.ctx.nsqd.logf("ERROR: client(%s) - %s", clientConn.RemoteAddr(), err)
return
}
}
先判断协议版本,不是“ V2”(前面有两个空格),报错,结束该goroutine。如果是,则调用该协议对应的IOLoop方法,读取协议内容,处理协议的具体行为。这里的prot.IOLoop也是一个接口,供每个协议自己实现,V2的实现方式在protocol_v2.go文件中。
V2协议大致为“ V2 command params\n”或“ V2 command params\r\n”
协议开头必须是“ V2”,command为要执行的命令,命令有IDENTIFY、SUB、PUB等等,params为命令的参数,具体参考官网这里。