剖析nsq消息队列-目录

实际应用中,一部分服务集群可能会同时订阅同一个topic,并且处于同一个channel下。当nsqd有消息需要发送给订阅客户端去处理时,发给哪个客户端是需要考虑的,也就是我要说的消息的负载。

如果不考虑负载情况,把随机的把消息发送到某一个客服端去处理消息,如果机器的性能不同,可能发生的情况就是某一个或几个客户端处理速度慢,但还有大量新的消息需要处理,其他的客户端处于空闲状态。理想的状态是,找到当前相对空闲的客户端去处理消息。

nsq的处理方式是客户端主动向nsqd报告自已的可处理消息数量(也就是RDY命令)。nsqd根据每个连接的客户端的可处理消息的状态来随机把消息发送到可用的客户端,来进行消息处理

如下图所示:

客户端更新RDY

从第一篇帖子的例子中我们就有配置consumer的config

	config := nsq.NewConfig()
config.MaxInFlight = 1000
config.MaxBackoffDuration = 5 * time.Second
config.DialTimeout = 10 * time.Second

MaxInFlight 来设置最大的处理中的消息数量,会根据这个变量计算在是否更新RDY

初始化的时候 客户端会向连接的nsqd服务端来发送updateRDY来设置最大处理数,

func (r *Consumer) maybeUpdateRDY(conn *Conn) {
inBackoff := r.inBackoff()
inBackoffTimeout := r.inBackoffTimeout()
if inBackoff || inBackoffTimeout {
r.log(LogLevelDebug, "(%s) skip sending RDY inBackoff:%v || inBackoffTimeout:%v",
conn, inBackoff, inBackoffTimeout)
return
} remain := conn.RDY()
lastRdyCount := conn.LastRDY()
count := r.perConnMaxInFlight() // refill when at 1, or at 25%, or if connections have changed and we're imbalanced
if remain <= 1 || remain < (lastRdyCount/4) || (count > 0 && count < remain) {
r.log(LogLevelDebug, "(%s) sending RDY %d (%d remain from last RDY %d)",
conn, count, remain, lastRdyCount)
r.updateRDY(conn, count)
} else {
r.log(LogLevelDebug, "(%s) skip sending RDY %d (%d remain out of last RDY %d)",
conn, count, remain, lastRdyCount)
}
}

当剩余的可用处理数量remain 小于等于1,或者小于最后一次设置的可用数量lastRdyCount的1/4时,或者可用连接平均的maxInFlight大于0并且小于remain时,则更新RDY状态

当有多个nsqd时,会把最大的消息进行平均计算:

// perConnMaxInFlight calculates the per-connection max-in-flight count.
//
// This may change dynamically based on the number of connections to nsqd the Consumer
// is responsible for.
func (r *Consumer) perConnMaxInFlight() int64 {
b := float64(r.getMaxInFlight())
s := b / float64(len(r.conns()))
return int64(math.Min(math.Max(1, s), b))
}

当有消息从nsqd发送过来后也会调用maybeUpdateRDY方法,计算是否需要发送RDY命令

func (r *Consumer) onConnMessage(c *Conn, msg *Message) {
atomic.AddInt64(&r.totalRdyCount, -1)
atomic.AddUint64(&r.messagesReceived, 1)
r.incomingMessages <- msg
r.maybeUpdateRDY(c)
}

上面就是主要的处理逻辑,但还有一些逻辑,就是当消息处理发生错误时,nsq有自己的退避算法backoff也会更新RDY 简单来说就是当发现有处理错误时,来进行重试和指数退避,在退避期间RDY会为0,重试时会先放尝试RDY为1看有没有错误,如果没有错误则全部放开,这个算法这篇帖子我就不详细说了。

服务端nsqd选择客户端进行发送消息

同时订阅同一topic的客户端(comsumer)有很多个,每个客户端根据自己的配置或状态发送RDY命令到nsqd表明自己能处理多少消息量

nsqd服务端会检查每个客户端的的状态是否可以发送消息。也就是IsReadyForMessages方法,判断inFlightCount是否大于readyCount,如果大于或者等于就不再给客户端发送数据,等待Ready后才会再给客户端发送数据

