NSQ学习笔记(三)

这次主要是聊一下nsq 的可用性方案

今天在看go的源代码时发现了个之前没理清楚的地方。就是go的import。
之前自己的写法都是包名和路径名统一的,所以模糊了import的具体逻辑,今天看到调用github.com/nsqio/go-nsq的时候,又梳理了一遍。

import xxx时,xxx为目录,再调用的时候是 package.xxx

所以我们可以发现如下情况:
import github.com/nsqio/go-nsq
使用的时候是nsq.consumer

这是因为目录/../github.com/nsqio/go-nsq
而里边的代码是 package nsq


authd有个坑爹的地方,就是例子里有一条记录login(auth secret)为空,但在实际操作上,如果这个字段为空就代表不会有效果,这是个误导人的地方

nsq_to_file这个小工具不支持auth,简单的两行代码,设置下


nsq可用性方案

常见的可用性方案:

Master-Slave

  1. 写master,然后slave定期从master那里拉数据。
    这种情况下如果master崩了,这个时间片的数据会丢失

  2. 写master,master再写slave,slave写成功后,master返回成功

Master-Master

Master间的异步做数据同步

Two/Three Phase Commit(2/3PC)

2PC分为两段,先vote,再decision

此处输入图片的描述

升级版,3PC
此处输入图片的描述

2PC在一阶段锁,所以如果二阶段时出现问题会锁住整个事物
3PC 二阶段都同意了,再锁资源
Paxos
强一致性算法

各种方式的对比

此处输入图片的描述

NWR模型

N个备份,W个写,R个读。R > N - W
也就是说,每次读取,都至少读取到一个最新的版本。

对比notify rocketq,我们可以发现,当设置mem_queue_size = 0时,nsq属于同步写盘,producer请求打到nsq,nsq同步写盘

func (d *diskQueue) Put(data []byte) error {
    d.RLock()
    defer d.RUnlock()

    if d.exitFlag == 1 {
        return errors.New("exiting")
    }

    d.writeChan <- data
    // 此处是nsqd有一个ioloop()常驻线程持续接受数据写入
    return <-d.writeResponseChan
}

func (d *diskQueue) ioLoop() {
    ...
    for {
        ...
        select {
            ...
        case dataWrite := <-d.writeChan:
            count++
            d.writeResponseChan <- d.writeOne(dataWrite)
            ...
        }
    }
    ...
}

不过这里边的写盘调用的是 go里边 file.Write 函数,也就是说更新的只是内存中的页缓存,而脏页面不会立即更新到硬盘中。当遇到突然断电,和系统崩溃时还是会有数据丢失。

横向对比
redis的持久化分为RDB快照、AOF
AOF中appendfsync可配置三个级别
a. no => redis不主动调用fsync,何时刷盘由OS来调度;
b. always => redis针对每个写入命令均会主动调用fsync刷磁盘;
c. everysec => 每秒调一次fsync刷盘。

redis always级别采用的是fdatasync,避免写入metadata
always 时同步,everysec 又独立进程完成rewrite Aof log