NSQ之客户端

2,148 阅读22分钟

原文地址:nsq.io/clients/cli…

客户端

客户端库

该表中的信息是否过时?您是否在生产中使用这些客户端库之一?在邮件列表或Twitter @ imsnakes / @ jehiah上告诉我们。

NameLanguageSUBPUBDiscoveryBackoffTLSSnappySamplingAUTHNotes
nsqdHTTPbuilt-in
go-nsqGoofficial
pynsqPythonofficial
nsqjsJavaScriptofficial
elixir_nsqElixir
ensqErlang
nsq-jJava
JavaNSQClientJava
TrendrrNSQClientJava
nsqjavaJava
nsq.jsJavaScript
node-nsqueueJavaScript
knsqKotlin
NsqSharp.NET
php-nsqPHP
phpnsqPHP
nsq-pyPython
gnsqPython
krakowRuby
ruby_nsqRuby
evnsqC++
libnsqCofficial
hsnsqHaskell
nsq-javaJava
nsq-clientJavaScript
NSQnet.NET
perl-anyevent-nsqPerl
NsqSpinnerPython
nsq-rubyRuby
uvnsqC++11
nsq-clojureClojure
nsqieScala
nodensqJavaScript
nsqueueRust
asyncnsqPython
nsq-ocamlOCaml
node-red-contrib-nsqNodeRED Nodes
asyncnsqRust
tokio-nsqRust
ansqPython

构建客户端库

NSQ的设计将很多责任推给了客户端库,以维持整个集群的健壮性和性能。

本指南试图概述行为良好的客户端库需要履行的各种职责。因为发布到nsqd是微不足道的(只是发送HTTP POST请求到/put端点 ),所以本文档主要针对消费者。

通过设定这些期望,我们希望为NSQ用户实现跨语言一致性提供基础。

概述

  1. 配置
  2. 发现(可选)
  3. 连接处理
  4. 功能协商
  5. 数据流/心跳
  6. 消息处理
  7. RDY状态
  8. backoff
  9. 加密/压缩

配置

在较高的层次上,我们关于配置的理念是将系统设计为具有支持不同工作负载的灵活性,使用合理的默认值(开箱即用)运行,并最大程度地减少拨号次数。

消费者通过TCP连接到nsqd实例,并在一个通道(channel)上订阅主题(topic)。每个连接只能订阅一个主题,因此需要相应地组织多个主题的消费。

使用nsqlookupd的服务发现是可选的,所以客户端库应该支持配置一个消费者可以直接连接到一个或多个nsqd实例或者配置为轮询一个或多个nsqlookupd实例。当消费者配置为轮询nsqlookupd时,轮询间隔应该是可配置的。此外,典型的NSQ部署是在具有许多生产者和消费者的分布式环境中,因此客户端库应自动添加基于配置值的随机百分比抖动(jitter)。这将有助于避免人潮汹涌的联系。有关更多详细信息,请参见Discovery

对消费者而言,一个重要的性能瓶颈是nsqd 期望得到响应之前它可以接收的消息数。这种流水线(pipelining)便于缓冲、批处理和异步消息处理。按照惯例,该值被称为max_in_flight,并且影响RDY状态如何管理。有关更多详细信息,请参见RDY状态

作为一个旨在妥善处理故障的系统,期望客户端库对失败的消息实现重试处理,并提供根据每条消息的尝试次数这一选项来限制该行为。有关更多详细信息,请参见消息处理

相关地,当消息处理失败时,期望客户端库自动处理消息的重新排队。NSQ支持随着REQ命令一起发送一个延迟。期望客户端库提供有关此延迟的选项:初始设置(对于第一次失败)以及对于随后的失败应如何更改。有关更多详细信息,请参见 Backoff

最重要的是,客户端库应支持某种方法,用于配置消息处理的回调。这些回调的签名应该简单,通常接收单个参数(一个"消息对象"的实例)。

发现

NSQ的重要组成部分是nsqlookupd,它为消费者提供发现服务,以定位在运行时提供给定主题的nsqd

尽管是可选的,但使用nsqlookupd会大大减少维护和扩展大型分布式NSQ集群所需的配置量。

当消费者使用nsqlookupd发现服务时,客户端库应管理所有nsqlookupd实例的轮询过程,以获取最新所关注主题的nsqd集合,并应当管理这些nsqd实例的连接。