func (c *clientV2) IsReadyForMessages() bool {
if c.Channel.IsPaused() {
return false
} readyCount := atomic.LoadInt64(&c.ReadyCount)
inFlightCount := atomic.LoadInt64(&c.InFlightCount) c.ctx.nsqd.logf(LOG_DEBUG, "[%s] state rdy: %4d inflt: %4d", c, readyCount, inFlightCount) if inFlightCount >= readyCount || readyCount <= 0 {
return false
} return true

每一次发送消息inFlightCount会+1并保存到发送中的队列中,当客户端发送FIN会-1在之前的帖子中有说过。

func (p *protocolV2) messagePump(client *clientV2, startedChan chan bool) {
// ...
for {
// 检查订阅状态和消息是否可处理状态
if subChannel == nil || !client.IsReadyForMessages() {
// the client is not ready to receive messages...
memoryMsgChan = nil
backendMsgChan = nil
flusherChan = nil
// ...
flushed = true
} else if flushed {
memoryMsgChan = subChannel.memoryMsgChan
backendMsgChan = subChannel.backend.ReadChan()
flusherChan = nil
} else {
memoryMsgChan = subChannel.memoryMsgChan
backendMsgChan = subChannel.backend.ReadChan()
flusherChan = outputBufferTicker.C
} select {
case <-flusherChan:
// ...
// 消息处理
case b := <-backendMsgChan:
client.SendingMessage()
// ...
case msg := <-memoryMsgChan:
client.SendingMessage()
//...
}
}
// ...
}

剖析nsq消息队列(四) 消息的负载处理的更多相关文章

  1. 为什么使用消息队列?消息队列有什么优点和缺点?Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么优点和缺点?

    面试题 为什么使用消息队列? 消息队列有什么优点和缺点? Kafka.ActiveMQ.RabbitMQ.RocketMQ 都有什么区别,以及适合哪些场景? 面试官心理分析 其实面试官主要是想看看: ...

  2. (二)RabbitMQ消息队列-RabbitMQ消息队列架构与基本概念

    原文:(二)RabbitMQ消息队列-RabbitMQ消息队列架构与基本概念 没错我还是没有讲怎么安装和写一个HelloWord,不过快了,这一章我们先了解下RabbitMQ的基本概念. Rabbit ...

  3. System V 消息队列 - 复用消息

    消息队列中的消息结构可以由我们自由定义,具备较强的灵活性.通过消息结构可以共享一个队列,进行消息复用.通常定义一个类似如下的消息结构: #define MSGMAXDAT 1024 struct my ...

  4. rabbitmq消息队列,消息发送失败,消息持久化,消费者处理失败相关

    转:https://blog.csdn.net/u014373554/article/details/92686063 项目是使用springboot项目开发的,前是代码实现,后面有分析发送消息失败. ...

  5. activemq读取剩余消息队列中消息的数量

    先上原文链接: http://blog.csdn.net/bodybo/article/details/5647968  ActiveMQ在C#中的应用 ActiveMQ是个好东东,不必多说.Acti ...

  6. 为什么使用消息队列? 消息队列有什么优点和缺点? Kafka、ActiveMQ、RabbitMQ、RocketMQ 都有什么区别,以及适合哪些场景?

    https://blog.csdn.net/Iperishing/article/details/86674084

  7. 剖析nsq消息队列目录

    剖析nsq消息队列(一) 简介及去中心化实现原理 剖析nsq消息队列(二) 去中心化源码解析 剖析nsq消息队列(三) 消息传输的可靠性和持久化[一] 剖析nsq消息队列(三) 消息传输的可靠性和持久 ...

  8. 四十九、进程间通信——System V IPC 之消息队列

    49.1 System V IPC 介绍 49.1.1 System V IPC 概述 UNIX 系统存在信号.管道和命名管道等基本进程间通讯机制 System V 引入了三种高级进程间通信机制 消息 ...

  9. Linux网络编程学习(九) ----- 消息队列(第四章)

    1.System V IPC System V中引入的几种新的进程间通信方式,消息队列,信号量和共享内存,统称为System V IPC,其具体实例在内核中是以对象的形式出现的,称为IPC 对象,每个 ...

随机推荐

  1. 向现有URL末尾添加查询字符串参数

    向现有URL末尾添加查询字符串参数 xhr.open("get", "example.php?name1=value1&name2=value2", t ...

  2. 2017.12.21 学习vue的新得

    温故而知新,这句话说的真的有道理.每次回顾vue总会学到不一样的知识点,我就在想,我第一遍到底看了什么? 废话不多说,简要记录今天的所得: 1.v-if 与 v-show 同:都是条件渲染 异:渲染的 ...

  3. 用c语言实现前序创建二叉树(递归),分别用前序,中序,后序遍历,以及分别输出节点个数和叶子节点个数

    本人c语言小白一枚,近期在学习数据结构(c语言版),特写此随笔,做一些总结和分享,如有不当之处,请各位技术大牛斧正 首先我们用一个结构体来抽象树的结点,代码如下(这里我们存放的数据为char型,大家可 ...

  4. 替换word中的数据,并给导入word的图片添加水印

    public static void ExportWord(string tempFilePath, string outPath, Dictionary<string, string> ...

  5. 第三十五章 POSIX共享内存

    POSIX共享内存函数介绍 shm_open 功能: 用来创建或打开一个共享内存对象 原型: int shm_open(const char *name, int oflag, mode_t mode ...

  6. Win10 连接CentOS 8 的Docker容器中 SqlServer数据库

    楔子 工作在win10环境下,使用Docker Windows桌面版容器化SqlServer数据库连接使用(主要是想用Docker),但是同时需要Linux系统测试,win10 下VMware 虚拟机 ...

  7. 读书笔记-《Maven实战》-2018/5/3

    5.7依赖调解 1.当一个项目有以下依赖关系的时候:A->B->C->X(1.0).A->D->X(2.0),X作为A的传递依赖而拥有两个版本,Maven为了解决以上问题 ...

  8. u检验粗浅理解

    假设检验是以小概率事件,在一次实验中是不可能发生为前提(事实上是有可能发生的,但不是这样说的话,就落入一个圈,不能继续玩了),来否认原假设. u检验的定义: 已知从正态母体N(u,σ2)中抽得容量为n ...

  9. [专题总结]矩阵树定理Matrix_Tree及题目&题解

    专题做完了还是要说两句留下什么东西的. 矩阵树定理通俗点讲就是: 建立矩阵A[i][j]=edge(i,j),(i!=j).即矩阵这一项的系数是两点间直接相连的边数. 而A[i][i]=deg(i). ...

  10. Hadoop3.2.1版本的环境搭建

    最近有人提出能不能发一些大数据相关的知识,No problem ! 今天先从安装环境说起,搭建起自己的学习环境. Hadoop的三种搭建方式以及使用环境: 单机版适合开发调试: 伪分布式适合模拟集群学 ...