nsq源码阅读 nsqlookupd源码一 nsqlookupd.go

查看nsq的几个模块,发现nsqlookupd模块的代码量最少,所以从它开始。从nsqlookupd/nsqlookupd.go文件开始:

package nsqlookupd

import (
	"fmt"
	"log"
	"net"
	"os"
	"sync"

	"github.com/nsqio/nsq/internal/http_api"
	"github.com/nsqio/nsq/internal/protocol"
	"github.com/nsqio/nsq/internal/util"
	"github.com/nsqio/nsq/internal/version"
)

type NSQLookupd struct {
	//读写互斥锁 https://golang.org/pkg/sync/#RWMutex
	//http://blog.csdn.net/aslackers/article/details/62044726
	/**
	 * 基本遵循两大原则:
	 * 1、可以随便度,多个goroutine同时读
	 * 2、写的时候,啥也不能干。不能读也不能写
	 */
	sync.RWMutex
	//在文件nsqlookupd/options.go中定义,记录NSQLookupd的配置信息
	opts         *Options
	tcpListener  net.Listener
	httpListener net.Listener
	//在文件internal/util/wait_group_wrapper.go中定义,与sync.WaitGroup相关,用于线程同步
	waitGroup util.WaitGroupWrapper
	//在文件nsqlookupd/registration_db.go中定义,与数据存储有关
	DB *RegistrationDB
}

//根据配置的nsqlookupd options信息,创建一个NSQLookupd实例
func New(opts *Options) *NSQLookupd {
	if opts.Logger == nil {
		opts.Logger = log.New(os.Stderr, opts.LogPrefix, log.Ldate|log.Ltime|log.Lmicroseconds)
	}
	n := &NSQLookupd{
		opts: opts,
		DB:   NewRegistrationDB(),
	}
	n.logf(version.String("nsqlookupd"))
	return n
}

func (l *NSQLookupd) logf(f string, args ...interface{}) {
	l.opts.Logger.Output(2, fmt.Sprintf(f, args...))
}

//Main函数,启动nsqlockupd进程时,
//首先运行nsqlookupd/options.go里的NewOptions()这个方法,设置默认配置
//然后运行上面的New()方法,根据配置信息创建一个NSQLookupd实例
//再运行这里的Main()方法,启动nsqlockupd进程
//详情见apps/nsqlookupd/nsqlookupd.go里的Start()方法
func (l *NSQLookupd) Main() {
	//Context实例,在文件nsqlookupd/context.go中定义,NSQLookupd类型的指针
	ctx := &Context{l}

	//监听TCP,监听地址取options中的默认参数,0.0.0.0:4160
	tcpListener, err := net.Listen("tcp", l.opts.TCPAddress)
	if err != nil {
		l.logf("FATAL: listen (%s) failed - %s", l.opts.TCPAddress, err)
		os.Exit(1)
	}
	//RWMutex写锁
	l.Lock()
	//将Listener保存到NSQLookupd对象元素里,供后面的方法调用,如:RealTCPAddr()
	l.tcpListener = tcpListener
	l.Unlock()
	//tcpServer实例,在文件nsqlookupd/tcp.go中定义,处理TCP接收到的数据
	tcpServer := &tcpServer{ctx: ctx}
	//使用sync.WaitGroup,关于sync.WaitGroup介绍,http://blog.csdn.net/aslackers/article/details/62046306
	//通过下面的Exit()方法,可知,当关闭nsqlookupd进程时,主线程(goroutine)会等待所有TCP监听关闭,才关闭自己
	l.waitGroup.Wrap(func() {
		//在文件internal/protocol/tcp_server.go中定义,
		//接收监听并处理TCP数据
		//第二个参数tcpServer实现了internal/protocol/tcp_server.go文件中定义的TCPHandler接口
		//tcpServer接收到TCP数据时,会调用其Handle()方法(nsqlookupd/tcp.go)处理。
		protocol.TCPServer(tcpListener, tcpServer, l.opts.Logger)
	})

	//监听并处理HTTP,逻辑和上面TCP差不多 0.0.0.0:4161
	httpListener, err := net.Listen("tcp", l.opts.HTTPAddress)
	if err != nil {
		l.logf("FATAL: listen (%s) failed - %s", l.opts.HTTPAddress, err)
		os.Exit(1)
	}
	l.Lock()
	l.httpListener = httpListener
	l.Unlock()
	//httpServer实例,在文件nsqlookupd/http.go中定义,处理HTTP接收到的数据
	//并定义了一系列HTTP接口(路由)
	httpServer := newHTTPServer(ctx)
	l.waitGroup.Wrap(func() {
		http_api.Serve(httpListener, httpServer, "HTTP", l.opts.Logger)
	})
}

//获取监听的TCP地址
func (l *NSQLookupd) RealTCPAddr() *net.TCPAddr {
	l.RLock()
	defer l.RUnlock()
	return l.tcpListener.Addr().(*net.TCPAddr)
}

//获取HTTP地址
func (l *NSQLookupd) RealHTTPAddr() *net.TCPAddr {
	l.RLock()
	defer l.RUnlock()
	return l.httpListener.Addr().(*net.TCPAddr)
}

//退出nsqloopupd进程,关闭两个Listener,等待TCP和HTTP线程都关闭了,才关闭自身
func (l *NSQLookupd) Exit() {
	if l.tcpListener != nil {
		l.tcpListener.Close()
	}

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


程序执行步骤:

1、执行nsqlookupd/options.go中的NewOptions()方法,创建Options实例,生成NSQLookupd配置信息

2、执行上面的New()方法,将第1步中创建的Options配置信息作为参数,创建NSQLookupd实例

3、执行NSQLookupd实例的Main()方法,创建nsqloopupd进程,TCP监听0.0.0.0:4160,HTTP监听0.0.0.0:4161

nsqloopupd进程退出时,调用NSQLookupd实例的Exit()方法,关闭TCP和HTTP监听,主线程(goroutine)等待子线程(goroutine)退出,程序退出




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值