NSQ源码分析之lookupd

什么是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
}
  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值