今天来说说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的具体实现