zoukankan      html  css  js  c++  java
  • go语言 nsq源码解读三 nsqlookupd源码nsqlookupd.go

    从本节开始,将逐步阅读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 sqlookupd.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 {
    //在文件nsqlookupdoptions.go中定义,记录NSQLookupd的配置信息
    options *nsqlookupdOptions

    //nsqlookupd监听TCP数据的地址
    tcpAddr *net.TCPAddr

    //nsqlookupd监听HTTP数据的地址
    httpAddr *net.TCPAddr

    //使用上面的tcpAddr建立的Listener
    tcpListener net.Listener

    //使用上面的httpAddr建立的Listener
    httpListener net.Listener

    //在utilwait_group_wrapper.go文件中定义,与sync.WaitGroup相关,用于线程同步。
    waitGroup util.WaitGroupWrapper

    //在nsqlookupd egistration_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 sqlookupd sqlookupd.go里的main方法,这个下篇文章会提到。
    //
    func (*NSQLookupd) Main() {
    //定义了Context的实例,Context在nsqlookupdcontext.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 cp.go文件中定义,用于处理TCP连接中接收到的数据。通过前面阅读知道,context里只是一个NSQLookupd类型的指针。
    tcpServer := &tcpServer{context: context}

    //调用util.TCPServer方法(在util cp_server.go中定义)开始接收监听并注册handler。 //传入的两个参数第一个是tcpListener
    //第二个tcpServer实现了util cp_server.go中定义的TCPHandler接口。
    //tcpServer接到TCP数据时,会调用其Handle方法(见nsqlookupd cp.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在nsqlookupdhttp.go文件中定义
    httpServer := &httpServer{context: context}

    //调用util.HTTPServer方法(在utilhttp_server.go中定义)开始在指定的httpListener上接收http连接。
    //传入的两个参数第一个是httpListener
    //第二个httpServer定义了http handler,用于处理HTTP请求。
    //同样,对waitGroup的用法还不是很理解。
    l.waitGroup.Wrap(func() { util.HTTPServer(httpListener, httpServer) })

    //经过以上阅读,基本上会有两个发现:
    //1、tcpServer和httpServer的代码很相似。
    //2、util cp_server.go在注册handler之前,先定义了一个接口,而tuilhttp_server.go却没有。
    //如果再仔细研究这两个文件,还会发现,tcp_server里,通过go handler.Handle(clientConn)这段代码,把连接clientConn做为变量,传给了handler
    //而在http_server,是把handler传给了HTTPServer
    //这主要是因为net/http包和net包用法不一样,net/http做了进一步有封装。
    }

    //
    //退出 关闭两个Listener
    //
    func (*NSQLookupd) Exit() {
    if l.tcpListener != nil {
    l.tcpListener.Close()
    }

    if l.httpListener != nil {
    l.httpListener.Close()
    }
    l.waitGroup.Wait()
    }

    上面的代码里共涉及到几个外部文件:
    nsqlookupdoptions.go
    nsqlookupdcontext.go
    nsqlookupd cp.go
    util cp_server.go
    nsqlookupdhttp.go
    utilhttp_server.go
    utilwait_group_wrapper.go
    nsqlookupd egistration_db.go

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

  • 相关阅读:
    jQuery对象和DOM对象
    虚拟主机的部署(Apache)
    事件流:事件冒泡和事件捕获
    ThinkPHP
    级联下拉列表
    今日份抽自己!!!
    c++中关于输入字符数组的一些问题
    今日新知(关于递归中变量的声明)
    格子游戏(并查集)
    1.3-14大象喝水
  • 原文地址:https://www.cnblogs.com/zhangboyu/p/7456928.html
Copyright © 2011-2022 走看看