查询nsqlookupd实例很简单。将消费者尝试发现的主题作为查询参数执行一个HTTP请求到查询端点(例如/lookup?topic=clicks)。响应格式为JSON

{
    "channels": ["archive", "science", "metrics"],
    "producers": [
        {
            "broadcast_address": "clicksapi01.routable.domain.net",
            "hostname": "clicksapi01.domain.net",
            "remote_address": "172.31.27.114:51996",
            "tcp_port": 4150,
            "http_port": 4151,
            "version": "1.0.0-compat"
        },
        {
            "broadcast_address": "clicksapi02.routable.domain.net",
            "hostname": "clicksapi02.domain.net",
            "remote_address": "172.31.34.29:14340",
            "tcp_port": 4150,
            "http_port": 4151,
            "version": "1.0.0-compat"
        }
    ]
}

broadcast_addresstcp_port用来连接到nsqd。因为根据设计, nsqlookupd实例不共享或协调它们的数据,所以客户端库应合并从所有nsqlookupd查询中接收到的列表,以构建最终要连接的nsqd列表。broadcast_address:tcp_port的组合应作为合并的唯一键。

应该使用定期计时器重复轮询已配置的nsqlookupd,以便消费者自动发现新的nsqd。客户端库应自动初始化所有新发现实例的连接。

当客户端库执行开始时,应通过启动已配置的nsqlookupd实例的一组初始请求来引导此轮询过程。

连接处理

一旦消费者连接到nsqd(通过发现服务或者手动配置),它应当打开一个TCP连接到broadcast_address:port.对于消费者想要订阅的每个主题,应该为每个主题建立单独的TCP连接到nsqd

