什么是lookupd
上一篇文章中提到,lookupd是NSQ关键模块之一,负责管理nsq的拓扑信息。客户端通过查询 lookupd 来发现指定 topic的生产者(也就是服务发现),存储了nsqd的元数据和服务信息(endpoind),并且向nsqd 节点广播 topic 和 channel 信息。
源码分析
nsqlookupd
首先我们看看lookupd接口体的成员。
type NSQLookupd struct {
// 读写锁
sync.RWMutex
// 启动时的配置参数
opts *Options
// 各种网络监听器
tcpListener net.Listener
httpListener net.Listener
tcpServer *tcpServer
// 多线程时的同步等待组
waitGroup util.WaitGroupWrapper
// 地址缓存
DB *RegistrationDB
}
其他什么都能理解,但是这个RegistrationDB是用来做什么的呢?看看它里面装了啥。
type RegistrationDB struct {
sync.RWMutex
// 主要的一个字典
registrationMap map[Registration]ProducerMap
}
// 字典的键
type Registration struct {
Category string
Key string
SubKey string
}
// 字典的键值,存的就是注册了的生产者的通信地址信息
type ProducerMap map[string]*Producer
type Producer struct {
peerInfo *PeerInfo
tombstoned bool
tombstonedAt time.Time
}
type PeerInfo struct {
lastUpdate int64
id string
RemoteAddress string `json:"remote_address"`
Hostname string `json:"hostname"`
BroadcastAddress string `json:"broadcast_address"`
TCPPort int `json:"tcp_port"`
HTTPPort int `json:"http_port"`
Version string `json:"version"`
}
创建
func New(opts *Options) (*NSQLookupd, error) {
var err error
if opts.Logger == nil {
opts.Logger = log.New(os.Stderr, opts.LogPrefix, log.Ldate|log.Ltime|log.Lmicroseconds)
}
// 创建一个lookupd对象,同时把配置信息填进去,再创建一个缓存
l := &NSQLookupd{
opts: opts,
DB: NewRegistrationDB(),
}
l.logf(LOG_INFO, version.String("nsqlookupd"))
//根据配置信息,创建端口监听器
l.tcpServer = &tcpServer{nsqlookupd: l}
l.tcpListener, err = net.Listen("tcp", opts.TCPAddress)
if err != nil {
return nil, fmt.Errorf("listen (%s) failed - %s", opts.TCPAddress, err)
}
l.httpListener, err = net.Listen("tcp", opts.HTTPAddress)
if err != nil {
return nil, fmt.Errorf("listen (%s) failed - %s", opts.HTTPAddress, err)
}
return l, nil
}
主线程
lookupd的启动线程,它负责开启tcp和http监听器,如果发生错误通过exitCh管道讲错误信息返回出来。
func (l *NSQLookupd) Main() error {
exitCh := make(chan error)
// 这里也用到了,保证并发下只执行一次的锁
var once sync.Once
exitFunc := func(err error) {
once.Do(func() {
if err != nil {
l.logf(LOG_FATAL, "%s", err)
}
exitCh <- err
})
}
l.waitGroup.Wrap(func() {
exitFunc(protocol.TCPServer(l.tcpListener, l.tcpServer, l.logf))
})
httpServer := newHTTPServer(l)
l.waitGroup.Wrap(func() {
exitFunc(http_api.Serve(l.httpListener, httpServer, "HTTP", l.logf))
})
err := <-exitCh
return err
}