从本节开始,将逐步阅读nsq各模块的代码。
读一份代码,我的思路一般是:
1、了解用法,知道了怎么使用,对理解代码有宏观上有很大帮助。
2、了解各大模块的功能特点,同时再想想,如果让自己来实现这些模块,会是怎么样的思路。
3、开始上手试读,为不打击阅读的积极性,可以选择一个简单的模块,或者某一个功能点开始读。对nsq而言,打开源码的目录看一下,发现nsqlookupd和nsqadmin的代码相对较少,而nsqd的代码量较多。再比较nsqlookupd和nsqadmin,发现nsqadmin下还有一个templates目录,这大概是在第一篇文章里用来显示截图里的网页的模板文件。再考虑到nsqlookupd的中枢作用,我决定从nsqlookupd的代码开始读起。
4、读代码的第一遍,偏向于读懂,了解功能的实现即可。所有代码全部读过一遍后,看一下文件名,就能知道这个文件里的代码实现了什么功能。碰到读不懂的地方,可以通过加注释输出变量、打断点跟踪等方式辅助学习。
5、之后读第二遍,理解宏观的架构体系,心里始终要想的问题是: 为什么要这么做?如果是我,我会怎么做?这两种做法有什么利弊?多揣摩,细研读,并把体会到的精华思想吸引牢记,转为已有。
6、再之后,可以读第三遍,这基本就是拨云见日的境界了,对代码了如之掌,考虑是否有更好的实现,然后可以对代码动手改造。如果在代码还没读懂前改代码,那属于在给白雪公主喂屎,恶心的要死了。
下面我们开始nsqlookupd的源码解读。
nsqlookupd的代码位于源码根目录的nsqlookupd下。目录下共十一个文件,去掉README.md文件和两个以_test.go结尾(这是单元测试文件)的文件,共有八个文件。另外nsqlookupd还会用到util目录下的一些功能代码,这个也会阅读到。对代码的解释我都会放在注释里,等第一遍代码阅读完,我会把所有代码打包传上来。
OK,首先从nsqlookupd\nsqlookupd.go文件开始:
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 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
package nsqlookupd import ("log""net""github.com/nsqio/nsq/util")type NSQLookupd struct {//在文件nsqlookupd\options.go中定义,记录NSQLookupd的配置信息options *nsqlookupdOptions //nsqlookupd监听TCP数据的地址tcpAddr *net .TCPAddr //nsqlookupd监听HTTP数据的地址httpAddr *net .TCPAddr //使用上面的tcpAddr建立的ListenertcpListener net .Listener //使用上面的httpAddr建立的ListenerhttpListener net .Listener //在util\wait_group_wrapper.go文件中定义,与sync.WaitGroup相关,用于线程同步。waitGroup util .WaitGroupWrapper //在nsqlookupd\registration_db.go文件中定义,看字面意思DB(database)就可知道这涉及到数据的存取DB *RegistrationDB }////根据配置的nsqlookupdOptions创建一个NSQLookupd的实例//func NewNSQLookupd (options *nsqlookupdOptions ) *NSQLookupd {//使用配置参数的TCPAddress创建TCP地址,用于和nsqd通信。tcpAddr , err := net .ResolveTCPAddr ("tcp", options .TCPAddress )if err != nil {log.Fatal (err )}//使用配置参数的HTTPAddress参数,创建http链接,可以供nsqadmin访问,以读取统计数据httpAddr , err := net .ResolveTCPAddr ("tcp", options .HTTPAddress )if err != nil {log.Fatal (err )}return &NSQLookupd {options : options ,tcpAddr : tcpAddr ,httpAddr : httpAddr ,DB : NewRegistrationDB (),}}////Main函数,启动时首先执行本函数//补注:阅读options.go时,发现nsqlookupd启动时,首先运行的并不是这个Main方法。而是apps\nsqlookupd\nsqlookupd.go里的main方法,这个下篇文章会提到。//func (l *NSQLookupd ) Main () {//定义了Context的实例,Context在nsqlookupd\context.go文件中定义,其中只包含了一个nsqlookupd的指针,注意花括号里是字符L的小写,不是数字一.context := &Context {l }//监听TCPtcpListener , err := net .Listen ("tcp", l .tcpAddr .String ())if err != nil {log.Fatalf ("FATAL: listen (%s) failed - %s", l .tcpAddr , err .Error ())}//把Listener存在NSQLookupd的struct里l .tcpListener = tcpListener //创建tcpServer的实例,tcpServer在nsqlookupd\tcp.go文件中定义,用于处理TCP连接中接收到的数据。通过前面阅读知道,context里只是一个NSQLookupd类型的指针。tcpServer := &tcpServer {context : context }//调用util.TCPServer方法(在util\tcp_server.go中定义)开始接收监听并注册handler。 //传入的两个参数第一个是tcpListener//第二个tcpServer实现了util\tcp_server.go中定义的TCPHandler接口。//tcpServer接到TCP数据时,会调用其Handle方法(见nsqlookupd\tcp.go)来处理。//此处为何要用到waitGroup,目前还比较迷糊l .waitGroup .Wrap (func () { util .TCPServer (tcpListener , tcpServer ) })//监听HTTPhttpListener , err := net .Listen ("tcp", l .httpAddr .String ())if err != nil {log.Fatalf ("FATAL: listen (%s) failed - %s", l .httpAddr , err .Error ())}//把Listener存在NSQLookupd的struct里l .httpListener = httpListener //创建httpServer的实例,httpServer在nsqlookupd\http.go文件中定义httpServer := & ;httpServer {context : context }//调用util.HTTPServer方法(在util\http_server.go中定义)开始在指定的httpListener上接收http连接。//传入的两个参数第一个是httpListener//第二个httpServer定义了http handler,用于处理HTTP请求。//同样,对waitGroup的用法还不是很理解。l .waitGroup .Wrap (func () { util .HTTPServer (httpListener , httpServer ) })//经过以上阅读,基本上会有两个发现://1、tcpServer和httpServer的代码很相似。//2、util\tcp_server.go在注册handler之前,先定义了一个接口,而tuil\http_server.go却没有。//如果再仔细研究这两个文件,还会发现,tcp_server里,通过go handler.Handle(clientConn)这段代码,把连接clientConn做为变量,传给了handler//而在http_server,是把handler传给了HTTPServer//这主要是因为net/http包和net包用法不一样,net/http做了进一步有封装。}////退出 关闭两个Listener//func (l *NSQLookupd ) Exit() {if l .tcpListener != nil {l .tcpListener .Close ()}if l .httpListener != nil {l .httpListener .Close ()}l .waitGroup .Wait ()}
|
上面的代码里共涉及到几个外部文件: nsqlookupd\options.go nsqlookupd\context.go nsqlookupd\tcp.go util\tcp_server.go nsqlookupd\http.go util\http_server.go util\wait_group_wrapper.go nsqlookupd\registration_db.go
这些文件,将在后续文章中继续阅读
|
请发表评论