async-nats Rust Client

repository·main·Indexed 23 days ago

https://github.com/nats-io/nats.rs

An asynchronous, Tokio-based Rust client for the NATS messaging system. It provides a performance-oriented mapping of the nats-server wire protocol, including support for Core NATS (publish, subscribe, request/reply), JetStream, Key Value (KV) Store, Object Store, and the Service API. It is the recommended replacement for the deprecated synchronous nats crate.

Tokens
27.8K
Snippets
50
Records
168
Agent score
80%

What's inside async-nats

  1. Choose between async-nats and nats clients

    main

    The NATS Rust ecosystem provides two client libraries, but you should almost always use async-nats.

    • async-nats (Recommended): An asynchronous, Tokio-based client that provides direct API access to Core NATS, JetStream, Key Value Store, Object Store, and the Service API. It is designed to be lightweight, performance-oriented, and maintains API parity with official NATS clients in other languages.
    • nats (Deprecated): A legacy client that only receives critical security fixes. Do not use this for new projects.

    Use async-nats for all modern NATS development in Rust.

  2. Understand the relationship between async-nats and Orbit

    main

    NATS client functionality is split into two distinct layers to balance stability with rapid innovation:

    1. Core Client (async-nats)

    This is the foundation. It provides a thin, unopinionated mapping of the nats-server wire protocol.

    • Scope: Connection, publishing, subscribing, request/reply, JetStream, KV, Object Store, Service API, TLS, and reconnection.
    • Philosophy: High parity with other official NATS clients (Go, Python, etc.), stable and conservative versioning, and performance-focused.

    2. Orbit (orbit.rs)

    Orbit is a separate set of crates built on top of async-nats. It provides higher-level, opinionated abstractions.

    • Scope: KV codecs, distributed counters, NATS contexts, and experimental patterns like partitioned groups.
    • Philosophy: Rust-idiomatic abstractions that do not need to match other languages, faster API iteration, and per-crate versioning.

    Rule of thumb: If you need a direct mapping of a NATS server feature, use async-nats. If you want a high-level pattern, helper, or Rust-specific abstraction, look for an Orbit crate.

  3. Migrate from `nats` to `async-nats`

    main

    The nats crate is deprecated. It will only receive critical security fixes and no new features or bug fixes.

    For all new development, use the async-nats crate instead. If you need to use the async client in a synchronous context, refer to the async-nats examples in the repository.

  4. Understand JetStream DeliverPolicy

    main

    The DeliverPolicy determines which message the consumer starts with when it is first created or reset.

    • All: (Default) Delivers the oldest messages still in the stream.
    • Last: Starts with the last sequence received.
    • New: Only delivers messages received by the server after the consumer is created.
    • ByStartSequence { start_sequence: u64 }: Starts at a specific stream sequence.
    • ByStartTime { start_time: DateTime }: Starts with the first message having a timestamp $\ge$ the provided time.
    • LastPerSubject: Starts with the last message for all subjects.
  5. Inspect JetStream account usage and limits

    main

    The Account struct provides a comprehensive view of a JetStream account's resource consumption, active entities, and imposed constraints. You can use this data to monitor how much memory or storage an account is consuming and how close it is to its configured limits.

    Key components of an Account include:

    • Resource Usage: Tracks memory, storage, reserved_memory, and reserved_storage.
    • Active Entities: Counts of active streams and consumers.
    • Limits: A Limits object defining maximum allowable resources.
    • API Requests: Statistics on total and errors requests, and (if using server 2.11+) inflight calls.
    • Tiers: A map of Tier objects, allowing for granular resource tracking within specific tiers of an account.
  6. Configure Consumer behavior with ConsumerConfig

    main

    The ConsumerConfig struct defines how a JetStream consumer receives and acknowledges messages. Key configuration decisions include:

    • Push vs. Pull:
      • Push-based: Set deliver_subject to Some(String). The consumer receives messages automatically. Supports AckPolicy::None and AckPolicy::All for high throughput.
      • Pull-based: Set deliver_subject to None. Requires explicit acknowledgment of each message (AckPolicy::Explicit). Best for work-queue patterns where a single process should handle a message.
    • Durability:
      • Durable: Set durable_name to Some(String). The server remembers progress, allowing recovery after a crash.
      • Ephemeral: Set durable_name to None. The server does not track progress; useful for high-churn workloads.
    • Delivery Policy: Use deliver_policy to determine where in the stream the consumer starts (e.g., All, Last, New, or specific sequences/times).
  7. Determine if a connection needs flushing

    main

    The ShouldFlush enum indicates whether the connection's internal write buffers require attention. This is useful for optimizing when to trigger a flush operation:

    • Yes: Write buffers are empty, but the connection hasn't been flushed yet.
    • May: The connection hasn't been flushed yet, but write buffers are not empty.
    • No: Flushing would be a no-op.
  8. Configure Stream properties with StreamConfig

    main

    Use StreamConfig to define the properties and limits of a JetStream stream.

    Key fields include:

    • name: Unique identifier for the stream.
    • subjects: A list of NATS subjects (supports wildcards) that populate the stream.
    • max_bytes / max_msgs: Limits on stream size. When reached, discard policy determines if Old messages are deleted or New messages are rejected.
    • retention: Determines how messages are removed (Limits, Interest, or WorkQueue).
    • storage: Defines the backend (File or Memory).
    • num_replicas: Number of replicas in a cluster (max 5).
    • duplicate_window: The time window used to detect duplicate messages.
  9. Configure the datetime backend in async-nats

    main

    By default, JetStream and Service datetime fields in async-nats use time::OffsetDateTime.

    You can switch the async_nats::datetime::DateTime type to use chrono by enabling the chrono feature flag in your Cargo.toml.

    Important: Because Cargo unifies features across the entire dependency graph, enabling chrono anywhere in your project will select it for all consumers of async-nats in that build. This can lead to unexpected type mismatches if you expect time::OffsetDateTime but a dependency has enabled chrono.

  10. Consume messages with an Ordered Push consumer

    main

    An Ordered consumer is a specialized push consumer designed to provide strict ordering and automatic recovery. It uses OrderedConfig and provides an Ordered stream.

    Key features include:

    • Automatic Reconnection: If the connection is lost, it attempts to recreate the consumer and subscription.
    • Sequence Validation: It monitors both stream and consumer sequences to ensure no messages are missed or duplicated due to connection shifts.
    • Heartbeat Monitoring: It uses idle heartbeats to detect if the consumer has been deleted or the server has restarted, triggering a recreation from the last delivered sequence.
  11. Customize reconnection logic with `ReconnectToServer`

    main

    You can control exactly which server the client attempts to reconnect to and how long it should wait by providing a reconnect_to_server_callback in your connection options. This callback receives the current server pool and the last known ServerInfo.

    To use it, return a ReconnectToServer instance:

    • addr: The ServerAddr to connect to. This must be a member of the pool provided to the callback, otherwise the library falls back to its default selection logic.
    • delay: An Option<Duration>. Use None to use the default exponential backoff, or Some(Duration::ZERO) to reconnect immediately.
  12. Create and manage Service Endpoints

    main

    Endpoints are the specific subjects within a service that handle requests. You can create an endpoint directly from a Service instance using .endpoint(subject).

    Endpoints are part of a queue group (defaulting to q if not specified at the service level) to allow for load balancing across multiple service instances. When a request is received, you use the Request object to send a response via .respond() or .respond_with_headers().