从本节开始,将逐步阅读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建立的Listener
tcpListener net.Listener

//使用上面的httpAddr建立的Listener
httpListener 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}

//监听TCP
tcpListener, 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) })

//监听HTTP
httpListener, 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

这些文件,将在后续文章中继续阅读

go语言 nsq源码解读三 nsqlookupd源码nsqlookupd.go的更多相关文章

  1. DRF(1) - REST、DRF(View源码解读、APIView源码解读)

    一.REST 1.什么是编程? 数据结构和算法的结合. 2.什么是REST? 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下: /books/ /get_all_books/ 访问所 ...

  2. REST、DRF(View源码解读、APIView源码解读)

    一 . REST            前言 1 . 编程 : 数据结构和算法的结合 .小程序如简单的计算器,我们输入初始数据,经过计算,得到最终的数据,这个过程中,初始数据和结果数据都是数据,而计算 ...

  3. Restful 1 -- REST、DRF(View源码解读、APIView源码解读)及框架实现

    一.REST 1.什么是编程? 数据结构和算法的结合 2.什么是REST? - url用来唯一定位资源,http请求方式来区分用户行为 首先回顾我们曾经做过的图书管理系统,我们是这样设计url的,如下 ...

  4. 5.2 spring5源码--spring AOP源码分析三---切面源码分析

    一. AOP切面源码分析 源码分析分为三部分 1. 解析切面 2. 创建动态代理 3. 调用 源码的入口 源码分析的入口, 从注解开始: 组件的入口是一个注解, 比如启用AOP的注解@EnableAs ...

  5. mybatis源码解读(三)——数据源的配置

    在mybatis-configuration.xml 文件中,我们进行了如下的配置: <!-- 可以配置多个运行环境,但是每个 SqlSessionFactory 实例只能选择一个运行环境常用: ...

  6. jQuery源码解读三选择器

    直接上jQuery源码截取代码 // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ i ...

  7. Python Web Flask源码解读(三)——模板渲染过程

    关于我 一个有思想的程序猿,终身学习实践者,目前在一个创业团队任team lead,技术栈涉及Android.Python.Java和Go,这个也是我们团队的主要技术栈. Github:https:/ ...

  8. ConcurrentHashMap源码解读三

    今天首先讲解helpTransfer方法 final Node<K,V>[] helpTransfer(Node<K,V>[] tab, Node<K,V> f) ...

  9. spring源码解读之 JdbcTemplate源码

    原文:https://blog.csdn.net/songjinbin/article/details/19857567 在Spring中,JdbcTemplate是经常被使用的类来帮助用户程序操作数 ...

随机推荐

  1. ASP.NET MVC不可或缺的部分——DI(IOC)容器及控制器重构的剖析(DI的实现原理)

    IoC框架最本质的东西:反射或者EMIT来实例化对象.然后我们可以加上缓存,或者一些策略来控制对象的生命周期,比如是否是单例对象还是每次都生成一个新的对象. DI实现其实很简单,首先设计类来实现接口, ...

  2. 关于web页面JApplet打印小票

    版权所有 做这个的例子太少,我把我做的示例亮出来 一.先说说需要的版本 1.我用的浏览器只有ie: 火狐只支持52版本以下,并且是java7.java8.chrome不支持 2.applet客户端打印 ...

  3. 详谈linux中压缩

    1.压 缩 的 用 途 和 技 术 1.1 为什么需要压缩: ①你是否有过文件档案太大,导致无法以正常的email方式发送出去(很多email都有容量大约25MB每封信的限制啊!)? ②你是否有过要备 ...

  4. java垃圾回收总结(2)

    java垃圾回收总结(2)   上一篇文章 介绍了jvm虚拟机运行时内存结构以及如何标识需要回收的对象,这一节主要讲解垃圾回收的基本算法. 基本上 jvm内存回收有三种 基本算法 标记-清除 标记清除 ...

  5. JavaScript中对象数组,如何给对象添加一个新属性

    var a =[{name: 'Tom',age:20},{name: 'Tom2',age:22}] 现在给a数组中的第一个对象添加性别属性 a[0]['gender']='women' a[0][ ...

  6. Linux 文件目录解释

    /bin:bin是binary(二进制)的缩写.这个目录是对UNIX系统习惯的沿袭,存放着使用者最经常使用的命令.例如:cp,ls,cat. /boot:这里存放的是启动LINUX时使用的一些核心文件 ...

  7. JDK 源码分析(4)—— HashMap/LinkedHashMap/Hashtable

    JDK 源码分析(4)-- HashMap/LinkedHashMap/Hashtable HashMap HashMap采用的是哈希算法+链表冲突解决,table的大小永远为2次幂,因为在初始化的时 ...

  8. 基于Python的数据分析(1):配置安装环境

    数据分析是一个历史久远的东西,但是直到近代微型计算机的普及,数据分析的价值才得到大家的重视.到了今天,数据分析已经成为企业生产运维的一个核心组成部分. 据我自己做数据分析的经验来看,目前数据分析按照使 ...

  9. Python_文件与文件夹操作

    ''' os模块除了提供使用操作系统功能和访问文件系统的简便方法之外,还提供了大量文件与文件夹操作的方法. os.path模块提供了大量用于路径判断.切分.连接以及文件夹遍历的方法. shutil模块 ...

  10. maven安装和配置及创建maven项目

    (1)下载maven,下载成功后,解压到本地磁盘 里面包含这几项 (2)配置maven环境变量MAVEN_HOME.path (3)最后检验配置是否成功:用win键+R,来打开命令行提示符窗口,即Do ...