diskqueue第六篇 - 如何使用diskqueue,示例

 diskqueue是nsq消息持久化的核心,内容较多,故分为多篇

1. diskqueue第一篇 - 是什么,为什么需要它,整体架构图,对外接口

2. diskqueue第二篇 - 元数据文件,数据文件,启动入口,元数据文件的读写及保存

3. diskqueue第三篇 - 数据定义详解,运转核心ioloop()源码详解

4. diskqueue第四篇 - 怎么写入消息,怎么对外发送消息

5. diskqueue第五篇 - 追尾检测,错误处理,如何正常关闭

6. diskqueue第六篇 - 如何使用diskqueue,示例

经过前面5篇博客的介绍,大家对diskqueue的整体框架、内部原理应该很清楚了,这篇博客我们讲如何使用diskqueue,毕竟学习它是为了更好地使用。以nsq中实际使用为例,希望大家能看得更清楚一些

一、使用diskqueue的方法,先说结论

1. 调用diskqueue.New()函数来创建一个新的diskqueue

2. 调用diskqueue.Put()函数往diskqueue中写入消息,需在select中执行

3. 调用diskqueue.ReadChan()函数从diskqueue中接收消息,需在select中执行

4. 调用diskqueue.Empty()函数+diskqueue.Delete()函数来删除一个diskqueue

5. 调用diskqueue.Close()函数正常关闭一个diskqueue

二、使用diskqueue的举例,以nsq的channel为例

1. 调用diskqueue.New()函数来创建一个新的diskqueue

示例代码,源码位置在nsq/nsqd/channel.go

// 新建一个channel
func NewChannel(topicName string, channelName string, nsqd *NSQD, deleteCallback func(*Channel)) *Channel {
        // 省略无关代码
		c.backend = diskqueue.New(
			backendName,
			nsqd.getOpts().DataPath,
			nsqd.getOpts().MaxBytesPerFile,
			int32(minValidMsgLength),
			int32(nsqd.getOpts().MaxMsgSize)+minValidMsgLength,
			nsqd.getOpts().SyncEvery,
			nsqd.getOpts().SyncTimeout,
			dqLogf,
        // 省略无关代码
}

上面的NewChannel()函数是创建channel,内部调用了diskqueue.New()来创建一个新的diskqueue,内部参数不再一一解释了

2. 调用diskqueue.Put()函数往diskqueue中写入消息,需在select中执行

示例:topic往channel中压入消息时会调用put()函数,源码位置在nsq/nsqd/channel.go

// 压入一个新消息
func (c *Channel) put(m *Message) error {
	select {
	case c.memoryMsgChan <- m:	// 先尝试压入内存队列
	default:
		err := writeMessageToBackend(m, c.backend)	// 不成功的话则压入持久化队列
		c.nsqd.SetHealth(err)
		if err != nil {
			c.nsqd.logf(LOG_ERROR, "CHANNEL(%s): failed to write message to backend - %s", c.name, err)
			return err
		}
	}
	return nil
}

put()函数内部:先尝试往内存队列中压入,如果不成功,则调用writeMessageToBackend()函数压入到持久化队列,即diskqueue

再来看writeMessageToBackend()函数,源码位置在nsq/nsqd/message.go

// 把指定消息写入持久化队列
func writeMessageToBackend(msg *Message, bq BackendQueue) error {
	// 使用buffpoll作为写入缓冲区
	buf := bufferPoolGet()
	defer bufferPoolPut(buf)
	// 先把消息写入buff
	_, err := msg.WriteTo(buf)
	if err != nil {
		return err
	}
	// 再往队列中写
	return bq.Put(buf.Bytes())
}

函数内部先把消息序列化成[]byte,再调用diskqueue.Put()函数写入

3. 调用diskqueue.ReadChan()函数从diskqueue中接收消息,需在select中执行

示例:客户端连接从持久化队列中接收消息,往客户端推送

源码位置:nsq/nsqd/protocol_v2.go的messagePump()函数

// 客户端连接的消息循环
func (p *protocolV2) messagePump(client *clientV2, startedChan chan bool) {
        var backendMsgChan 	<-chan []byte	// 持久化队列的通道(只读)        
        // 省略无关代码
	    backendMsgChan 	= subChannel.backend.ReadChan()

		select {
        // 省略无关代码
		case b := <-backendMsgChan:		// 持久化队列中有消息
			if sampleRate > 0 && rand.Int31n(100) > sampleRate {
				continue
			}
        }   
}

可以看到messagePump()函数中的backendMsgChan会被赋值为diskqueue.ReadChan()的返回值,这是个只读的通道,然后在select中读取

4. 调用diskqueue.Empty()+diskqueue.Delete()来删除一个diskqueue

示例:nsq的channel关闭时会调用diskqueue的Empty()和Delete()函数

源码位置:nsq/nsqd/channel.go的Delete()函数

// 删除channel的处理
func (c *Channel) Delete() error {
	return c.exit(true)
}

函数内部调用了exit()函数,参数为true表删除,再来看exit()函数的实现

// channel退出操作(删除或关闭channel时调用)
func (c *Channel) exit(deleted bool) error {
    // 省略无关代码......

	// 删除channel时的操作
	if deleted {
		c.Empty()		// 清空消息(清空内存队列,清空持久化队列->调用Empty()函数)
		return c.backend.Delete()
	}

	// 关闭channel时的操作
	c.flush()					// 把数据刷到磁盘
	return c.backend.Close()
}

可以看到参数deleted为true时,有两步操作

第一步:调用Empty()函数,该函数内部调用了diskqueue.Empty()函数进行清空操作

第二步:调用diskqueue.Delete()函数

5. 调用diskqueue.Close()函数正常关闭一个diskqueue

示例: nsq的channel关闭时会调用diskqueue.Delete()函数

源码位置:nsq/nsqd/channel.go的Close()函数

// 关闭channel的处理(nsqd关闭时才调用)
func (c *Channel) Close() error {
	return c.exit(false)
}

函数内部调用了exit()函数,参数为false

具体exit()函数的实现,上面第4模块刚刚讲过,大家可以看到函数最下面调用了diskqueue.Close()函数,不再重复了

三、本篇总结

本篇博客主要讲如何使用diskqueue,并对各种用法都给出了示例,示例取自nsq。只要大家能仔细看下nsq的使用例子,相信以后自己使用时会得心应手

至此,diskqueue全部讲完

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值