你可以使用 NATS 与其他应用程序交换信息,并向其他应用程序发起请求。你也可以使用NATS将你的应用程序变成一个分布式对等(peer-to-peer)应用程序。
从高层次来看,你的应用程序可以使用NATS:
使用NATS意味着,作为应用程序开发者,你无需担心以下问题:
NATS 客户端应用会通过以下方式使用 NATS 客户端库:
在初始化时,它会首先(如果需要则通过安全方式)连接到 NATS 服务基础设施(即某个 NATS 服务器)。
成功连接后,应用将:
最后,当应用终止时,它应当与 NATS 服务基础设施断开连接。
请参阅以下部分以了解更多关于这些活动的信息。
任何应用需要做的第一件事就是连接到 NATS。根据 NATS 服务基础设施使用的配置方式,连接可能需要是安全的,因此应用也需要可以在连接时使用安全凭证。一个应用可以按需创建多个 NATS 连接(每个连接完全独立,例如,它可以以两个不同用户的身份连接两次),尽管通常大多数应用只建立单个 NATS 连接。
一旦获得有效连接,你就可以在应用中使用该连接来使用所有 Core NATS 功能,例如订阅主题、发布消息、发出请求(以及获取 JetStream 上下文)。
最后,应用需要安全地从 NATS 断开连接。
建议应用使用 连接事件监听器,以便在连接、重连或断开连接发生时得到警报并进行日志记录。请注意,如果断开了与 NATS 服务器进程的连接,客户端库将自动尝试重新连接到集群中的其他 NATS 服务器。你也可以随时检查当前连接状态。
推荐的断开连接方式是使用 Drain(),它将等待任何正在进行的处理结束,并妥善清理所有资源,但如果你需要立即关闭连接,可以使用连接对象上的 close() 方法。
消息里面包含了应用程序之间交换的数据。一条消息有一个主题、一个数据载荷(以字节数组的形式),也可能有一个*回复到(reply-to)和头(header)*字段。
你通过 订阅/发出请求(request),在你设定的回调函数中、或通过返回值获取到消息。发布(和请求)操作 通常只需要一个主题和一个字节数组数据负载即可创建消息,但你也可以自己亲自创建消息(如果你想设置一些头字段)。
有些消息可以被 ‘确认’ (例如从 JetStream 拉取型消费者接收到的消息),并且有多种形式的确认(包括否定确认,以及表明你的应用已正确接收到消息但需要更多时间来处理它的确认)。
一旦你的应用成功连接到 NATS 服务器基础设施,你就可以开始使用返回的连接对象与 NATS 交互。
你可以直接使用连接对象 发布 一些以主题为地址的数据(或者发布一个预先创建的、带有 headers 字段的消息)。
由于存在缓存,如果你的应用对延迟高度敏感,你可能希望在发布后进行刷新(flush)。
许多客户端库使用 NATS 协议内置的 PING/PONG 交互来确保刷新操作将所有缓冲的消息推送到服务器。当应用程序调用刷新时,大多数库会在传出消息队列上放置一个 PING,并等待服务器响应 PONG,然后才确认刷新成功。
即使客户端可能使用 PING/PONG 进行刷新,以这种方式发送的 ping 不计入最大传出 ping 数。
订阅的过程是让客户端库告知 NATS 某个应用对特定主题感兴趣。当应用程序不再需要某个订阅时,它应通过取消订阅(unsubscribe)来告知服务器停止发送相关消息。
使用 NATS 接收消息的方式取决于具体的库,有些语言(如 Go 或 Java)提供同步和异步 API,而其他语言可能只支持一种订阅类型。通常,应用可以异步地或同步地接收消息。
你始终可以使用通配符一次性订阅多个主题。
客户端会为每个匹配的订阅收到一条消息。因此,如果一个连接拥有多个使用相同或重叠主题(例如 foo 和 >)的订阅,那同一条消息将会被多次发送给该客户端。
你也可以作为分布式队列组的一部分进行订阅。所有具有相同队列组名称的订阅者共同构成了分布式队列。NATS 服务器会自动将在匹配主题上发布的消息分发给队列组的各个成员。
在一个给定的主题上,订阅应用程序可以创建多个队列组,每个队列组都是一个独立的队列,并在其队列组成员之间分发自己的消息副本。
在对主题进行 Core NATS 订阅时需要记住的一点是,你的应用程序必须能够跟上在该(些)主题上发布的消息流,否则它将会变成一个慢消费者。
当你不再希望接收特定主题的消息时,必须调用取消订阅(unsubscribe),或者你也可以在收到特定数量的消息后自动取消订阅。
你也可以使用 NATS 轻松透明地调用服务,而无需了解该服务的位置或服务器数量。连接的 request 调用会在指定主题上发布一条包含回复主题(reply-to)收件箱主题的消息,然后等待该收件箱接收到回复消息。
处理这些请求的服务器应用程序只需订阅发布请求的主题,处理接收到的请求消息,并在请求消息的 Reply-to 属性所包含的主题上回复该消息。
通常,没有理由不希望将你的服务设计为分布式的(即可扩展且容错)。这意味着,处理请求的应用程序应该使用相同的队列组名称订阅请求主题,除非有特定原因不这样做。一个主题上可以存在多个队列组(例如,你可以有一个队列组在服务实例之间分发请求的处理,另一个队列组来分发对服务所做请求的日志或监控)。
部分应用程序可以利用 JetStream 所启用的额外功能(流、键值存储、对象存储)。正如你使用 Core NATS 连接对象来调用 Core NATS 操作一样,你使用一个 JetStream 上下文 来调用 JetStream 操作。你可以指定一些设置,例如从该上下文执行的所有操作的超时值。JetStream 上下文是轻量级的,因此虽然在线程间共享 JetStream 上下文是安全的,但为了获得最佳性能,你可以给每个线程都使用一个上下文。
你可以将流用于两大主要用例:
在你可以使用流来重放或消费在某个主题上发布的消息之前,必须先对其进行定义。你可以在流定义的参数中设定:
流可以(并且通常)由管理员提前定义(例如使用 NATS CLI 工具)。应用程序也可以以编程方式管理流(和消费者)。
任何在流所监控的主题上发布的消息都会被存储到该流中。如果你的应用程序使用 Core NATS publish 调用(通过连接对象)在某个流的主题上发布消息,该消息也会被存储到流中。使用 Core NATS 的发布者不知道也不关心该主题是否有对应的流。 然而,如果你知道该主题将会有对应的一个流定义,你可以通过使用 JetStream 上下文的 publish 调用(而不是连接对象的 publish 调用)进行发布,你将获得更高质量的服务。这是因为 JetStream 发布功能只有在消息被成功接收并存储到流中后,才会从 NATS 服务器收到确认(或错误)(而 Core NATS 发布不会得到 NATS 服务器的确认)。这种差异也是为什么 JetStream 发布操作同时提供同步和异步版本的原因。
流消费者 提供给应用程序从流中获取消息。可以类比数据库的概念,消费者可以被看作是一种(针对流的)“视图”:
消费者在 NATS 服务器上还存有少量状态,用于存储一些消息序列号“游标”。每个流可以根据需要添加任意个消费者。
客户端应用程序要么创建*临时(ephemeral)消费者,要么定义/查找持久(durable)*消费者。应用程序要么订阅‘推送型(push)’消费者(这些消费者定义了一个传递主题,并可选地为该传递主题指定一个队列组名称),要么按需(包括可选的预取)从‘拉取型(pull)’消费者获取消息(这些消费者没有定义传递主题或队列组名称,因为它们不需要这些就能提供相同的功能)。
临时消费者,顾名思义,并非设计为持久存在,当创建它们的应用程序实例关闭时,会被 NATS 服务器自动清理。临时消费者由单个应用程序实例按需创建,并且仅由创建它们的应用程序实例使用。
应用程序通常使用临时的*有序推送型消费者(ordered push consumers)*来在需要时获取存储在流中的消息的私有副本。
持久消费者,顾名思义,旨在“始终在线”,并由客户端应用程序的多个实例使用(共享),或者由那些多次停止和重启且需要在应用程序的不同运行周期之间保持状态的应用程序使用。
持久消费者可以使用 NATS CLI 工具进行管理,也可以由应用程序自身以编程方式管理。只需在创建时指定一个持久名称(durable name),消费者就会被创建为持久消费者。
应用程序通常使用*持久拉取型消费者(durable pull consumers)*来水平分布和扩展流中消息的处理(或消费)。
某些类型的消费者(例如拉取消费者)要求从消费者接收消息的应用程序显式地确认(acknowledge)接收和处理了这些消息。应用程序可以对从消费者接收到的消息调用以下确认函数之一:
ack():正面确认(ACK)消息的接收和处理。term():表明该消息无法且永远无法被处理,不应再次发送。当请求无效时使用 term。nack():否定确认(NACK)消息的处理,表明应再次发送该消息。当请求有效但你无法处理时使用 nack。如果这种无法处理的情况是暂时的,你还应暂时关闭订阅,直到能够再次处理。inProgress():表明消息的处理仍在进行中,需要更多时间(在此之后,该消息才会被考虑再次发送)。除了时间解耦和队列功能,与 Core NATS 相比,JetStream 还能实现更高质量的服务。在主题上定义流并使用消费者可以将服务质量提高到至少一次(at least once),这意味着保证你能收到消息(即使你的应用程序在发布时已关闭),但在某些极端故障场景下,可能会导致消息重复,原因可能是消息的重复发布,或者因确认丢失或在处理之后、确认之前发生崩溃而导致消息的重复处理。你可以启用和使用消息去重(message de-duplication)以及双重确认(double-acking)来防止这些故障场景,从而获得精确一次(exactly once)的服务质量。
键值存储功能构建在 JetStream 之上,但提供了不同的接口形式,即键(keys)和值(values),而不是主题名称和消息。你可以使用一个存储桶(bucket)来放置(包括比较并设置,compare and set)、获取和删除与某个键(一个字符串,类似于主题)关联的值(一个字节数组,类似于消息负载)。它还允许你“监视(watch)”存储桶中实时发生的变化。最后,它还允许你维护一个键随时间变化的值历史记录,以及获取值的特定修订版本。
注意: 目前对象存储是技术预览功能
对象存储与键值存储类似,但旨在用于值可以是任意大尺寸的情况,这与键值存储中值受限于 NATS 消息的最大尺寸不同。