本篇讲nsqlookupd中tcp.go、tcp_server.go
tcp_server.go位于util目录下。
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
package util
import ( "log" "net" "runtime" "strings" ) type TCPHandler interface { Handle (net. Conn ) } /** *本方法开始接受客户端连接,并注册处理方法 */ func TCPServer (listener net. Listener , handler TCPHandler ) { log. Printf ( "TCP: listening on %s" , listener. Addr ( ). String ( ) ) for { clientConn , err := listener. Accept ( ) if err != nil { if nerr , ok := err. (net. Error ) ; ok && nerr. Temporary ( ) { log. Printf ( "NOTICE: temporary Accept() failure - %s" , err. Error ( ) ) 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" ) { log. Printf ( "ERROR: listener.Accept() - %s" , err. Error ( ) ) } break } //handler由调用时通过参数传入,作为工具方法,将可能的变化放在外部,以保证本方法可以有尽可能多的适用范围 go handler. Handle (clientConn ) } log. Printf ( "TCP: closing %s" , listener. Addr ( ). String ( ) ) } |
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
package nsqlookupd
import ( "io" "log" "net" "github.com/bitly/nsq/util" ) type tcpServer struct { context *Context } // // nsqlookupd接收到tcp数据时,会调用本方法处理。 //实现了/util/tcp_server.go中定义的TCPHandler接口 // func (p *tcpServer ) Handle (clientConn net. Conn ) { log. Printf ( "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... //客户端在初始化自己的时候,需要发送4字节的数据用来标识它自己所有使用协议版本。将来升级协议的时候可以避免使用旧协议的客户端不能使用。 buf := make ( [ ]byte , 4 ) _ , err := io. ReadFull (clientConn , buf ) if err != nil { log. Printf ( "ERROR: failed to read protocol version - %s" , err. Error ( ) ) return } //获取协议内容 protocolMagic := string (buf ) log. Printf ( "CLIENT(%s): desired protocol magic '%s'" , clientConn. RemoteAddr ( ) , protocolMagic ) //util\Protocol.go中定义了一个Protocol的接口 var prot util. Protocol switch protocolMagic { case " V1" : //当前只支持" V1"协议(前两有两个空格,所以总共是4字节),协议在nsqlookupd\lookup_protocol_v1.go文件中定义,这里创建了LookupProtocolV1的实例 //LookupProtocolV1实现了Protocol接口 prot = &LookupProtocolV1 {context : p. context } default : //如果不是" V1"协议,则协议出错,断开链接,返回。 util. SendResponse (clientConn , [ ]byte ( "E_BAD_PROTOCOL" ) ) clientConn. Close ( ) log. Printf ( "ERROR: client(%s) bad protocol magic '%s'" , clientConn. RemoteAddr ( ) , protocolMagic ) return } //如果是" V1"f协议,这里就进入了LookupProtocolV1的IOLoop方法。此方法里有for死循环运行,直到出现error时,才会执行下面的代码。 err = prot. IOLoop (clientConn ) if err != nil { log. Printf ( "ERROR: client(%s) - %s" , clientConn. RemoteAddr ( ) , err. Error ( ) ) return } } |
从以上代码可看出,tcp.go和tcp_server.go主要功能就是监听并接受TCP请求,收到请求后,将数据转给lookup_protocol_v1.go来处理。这个下篇再讲。