当连接到nsqd实例时,客户端库应按顺序发送以下数据:

  1. 魔术标识符(the magic identifier)
  2. 一个IDENTIFY命令(以及载荷)和读/验证响应(参见功能协商
  3. 一个SUB命令(指定期望的主题)以及读/验证响应
  4. 一个初始为1的RDY状态计数(参见RDY 状态

(有关该协议的底层细节,请参阅规范

重新连接

客户端库应自动处理重新连接,如下所示:

  • 如果为消费者配置了特定的nsqd实例列表,则应通过指数退避(exponential backoff)的方式延迟重新尝试来处理重新连接(即尝试以8s,16s,32s等进行重新连接,直到最大次数)。
  • 如果将消费者配置为通过nsqlookupd来发现实例,则应根据轮询间隔自动处理重新连接(即如果消费者与nsqd断开连接,则客户端库应仅在随后的nsqlookupd轮询回合中尝试重新连接已发现的实例)。这样可以确保消费者可以了解到 拓扑中引入的nsqd以及已删除(或失败)的nsqd

功能协商

IDENTIFY命令可用于设置nsqd端元数据,修改客户端设置以及协商功能。它满足两个需求:

  1. 在某些情况下,客户端希望修改nsqd与其交互的方式(例如,修改客户端的心跳间隔并启用压缩,TLS,输出缓冲等。有关完整列表,请参见规范
  2. nsqd使用JSON负载来响应IDENTIFY命令,该负载包含重要的服务器端配置值,客户端在与nsqd实例进行交互时应遵循这些配置值。

连接后,基于用户的配置,客户端库应发送IDENTIFY 命令,其body为一个JSON负载:

{
    "client_id": "metrics_increment",
    "hostname": "app01.bitly.net",
    "heartbeat_interval": 30000,
    "feature_negotiation": true
}

feature_negotiation字段表示客户端可以接受JSON负载作为返回。client_idhostname是任意的文本字段,nsqd(和nsqadmin)使用它们来识别客户端。heartbeat_interval配置每个客户端的心跳间隔。

如果不支持功能协商(nsqd v0.2.20+引入)nsqd会作出OK回应,否则:

{
    "max_rdy_count": 2500,
    "version": "0.2.20-alpha"
}

有关max_rdy_count字段使用的更多详细信息,请参见"RDY状态"部分。

数据流和心跳

一旦消费者处于订阅状态后,NSQ协议中的数据流将是异步的。对于消费者而言,这意味着为了构建真正强大且高性能的客户端库,应该使用异步网络IO循环和/或"线程"对它们进行结构化(双引号用于表示OS级线程和用户级线程,例如协程)。

此外,还希望客户端响应来自所连接的nsqd实例的定期心跳。默认情况下,此间隔为30秒。客户端可以使用任何命令进行响应, 但是按照惯例,每当收到心跳信号时,简化响应最简单的方法就是使用NOP。有关如何识别心跳的详细信息,请参见协议规范

"线程"应专用于从TCP套接字读取数据,从帧中解包数据以及执行多路复用逻辑以适当地路由数据。这也是方便地处理心跳的最佳地点(the best spot)。在最底层,读取协议涉及以下顺序步骤:

  1. read 4 byte big endian uint32 size
  2. read size bytes data
  3. unpack data
  4. profit
  5. goto 1

关于Errors的小插曲

由于异步特性,为了使协议错误(protocol errors )与生成它们的命令相关联,需要花费一些额外的状态跟踪。相反,我们采用了"快速失败"的方法,因此绝大多数协议层级的错误处理都是致命的。这意味着,如果客户端发送了一个无效命令(或使其自身进入无效状态),则所连接的nsqd实例将通过强制关闭连接(并在可能的情况下向客户端发送错误)来保护自身(和系统)。这加上上面提到的连接处理,使得系统更加健壮和稳定。

只有这些错误是非致命的:

  • E_FIN_FAILED-用于无效消息ID的FIN命令
  • E_REQ_FAILED-用于无效消息ID的REQ命令
  • E_TOUCH_FAILED-用于无效消息ID的TOUCH命令

因为这些错误通常是计时问题,所以并不认为它们是致命的。当消息在nsqd端超时并重新排队然后传递给另一个消费者时,通常会发生这些情况。不再允许原始接收人代表该消息进行响应。

消息处理

当IO循环解包包含消息的数据帧时,它应将该消息路由到已配置的处理程序进行处理。

发送方nsqd希望在其配置的消息超时(默认值:60秒)内收到答复。有几种可能的情况:

  1. 处理程序指示消息已成功处理。
  2. 处理程序指示消息处理不成功。
  3. 处理程序决定需要更多时间来处理消息。
  4. 传输超时的时间到期并且nsqd自动重新排队该消息。

在前三种情况中,客户端库应代表消费者发送相应的指令(分别为FINREQ,和TOUCH)。

FIN命令是最简单的命令。它表明nsqd可以安全地丢弃该消息。FIN也可以用于丢弃您不想处理或重试的消息。

REQ命令表明nsqd应该重新排队该消息(使用可选参数指定延迟其他尝试的时间)。如果消费者未指定可选参数,则客户端库应自动计算与消息处理的尝试次数有关的持续时间(通常为倍数即可)。客户端库应丢弃超出了配置的最大尝试次数的消息。发生这种情况时,应执行用户提供的回调以通知并启用特殊处理。

如果消息处理程序需要的时间超过了配置的消息超时时间,则TOUCH命令可用于重置nsqd端的计时器。可以重复执行此操作,直到消息为FINREQ状态,或达到发送方nsqd配置的max_msg_timeout为止。客户端库永远不能代表消费者自动 TOUCH

如果发送方nsqd实例没有接收到响应,该消息将超时并自动重新排队,然后等待传递给可用的消费者。

最后,每个消息具有一个属性是尝试次数。客户端库应将此值与配置的最大值进行比较,并丢弃超过该值的消息。当消息被丢弃时,应该触发一个回调。此回调典型的默认实现可能包括写入到一个磁盘目录,进行日志记录等。用户应能够覆盖此默认处理。

RDY 状态

由于消息是从nsqd向消费者推送的,因此我们需要一种方法来管理用户域中的数据流,而不是依赖于底层TCP语义。消费者的RDY状态是NSQ的流控机制。

配置部分中概述的那样,消费者使用 max_in_flight进行配置。这是一个并发和性能瓶颈,例如,某些下游系统能够更为轻松地批量处理消息,并从更高的max-in-flight中获益。

当消费者连接nsqd(并订阅)时,其处于初始RDY状态0。此时将不会传递任何消息。

客户端库承担以下职责:

  1. 引导并均匀地将max_in_flight配置分配给所有连接。
  2. 绝不允许所有连接的RDY计数总和(total_rdy_count)超过所配置的max_in_flight
  3. 永远不要超过nsqd配置的每个连接的max_rdy_count
  4. 公开API方法以可靠地指示消息流的不足(message flow starvation)。

1. 引导和分配

当为一个连接选择适当的RDY计数时(为了平均分配max_in_flight),有一些注意事项:

  • 连接数是动态的,甚至通常是事先都不知道的(例如通过nsqlookupd时发现nsqd时 )。
  • max_in_flight可能低于你的连接数

为了启动消息流,客户端库需要发送初始 RDY计数。因为通常不能提前知道最终的连接数,因此应以值1开始,以使客户端库不会不公平地偏向于第一个连接。

此外,在处理完每条消息之后,客户端库应该评估是否应该更新RDY状态。如果当前值等于0或小于最后发送值的25%,则应触发更新。

客户端库应始终尝试在所有连接之间平均分配RDY计数。通常,此实现为max_in_flight / num_conns

但是,当max_in_flight < num_conns时,这个简单公式就不够用了。在这种状态下,客户端库应该通过测量自上次从给定连接收到消息以来的持续时间,来对连接的nsqd"活动性"(liveness)执行动态运行时评估。可配置的期限到期后,应将所有可用的计数重新分配给新的(随机)nsqd集合。这样,您可以保证(最终)会找到带有消息的nsqd。显然,这会对延迟产生影响。

2. 维护 max_in_flight

客户端库应为给定的消费者保持最大的传递(in flight)消息数上限。具体来说,每个连接的RDY计数总和不应超过配置的 max_in_flight

以下是Python中的示例代码,用于确定建议的RDY计数对于给定的连接是否有效:

def send_ready(reader, conn, count):
    if (reader.total_ready_count + count) > reader.max_in_flight:
        return

    conn.send_ready(count)
    conn.rdy_count = count
    reader.total_ready_count += count

3. nsqd 最大RDY计数

每一个nsqd都可以配置--max-rdy-count(有关消费者可以执行握手以确定该值的更多信息,请参见功能协商)。如果消费者发送的RDY计数超出可接受范围,则其连接将被强制关闭。为了向后兼容,如果nsqd实例不支持功能协商,则应假定此值为2500

4. 消息流不足(Message Flow Starvation)

最后,客户端库应提供一种API方法来指示消息流不足。对消费者(在其消息处理程序中)而言,仅仅比较传输中的消息数量与配置的max_in_flight消息数量以决定"处理批次"(process a batch)是不够的。在如下两种情况下是有问题的:

  1. 当消费者配置max_in_flight > 1时,由于存在变量num_conns,有些情况下max_in_flight不能被num_conns整除。由于合同(contract)规定您不得超过 max_in_flight,因此您必须四舍五入,最后遇到所有RDY计数之和小于max_in_flight的情况。
  2. 考虑仅nsqd中的一部分具有消息的情况。由于预期RDY计数是均匀分布的,这些活跃的nsqd仅具有已配置的max_in_flight的一小部分。

在这两种情况下,消费者都不会真正收到max_in_flight条消息。因此,客户端库应公开一个方法is_starved,该方法将评估是否有任何连接处于饥饿(不足)状态,如下所示:

def is_starved(conns):
    for c in conns:
        # the constant 0.85 is designed to *anticipate* starvation rather than wait for it
        if c.in_flight > 0 and c.in_flight >= (c.last_ready * 0.85):
            return True
    return False

消息处理程序应使用is_starved方法来可靠地标识何时处理一批消息。

Backoff

消息处理失败时该怎么办是一个很难回答的问题。在消息处理部分中详细描述了客户端库的行为,它将在推迟某个(递增)持续时间后再处理失败的消息。另一个难题是是否应减少吞吐量。这两个功能之间的相互作用对于整个系统的稳定性至关重要。

通过减慢处理速度或"退避"(backing off),消费者可以使下游系统从瞬态故障中恢复。但是,由于并非总是如此此,该行为应该是可配置的,例如优先考虑延迟的情况。

应通过发送RDY 0到适当的nsqd然后停止消息流来实现退避。应当基于重复失败的次数(指数)来计算保持这种状态的持续时间。同样,成功的处理应缩短此持续时间,直到读取器不再处于退避状态为止。

当读取器处于退避状态时,超时到期后,无论max_in_flight如何,客户端库都应仅发送RDY 1状态。在完全恢复节流之前,这可以有效地"测试水域"( tests the waters)。另外,在退避超时期间,客户端库在计算退避持续时间时应忽略任何成功或失败的结果(即每次退避超时仅应当考虑一个结果)。

加密/压缩

NSQ通过IDENTIFY命令支持加密 和/或 压缩功能协商。TLS用于加密。SnappyDEFLATE都支持用于压缩。Snappy可以作为第三方库使用,但是大多数语言都对DEFLATE具有一些原生支持。

当接收到IDENTIFY响应并且您已经通过TLS tls_v1标志进行了请求,你会得到类似如下的JSON内容:

{
    "deflate": false,
    "deflate_level": 0,
    "max_deflate_level": 6,
    "max_msg_timeout": 900000,
    "max_rdy_count": 2500,
    "msg_timeout": 60000,
    "sample_rate": 0,
    "snappy": true,
    "tls_v1": true,
    "version": "0.2.28"
}

在确认将tls_v1设置为true(表明服务器支持TLS)之后,您可以在网络发送或接收其他任何内容之前启动TLS握手(例如,在Python中使用ssl.wrap_socket回调)。TLS握手成功后,您将立即读取到一个加密的NSQOK响应。

类似,如果启用了压缩,则将查找snappydeflate为值true,然后使用适当的解压器包装套接字的读写调用。再一次,会立即读取到一个压缩的NSQOK响应。

这些压缩功能是互斥的。

非常重要的是,您必须在协商完成加密/压缩之后才阻止缓冲,或者在协商功能时一定要注意将其读为空(read-to-empty)。

总结

分布式系统很有趣。

NSQ集群的各个组件之间的相互协同工作,提供了一个构建健壮、高性能和稳定的基础架构的平台。我们希望本指南能阐明客户端角色的重要性。

在实际实现的所有这些方面,我们将pynsqgo-nsq视为我们的参考代码库。pynsq的结构可以分为三个核心组件:

  • Message-一个高层次的消息对象,它公开了用于响应nsqd状态(FINREQTOUCH等)的方法以及元数据,例如(失败)尝试和时间戳。
  • Connection-连接到特定nsqd的TCP高级封装,该封装知道传递中的消息、RDY状态、协商的功能以及各种计时。
  • Consumer-与用户进行交互的前端API,用于处理发现,创建连接(和订阅),引导和管理RDY状态,解析原始传入的数据,创建Message对象并分发消息给处理程序。
  • Producer -与用户交互的前端API,用于处理消息发布。

我们很高兴为任何对构建NSQ客户端库感兴趣的人提供支持。我们正在寻找贡献者,以继续扩展我们的语言支持以及充实现有库中的功能。社区已经开源了多个客户端库

TCP协议规范

NSQ协议非常简单,以至于使用任何语言构建客户端都非常简单。我们提供官方的Go和Python客户端库。

一个nsqd进程监听在一个可配置的TCP端口上,用于接收客户端连接。

连接后,客户端必须发送一个4字节的"魔术"标识符,以指示将要用于通信的协议版本(升级很容易)。

  • V2 (4-byte ASCII [space][space][V][2])是一个基于流协议,用于消费(以及发布请求/响应)的推送。

验证之后,客户端可以选择发送IDENTIFY命令以提供自定义元数据(例如,更具描述性的标识符)并协商功能。为了开始消费消息,客户端必须订阅(SUB)到一个通道。

订阅后,客户端将处于RDY 0状态。这意味着不会有任何消息发送到客户端。当客户端准备好接收消息时,它应该发送一条命令,更新其RDY状态为准备处理的消息条数,例如100。没有任何其他命令,则会在有可用消息时将100条消息推送到客户端(每次递减该客户端的服务端RDY计数)。

V2协议还设有客户端心跳。每隔30秒(默认30s但可配置),nsqd 将发送_heartbeat_响应并期望一个返回命令(expect a command in return)。如果客户端空闲,则发送 NOP。在2个未答复的_heartbeat_响应后,nsqd将超时并强行关闭其未听到心跳的客户端连接。IDENTIFY命令可用于更改/禁用此行为。

注意

  • 除非另有说明,否则网络上的所有二进制大小/整数都是网络字节顺序 (即,big endian
  • 有效的主题和通道名称是字符[.a-zA-Z0-9_-]并且1 < length <= 64 (在nsqd 0.2.28之前,最大长度为32

命令

IDENTIFY

更新客户端在服务端的元数据以及协商功能(negotiate features)

IDENTIFY\n
[ 4-byte size in bytes ][ N-byte JSON data ]

注意:此命令采用一定大小的JSON BODY前缀,相关字段为:

  • client_id 用于消除客户端歧义的标识符(即特定于消费者的东西)。

  • hostname 部署客户端的主机名。

  • feature_negotiationnsqd v0.2.19+)布尔值,用于指示客户端其是否支持功能协商。如果服务器有能力支持,它将以JSON负荷发回支持的功能和元数据。

  • heartbeat_interval (nsqd v0.2.19+): 心跳毫秒数。有效范围: 1000 <= heartbeat_interval <= configured_max (-1 禁用心跳)--max-heartbeat-interval (nsqd 标志) 控制最大值,默认为 --client-timeout / 2

  • output_buffer_sizensqd v0.2.21+):写入客户端时nsqd将使用的缓冲区字节大小。有效范围:64 <= output_buffer_size <= configured_max-1禁止输出缓冲)。--max-output-buffer-sizensqd标志)控制最大值,默认为 16kb

  • output_buffer_timeoutnsqd v0.2.21+):缓冲超时,在超时之后nsqd缓冲的所有数据将刷新至客户端。有效范围:1ms <= output_buffer_timeout <= configured_max-1禁用超时)

    --max-output-buffer-timeoutnsqd标志)控制最大值, 默认为 250ms

    警告:将客户端output_buffer_timeout配置为极低的值(< 25ms) 会对nsqdCPU使用率产生重大影响(尤其是在> 50个客户端连接的情况下)。

    这是由于当前的实现依赖于Go计时器,该计时器由Go运行时在优先级队列中维护。有关更多详细信息请参见 pull request #236中的提交信息

  • tls_v1nsqd v0.2.22+): 为连接启用TLS。--tls-cert--tls-keynsqd标志)用于启用TLS并配置服务器证书。如果服务器支持TLS,它将回复 "tls_v1": true。客户端应在读取IDENTIFY响应后立即开始TLS握手。服务器将在完成TLS握手后做出OK响应。

  • snappynsqd v0.2.23+):为连接启用snappy压缩。--snappynsqd标志)用于启用服务端支持。

    客户端应在IDENTIFY响应之后立即获得一个额外的,快速的压缩OK响应。客户端不能同时启用snappydeflate

  • deflatensqd v0.2.23+):为连接启用deflate压缩。--deflatensqd标志)用于启用服务端支持。客户端应在IDENTIFY响应之后立即获得一个额外的deflate压缩OK响应。客户端不能同时启用snappydeflate

  • deflate_levelnsqd v0.2.23+): 配置连接的压缩级别。--max-deflate-levelnsqd标志)配置最大允许值。有效范围: 1 <= deflate_level <= configured_max。较高的值表示更好的压缩,但是nsqd的CPU使用率更高。

  • sample_ratensqd v0.2.25+):传递 连接所接收到的所有消息的百分比。有效范围:0 <= sample_rate <= 990禁用采样),默认为 0

  • user_agentnsqd v0.2.25+): 按照HTTP精神,客户端代理的字符串标识符。默认为:<client_library_name>/<version>

  • msg_timeoutnsqd v0.2.28+): 以毫秒为单位配置的服务器端消息超时,用于将消息传递给客户端。

