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

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

  • 相关阅读:
    ....
    CodeForces 375A(同余)
    POJ 2377 Bad Cowtractors (最小生成树)
    POJ 1258 AgriNet (最小生成树)
    HDU 1016 Prime Ring Problem(全排列)
    HDU 4460 Friend Chains(bfs)
    POJ 2236 Wireless Network(并查集)
    POJ 2100 Graveyard Design(尺取)
    POJ 2110 Mountain Walking(二分/bfs)
    CodeForces 1059B Forgery(模拟)
  • 原文地址:https://www.cnblogs.com/zhangboyu/p/7456928.html
Copyright © 2011-2022 走看看