NSQ 学习随记(1)

NSQ是用golang编写的高性能分布式消息队列(可以理解为轻量化的kafka),就个人理解来说,消息队列可以看成一个大的buffer ,用于信息生产速率和信息消费速率、分布式消息同步,同时也便于一些容灾策略的实现。

本系列文章着重点在于深入源码理解,不会写一些大的概念以及整体架构,方便理解具体功能的具体实现方式。

项目地址 nsq

整体的代码结构如下

  • apps里主要是nsq系列进程的启动器,可以理解为具体业务逻辑。主要包含nsqd,nsqlookup以及一些通讯和存储相关daemon
  • bench里主要是一些性能测试代码,没啥说的。
  • contrib里是一些配置文件,有兴趣可以看看到底需要配置啥。
  • internal是主要的方法库和底层逻辑,本系列会重点关注这一部分
  • nsqadmin是web管理界面的代码,可以稍微看看,主要是了解打桩数据的获取方式以及这些数据说明的问题
  • nsqd和nsqlookup就是这两部分需要的方法库。

 

作为系列的第一篇文章,先大概阐述下internal包的部分内容

  • 首先是实现了float_array和string_array(我只能说实现的方法很朴实……本来以为会有一些优化考虑)
  • 提供了认证机制,对producer的publish和customer的subscribe操作进行校验,主要是校验合法性(是不是可以推送,是不是可以订阅等),同时包括认证过期检测、tls等。(实现也很朴实……)
  • 提供了集群信息的获取接口(admin和其他的一些会用到,自己也可以使用这些方法来进行上层封装),这一部分主要涉及http相关的知识,部分控制信息通过Header传送。
  • 文件锁部分,log文件(数据文件)需要进行缓存,在这里提供了一个dirlock,调用系统的flock接口。看上去目前没有提供windos版本
  • http_api部分,这部分主要就是封装了下http通信接口,同时在传输时进行压缩(gzip和deflate方法),我个人觉得比较好的点就是Hijack方法的使用,自己管理连接,不用通过go原生的client,同时可以偷换应用层协议,只使用http的建连方式,这样实际效果好很多。
  • 自己实现了一个优先队列,没啥特殊的。
  • 定义了logger接口,并且提供了对loglevel的一系列操作
  • 提供了端到端(e2e)延迟收集接口。
  • 提供了一些字符串处理方法(比如一些字段的fmt、查询,合并等操作)
  • 提供了一个rand方法,可以防止原生rand造成的碰撞(math/rand是伪随机,可能会出现随机结果相同的情况,如果两个协程同时访问一个单元就会block住)
  • 封了下WaitGroup,方便使用,包了个函数句柄。
    type WaitGroupWrapper struct {
    	sync.WaitGroup
    }
    
    func (w *WaitGroupWrapper) Wrap(cb func()) {
    	w.Add(1)
    	go func() {
    		cb()
    		w.Done()
    	}()
    }

     

  • 关于传输的相关协议:首先是提供了base10方法(ascii码转uint64,0~9的映射)
  • 内部提供了一个TCPserver
type TCPHandler interface {
	Handle(net.Conn)
}

func TCPServer(listener net.Listener, handler TCPHandler, logf lg.AppLogFunc) error {
	logf(lg.INFO, "TCP: listening on %s", listener.Addr())

	var wg sync.WaitGroup

	for {
		clientConn, err := listener.Accept()
		if err != nil {
			if nerr, ok := err.(net.Error); ok && nerr.Temporary() {
				logf(lg.WARN, "temporary Accept() failure - %s", err)
				runtime.Gosched()//让当前goroutine释放cpu资源
				continue
			}
			// theres no direct way to detect this error because it is not exposed
			if !strings.Contains(err.Error(), "use of closed network connection") {
				return fmt.Errorf("listener.Accept() error - %s", err)
			}
			break
		}

		wg.Add(1)
		go func() {
			handler.Handle(clientConn)//处理链接
			wg.Done()
		}()
	}

	// wait to return until all handler goroutines complete
	wg.Wait()

	logf(lg.INFO, "TCP: closing %s", listener.Addr())

	return nil
}
  • 提供了传输协议接口,目前有V1和V2两版,nsqd使用V2,nsqdlookup使用V1(这个地方不是很清楚为什么这么做,后面补充)
  • 剩下就是一些valid方法,检查一些命名规范问题。

 

总体来说整体还是比较简单的,结构也比较清晰。

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章