成功的响应:

OK

注意: 如果客户端发送了feature_negotiation(并且服务端支持它),响应将是如上所述的JSON负荷。

错误响应:

E_INVALID
E_BAD_BODY

SUB

订阅一个主题/通道:

SUB <topic_name> <channel_name>\n

<topic_name> - a valid string (optionally having #ephemeral suffix)
<channel_name> - a valid string (optionally having #ephemeral suffix)

成功响应:

OK

错误响应:

E_INVALID
E_BAD_TOPIC
E_BAD_CHANNEL

PUB

发布一个消息到一个主题:

PUB <topic_name>\n
[ 4-byte size in bytes ][ N-byte binary data ]

<topic_name> - a valid string (optionally having #ephemeral suffix)

成功响应:

OK

错误响应:

E_INVALID
E_BAD_TOPIC
E_BAD_MESSAGE
E_PUB_FAILED

MPUB

发布 多个消息到一个主题(原子的):

注意: nsqd v0.2.16+可用

MPUB <topic_name>\n
[ 4-byte body size ]
[ 4-byte num messages ]
[ 4-byte message #1 size ][ N-byte binary data ]
      ... (repeated <num_messages> times)

<topic_name> - a valid string (optionally having #ephemeral suffix)

成功响应:

OK

错误响应:

E_INVALID
E_BAD_TOPIC
E_BAD_BODY
E_BAD_MESSAGE
E_MPUB_FAILED

DPUB

发布一个延迟消息到一个主题:

注意: nsqd v0.3.6+可用

DPUB <topic_name> <defer_time>\n
[ 4-byte size in bytes ][ N-byte binary data ]

<topic_name> - a valid string (optionally having #ephemeral suffix)
<defer_time> - a string representation of integer D which defines the time for how long to defer where 0 <= D < max-requeue-timeout

成功响应:

OK

错误响应:

E_INVALID
E_BAD_TOPIC
E_BAD_MESSAGE
E_DPUB_FAILED

RDY

更新RDY状态(指示你准备获取N个消息)

注意:nsqd v0.2.20+使用 --max-rdy-count 来界定此值。

RDY <count>\n

<count> - a string representation of integer N where 0 < N <= configured_max

注意:没有成功的响应

错误响应:

E_INVALID

FIN

完成一个消息(表明成功处理)

FIN <message_id>\n

<message_id> - message id as 16-byte hex string

注意:没有成功的响应

错误响应:

E_INVALID
E_FIN_FAILED

REQ

重新排队消息(表明处理失败) 重新排队的消息放置在队列的尾部,等价于刚刚发布该消息,但是出于各种实现的特定原因,不应明确依赖此行为,并且将来可能会发生变化。

同样,传输中且超时的消息在行为上与明确的REQ相同。

REQ <message_id> <timeout>\n

<message_id> - message id as 16-byte hex string
<timeout> - a string representation of integer N where N <= configured max timeout
    0 is a special case that will not defer re-queueing

注意:没有成功的响应

错误响应:

E_INVALID
E_REQ_FAILED

TOUCH

对传递中的消息,重置其超时。

注意:nsqd v0.2.17+可用

TOUCH <message_id>\n

<message_id> - the hex id of the message

注意:没有成功的响应

错误响应:

E_INVALID
E_TOUCH_FAILED

CLS

干净地关闭连接(不再发送任何消息)

CLS\n

成功响应:

CLOSE_WAIT

错误响应:

E_INVALID

NOP

No-op

NOP\n

注意: 没有任何响应.

AUTH

注意: nsqd v0.2.29+可用。

如果IDENTIFY响应表明 auth_required=true ,客户端必须在任何 SUB, PUBMPUB之前发送AUTH。如果auth_required不存在(或为false),则客户端不得授权。

nsqd接收到一个AUTH命令,它将通过查询参数的形式:连接的远程地址、TLS状态、以及所提供的AUTH密码,执行一个带有客户端元数据的HTTP请求,将责任委托给已配置的--auth-http-address 。有关更多详细信息,请参见 AUTH

AUTH\n
[ 4-byte size in bytes ][ N-byte Auth Secret ]

成功响应:

JSON负荷描述了授权客户端身份,可选URL和授权计数

{"identity":"...", "identity_url":"...", "permission_count":1}

错误响应:

E_AUTH_FAILED - An error occurred contacting an auth server
E_UNAUTHORIZED - No permissions found

数据格式

数据异步传输到客户端并进行分帧以支持各种响应主体,即:

[x][x][x][x][x][x][x][x][x][x][x][x]...
|  (int32) ||  (int32) || (binary)
|  4-byte  ||  4-byte  || N-byte
------------------------------------...
    size     frame type     data

客户端应期望以下帧类型之一:

FrameTypeResponse int32 = 0
FrameTypeError    int32 = 1
FrameTypeMessage  int32 = 2

最后,消息格式为:

[x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x][x]...
|       (int64)        ||    ||      (hex string encoded in ASCII)           || (binary)
|       8-byte         ||    ||                 16-byte                      || N-byte
------------------------------------------------------------------------------------------...
  nanosecond timestamp    ^^                   message ID                       message body
                       (uint16)
                        2-byte
                       attempts

Previous

NSQ之组件

Next

NSQ之部署