Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Watch RethinkConn 2025 - a free, virtual event for the NATS community! See what you missed!
NATS is a simple, secure and high performance open source data layer for cloud native applications, IoT messaging, and microservices architectures.
We feel that it should be the backbone of your communication between services. It doesn't matter what language, protocol, or platform you are using; NATS is the best way to connect your services.
Publish and subscribe to messages at millions of messages per second. At most once delivery.
Supports fan-in/out delivery patterns
Request/reply
Every major language is supported
Persistence via JetStream
at least once delivery or exactly once delivery
work queues
stream processing
data replication
data retention
data deduplication
Higher order data structures
Key/Value with watchers, versioning, and TTL
Object storage with versioning
Security
TLS
JWT-based zero trust security
Clustering
High availability
Fault tolerance
Auto-discovery
Protocols supported
TCP
MQTT
WebSockets
All of this in a single binary that is easy to deploy and manage. No external dependencies, just drop it in and add a configuration file to point to other NATS servers and you are ready to go. In fact, you can even embed NATS in your application (for Go users)!
In general we recommend trying to solve your problems first using Core NATS.
If you need to share state between services, take a look at the KV or Object Store in JetStream.
When you need lower level access to persistence streams, move on to using JetStream directly for more advanced messaging patterns.
Learn about deployment strategies
Secure your deployments with zero trust security
NATS is Open Source as is this documentation. Please let us know if you have updates and/or suggestions for these docs. You can also create a Pull Request using the Edit on GitHub
link on each page.
Feel free to chat with us on Slack slack.nats.io.
Thank you from the entire NATS Team of Maintainers for your interest in NATS!
This guide is tailored for existing NATS users upgrading from NATS version 2.9.x. This will read as a summary with links to specific documentation pages to learn more about the feature or improvement.
Although all existing client versions will work, new client versions will expose additional options used to leverage new features. The minimum client versions that have full 2.10.0 support include:
CLI - v0.1.0
nats.go - v1.30.0
nats.rs - v0.32.0
nats.deno - v1.17.0
nats.js - v2.17.0
nats.ws - v1.18.0
nats.java - v2.17.0
nats.net - v1.1.0
nats.net.v2 - Coming soon!
nats.py - Coming soon!
nats.c - Coming soon!
For critical infrastructure like NATS, zero downtime upgrades are table stakes. Although the best practice for all infrastructure like this is for users to thoroughly test a new release against your specific workloads, inevitably there are cases where an upgrade occurs in production followed by a decision to downgrade. This is never recommended and can cause more harm than good for most infrastructure and data systems.
Below are a few important considerations if downgrading is required.
2.10.0 brings on-disk storage changes which bring significant performance improvements. These are not compatible with previous versions of the NATS Server. If an upgrade is performed to a server with existing stream data on disk, followed by a downgrade, the older version server will not understand the stream data in the new format.
However, being mindful of the possibility of the need to downgrade, a special version of the 2.9.x series was released with awareness of key changes in the new storage format, allowing it to startup properly.
The takeaway is that if a downgrade is the only resort, it must be to 2.9.22 or later to ensure storage format changes are handled appropriately.
There are new stream and consumer configuration options that could be problematic if a downgrade occurs since previous versions of the server have no awareness of them. Examples include:
Multi-filter consumers - Downgrading would result in no filter being applied since the new field is configured as a list rather than a single string.
Subject-transform on streams - Downgrading would result in the subject transform not being applied since the server has no awareness of it.
Compression on streams - Downgrading when compression is enabled on streams will cause those streams to become unloadable since the older server versions will not understand the compression being used.
A server reload can now be performed by sending a message on $SYS.REQ.SERVER.<server-id>.RELOAD
by a client authenticated in the system account.
A new sync_interval
server config option has been added to change the default sync interval of stream data when written to disk, including allowing all writes to be flushed immediately. This option is only relevant if you need to modify durability guarantees.
Subject mappings can now be cluster-scoped and weighted, enabling the ability to have different mappings or weights on a per cluster basis.
The requirement to use all wildcard tokens in subject mapping or transforms has been relaxed. This can be applied to config or account-based subject mapping, stream subject transforms, and stream republishing, but not on subject mappings that are associated with stream and service import/export between accounts.
A subject_transform
field has been added enabling per-stream subject transforms. This applies to standard streams, mirrors, and sourced streams.
A metadata
field has been added to stream configuration enabling arbitrary user-defined key-value data. This is to supplant or augment the description
field.
A first_seq
field has been added to stream configuration enabling explicitly setting the initial sequence on stream creation.
A compression
field has been added to stream configuration enabling on-disk compression for file-based streams.
The ability to edit the republish
config option on a stream after stream creation was added.
A Nats-Time-Stamp
header is now included in republished messages containing the original message's timestamp.
A ts
field has been added to stream info responses indicating the server time of the snapshot. This was added to allow for local time calculations relying on the local clock.
An array of subject-transforms (subject filter + subject transform destination) can be added to a mirror or source configuration (can not use the single subject filter/subject transform destination fields at the same time as the array).
A stream configured with sources
can source from the same stream multiple times when distinct filter+transform options are used, allowing for some messages of a stream to be sourced more than once.
A filter_subjects
field has been added which enables applying server-side filtering against multiple disjoint subjects, rather than only one.
A metadata
field has been added to consumer configuration enabling arbitrary user-defined key-value data. This is to supplant or augment the description
field.
A ts
field has been added to consumer info responses indicating the server time of the snapshot. This was added to allow for local time calculations without relying on the local clock.
A metadata
field has been added to key-value configuration enabling arbitrary user-defined key-value data. This is to supplant or augment the description
field.
A bucket configured as a mirror or sourcing from other buckets
A metadata
field has been added to object store configuration enabling arbitrary user-defined key-value data. This is to supplant or augment the description
field.
A pluggable server extension, referred to as auth callout, has been added. This provides a mechanism for delegating authentication checks against a bring-your-own (BYO) provider and, optionally, dynamically declaring permissions for the authenticated user.
A slow_consumer_stats
field has been added to the /varz
HTTP endpoint providing a count of slow consumers for clients, routes, gateways, and leafnodes.
A raft=1
query parameter has been added to the /jsz
HTTP endpoint which adds stream_raft_group
and consumer_raft_groups
fields to the response.
A num_subscriptions
field has been added to the $SYS.REQ.SERVER.PING.STATZ
NATS endpoint responses.
A system account responder for $SYS.REQ.SERVER.PING.IDZ
has been added which returns info for the server that the client is connected to.
A system account responder for $SYS.REQ.SERVER.PING.PROFILEZ
has been added and works even if a profiling port is not enabled in the server configuration.
A user account responder for $SYS.REQ.USER.INFO
has been added which allows a connected user to query for the account they are in and permissions they have.
Support for QoS2 has been added. Check out the new MQTT implementation details overview.
When defining routes between servers, a handful of optimizations have been introduced including a pool of TCP connections between servers, optional pinning of accounts to connections, and optional compression of traffic. There is quite a bit to dig into, so check out the v2 routes page for details.
A handshake_first
config option has been added enabling TLS-first handshakes for leafnode connections.
The NATS_STARTUP_DELAY
environment variable has been added to allow changing the default startup for the server of 10 seconds
The nats-server --signal
command now supports a glob expression on the <pid>
argument which would match a subset of all nats-server
instances running on the host.
Prior to 2.10, setting republish
configuration on mirrors would result in an error. On sourcing streams, only messages that were actively between stored matching configured subjects
would be republished. The behavior has been relaxed to allow republishing on mirrors and includes all messages on sourcing streams.
A new header has been added on a fetch response that indicates to clients the fetch has been fulfilled without requiring clients to rely on heartbeats. It avoids some conditions in which the client would issue fetch requests that could go over limits or have more fetch requests pending than required.
Previously, a leafnode configured with two or more remotes binding to the same hub account would be rejected. This restriction has been relaxed since each remote could be binding to a different local account.
Previously a dot .
in an MQTT topic was not supported, however now it is! Check out the topic-subject conversion table for details.
This guide is tailored for existing NATS users upgrading from NATS version v2.10.x. This will read as a summary with links to specific documentation pages to learn more about the feature or improvement.
Distributed message tracing: Users can now trace messages as they move through the system by setting a Nats-Trace-Dest
header to an inbox subject. Servers on the message path will return events to the provided subject that report each time a message enters or leaves a server, by which connection type, when subject mappings occur, or when messages traverse an account import/export boundary. Additionally, the Nats-Trace-Only
header (if set to true) will allow tracing events to propagate on a specific subject without delivering them to subscribers of that subject.
JetStream per-message TTLs: It is now possible to age out individual messages using a per-message TTL. The Nats-TTL
header, in either string or integer format (in seconds) allows for individual message expiration independent of stream limits. This can be combined with other limits in place on the stream. More information is available in ADR-43.
Subject delete markers on MaxAge: The SubjectDeleteMarkerTTL
stream configuration option now allows for the placement of delete marker messages in the stream when the configured MaxAge
limit causes the last message for a given subject to be deleted. The delete markers include a Nats-Marker-Reason
header explaining which limit was responsible for the deletion.
Stream ingest rate limiting: New options max_buffered_size
and max_buffered_msgs
in the jetstream
configuration block enable rate limiting on Core NATS publishing into JetStream streams, protecting the system from overload.
Pull consumer priority groups: Pull consumers now support priority groups with pinning and overflow, enabling flexible failover and priority management when multiple clients are pulling from the same consumer. Configurable policies based on the number of pending messages on the consumer, or the number of pending acks, can control when messages overflow from one client to another, enabling new design patterns or regional awareness.
Consumer pausing: Message delivery to consumers can be temporarily suspended using the new pause API endpoint (or the PauseUntil
configuration option when creating), ideal for maintenance or migrations. Message delivery automatically resumes once the configured deadline has passed. Consumer clients continue to receive heartbeat messages as usual to ensure that they do not surface errors during the pause.
Replication traffic in asset accounts: Raft replication traffic can optionally be moved into the same account in which replicated assets live on a per-account basis, rather than being sent and received in the system account. When combined with multiple route connections, this can help to reduce latencies and avoid head-of-line blocking issues that may occur in heavily-loaded multi-tenant or multi-account deployments.
TLS first on leafnode connections: A new handshake_first
in the leafnode tls
block allows setting up leafnode connections that perform TLS negotiation first, before any other protocol handshakes take place.
Configuration state digest: A new -t
command line flag on the server binary can generate a hash of the configuration file. The config_digest
item in varz
displays the hash of the currently running configuration file, making it possible to check whether a configuration file has changed on disk compared to the currently running configuration.
TPM encryption on Windows: When running on Windows, the filestore can now store encryption keys in the TPM, useful in environments where physical access may be a concern.
SparkplugB: The built-in MQTT support is now compliant with SparkplugB Aware, with support for NBIRTH
and NDEATH
messages.
Replicated delete proposals: Message removals in clustered interest-based or workqueue streams are now propagated via Raft to guarantee consistent removal order across replicas, reducing a number of possible ways that a cluster failure can result in de-synced streams.
Metalayer, stream and consumer consistency: A new leader now only responds to read/write requests after synchronizing with its Raft log, preventing desynchronization between KV key updates and the stream during leader changes.
Replicated consumer reliability: Replicated consumers now consistently redeliver unacknowledged messages after a leader change.
Consumer starting sequence: The consumer starting sequence is now always respected, except for internal hidden consumers for sources/mirrors.
The NATS Server can now return a 429 error with type JSStreamTooManyRequests
when too many messages have been queued up for a stream. It should not generally be possible to hit this limit while using JetStream publishes and waiting for PubAcks, but may trigger if trying to publish into JetStream using Core NATS publishes without waiting for PubAcks, which is not advised.
The new max_buffered_size
and max_buffered_msgs
options control how many messages can be queued for each stream before the rate limit is hit, therefore if needed, you can increase these limits on your deployments. The default values for max_buffered_size
and max_buffered_msgs
are 128MB and 10,000 respectively, whereas in v2.10 these were unlimited.
You can detect in the server logs whether running into a queue limit with the following warning:
If your application starts to log the above warnings then you can first try to increase the limits to higher values while investigating the fast publishers, for example:
Since stream deletes are now replicated through group proposals in a replicated stream, there may be a slight increase in replication traffic on this version.
The js-server-only
healthcheck no longer checks for the health of the metaleader on v2.11.0. Since this healthcheck was designed to detect the server readiness (or in k8s for the readiness probe) checking the metaleader would sometimes cause a NATS server to be considered unhealthy when restarting the servers. In v2.11, this should no longer be an issue. If the previous behavior from v2.10 is preferred, there is a new healthcheck option js-meta-only
which can be used to check whether the meta group is healthy.
Earlier versions of the NATS Server would return an exit code 1 when gracefully shut down, i.e. after SIGTERM. From v2.11, an exit code of 0 (zero) will now be returned instead.
Configurations that have server, cluster, and gateway names with spaces are now considered invalid, as this can cause problems at the protocol level. A server running NATS v2.11 will fail to start with spaces configured in these names. Please ensure that spaces are not used in server, cluster or gateway names.
When downgrading from v2.11 to v2.10, the stream state files on disk will be rebuilt due to a change in the format of these files in v2.11. This requires re-scanning all stream message blocks, which may use higher CPU than usual and will likely take longer for the restarted node to report healthy. This will only happen on the first restart after downgrading and will not result in data loss.
NATS 2.2 is the largest feature release since version 2.0. The 2.2 release provides highly scalable, highly performant, secure and easy-to-use next generation streaming in the form of JetStream, allows remote access via websockets, has simplified NATS account management, native MQTT support, and further enables NATS toward our goal of securely democratizing streams and services for the hyperconnected world we live in.
JetStream is the next generation streaming platform for NATS, highly resilient, highly available, and easy to use. We’ve spent a long time listening to our community, learning from our experiences, looking at the needs of today, and thinking deeply about the needs of tomorrow. We built JetStream to address these needs.
JetStream:
is easy to deploy and manage, built into the NATS server
simplifies and accelerates development
supports wildcard subjects
supports at least once delivery and exactly once within a window
is horizontally scalable at runtime with no interruptions
persists data via streams and delivers or replays via consumers
supports multiple patterns to consume data on the same stream
supports push and pull modes when consuming messages
is account aware
allows for detailed granularity of security, by stream, by consumer, by function
Get started with JetStream.
Account management just became much easier. This version of NATS has a built-in account management system, eliminating the need to set up an account manager when not using the memory account resolver. With automated default system account generation, and the ability to preload accounts, simply enable a set of servers in your deployment to be account resolvers or account resolver caches, and they will handle public account information provided to the NATS system through the NATS nsc tooling. Have enterprise-scale account management up and running in minutes.
By specifying a CIDR block restriction for a user, policy can be applied to limit connections from clients within a certain range or set of IP addresses. Use this as another layer of security atop user credentials to better secure your distributed system. Ensure your applications can only connect from within a specific cloud, enterprise, geographic location, virtual or physical network.
Scoped to the user, you can now specify a specific block of time during the day when applications can connect. For example, permit certain users or applications to access the system during specified business hours, or protect business operations during the busiest parts of the day from batch driven back-office applications that could adversely impact the system when run at the wrong time.
Now you can specify default user permissions within an account. This significantly reduces efforts around policy, reduces chances for error in permissioning, and simplifies the provisioning of user credentials.
Connect mobile and web applications to any NATS server using WebSockets. Built to more easily traverse firewalls and load balancers, NATS WebSocket support provides even more flexibility to NATS deployments and makes it easier to communicate to the edge and endpoints. This is currently supported in NATS server leaf nodes, nats.ts, nats.deno, and the nats.js clients.
With the Adaptive Edge architecture and the ease with which NATS can extend a cloud deployment to the edge, it makes perfect sense to leverage existing investments in IoT deployments. It’s expensive to update devices and large edge deployments. Our goal is to enable the hyperconnected world, so we added first-class support for MQTT 3.1.1 directly into the NATS Server.
Seamlessly integrate existing IoT deployments using MQTT 3.1.1 with a cloud-native NATS deployment. Add a leaf node that is MQTT enabled and instantly send and receive messages to your MQTT applications and devices from a NATS deployment whether it be edge, single-cloud, multi-cloud, on-premise, or any combination thereof.
We’ve added a variety of features to allow you to build a more resilient, secure, and simply better system at scale.
We’ve added the ability to optionally use headers, following the HTTP semantics familiar to developers. Headers naturally apply overhead, which was why we resisted adding them for so long. By creating new internal protocol messages transparent to developers, we maintain the extremely fast processing of simple NATS messages that we have always had while supporting headers for those who would like to leverage them. Adding headers to messages allows you to provide application-specific metadata, such as compression or encryption-related information, without touching the payload. We also provide some NATS specific headers for use in JetStream and other features.
When taking down a server for maintenance, servers can be signaled to enter Lame Duck Mode where they do not accept new connections and evict existing connections over a period of time. Maintainer supported clients will notify applications that a server has entered this state and will be shutting down, allowing a client to smoothly transition to another server or cluster and better maintain business continuity during scheduled maintenance periods.
Why wait for timeouts when services aren’t available? When a request is made to a service (request-reply) and the NATS Server knows there are no services available the server will short circuit the request. A “no-responders” protocol message will be sent back to the requesting client which will break from blocking API calls. This allows applications to immediately react which further enables building a highly responsive system at scale, even in the face of application failures and network partitions.
Reduce risk when onboarding new services. Canary deployments, A/B testing, and transparent teeing of data streams are now fully supported in NATS. The NATS Server allows accounts to form subject mappings from one subject to another for both client inbound and service import invocations and allows weighted sets for the destinations. Map any percentage - 1 to 100 percent of your traffic - to other subjects, and change this at runtime with a server configuration reload. You can even artificially drop a percentage of traffic to introduce chaos testing into your system. See Configuring Subject Mapping and Traffic Shaping in NATS Server configuration for more details.
NATS now allows for fine-grained monitoring to identify usage metrics tied to a particular account. Inspect messages and bytes sent or received and various connection statistics for a particular account. Accounts can represent anything - a group of applications, a team or organization, a geographic location, or even roles. If NATS is enabling your SaaS solution you could use NATS account scoped metrics to bill users.
What is NATS? NATS is a connective technology that powers modern distributed systems. A connective technology is responsible for addressing, discovery and exchanging of messages that drive the common patterns in distributed systems; asking and answering questions, aka services/microservices, and making and processing statements, or stream processing.
Challenges faced by modern distributed systems Modern distributed systems are defined by an ever increasing number of hyper-connected moving parts and the additional data they generate. They employ both services and streams to drive business value. They are also being defined by location independence and mobility, and not just for things we would typically recognize as front end technologies. Today’s systems and the backend processes, microservices and stream processing are being asked to be location independent and mobile as well, all while being secure.
These modern systems present challenges to technologies that have been used to connect mobile front ends to fairly static backends. These incumbent technologies typically manage addressing and discovery via hostname (DNS) or IP and port, utilize a 1:1 communication pattern, and have multiple different security patterns for authentication and authorization. Although not perfect, incumbent technologies have been good enough in many situations, but times are changing quickly. As microservices, functions, and stream processing are being asked to move to the edge, these technologies and the assumptions they make are being challenged.
Effortless M:N connectivity: NATS manages addressing and discovery based on subjects and not hostname and ports. Defaulting to M:N communications, which is a superset of 1:1, meaning it can do 1:1 but can also do so much more. If you have a 1:1 system that is successful in development, ask how many other moving parts are required for production to work around the assumption of 1:1? Things like load balancers, log systems, and network security models, as well as proxies and sidecars. If your production system requires all of these things just to get around the fact that the connective technology being used, e.g. HTTP or gRPC, is 1:1, it’s time to give NATS.io a look.
Deploy anywhere: NATS can be deployed nearly anywhere; on bare metal, in a VM, as a container, inside K8S, on a device, or whichever environment you choose. NATS runs well within deployment frameworks or without.
Secure: Similarly, NATS is secure by default and makes no requirements on network perimeter security models. When you start considering mobilizing your backend microservices and stream processors, many times the biggest roadblock is security.
NATS infrastructure and clients communicate all topology changes in real-time. This means that NATS clients do not need to change when NATS deployments change. Having to change clients with deployments would be like having to reboot your phone every time your cell provider added or changed a cell tower. This sounds ridiculous of course, but think about how many systems today have their front ends tied so closely to the backend, that any change requires a complete front end reboot or at least a reconfiguration. NATS clients and applications need no such change when backend servers are added and removed and changed. Even DNS is only used to bootstrap first contact, after that, NATS handles endpoint locations transparently.
Another advantage to utilizing a NATS is that it allows a hybrid mix of SaaS/Utility computing with separately owned and operated systems. Meaning you can have a shared NATS service with core microservices, streams and stream processing be extended by groups or individuals who have a need to run their own NATS infrastructure. You are not forced to choose one or the other.
Today’s systems will fall short with new demands. As modern systems continue to evolve and utilize more components and process more data, supporting patterns beyond 1:1 communications, with addressing and discovery tied to DNS is critical. Foundational technologies like NATS promise the most return on investment. Incumbent technologies will not work as modern systems unify cloud, Edge, IoT and beyond. NATS does.
NATS can run anywhere, from large servers and cloud instances, through edge gateways and even IoT devices. Use cases for NATS include:
Cloud Messaging
Services (microservices, service mesh)
Event/Data Streaming (observability, analytics, ML/AI)
Command and Control
IoT and Edge
Telemetry / Sensor Data / Command and Control
Augmenting or Replacing Legacy Messaging Systems
NATS 2.0 was the largest feature release since the original code base for the server was released. NATS 2.0 was created to allow a new way of thinking about NATS as a shared utility, solving problems at scale through distributed security, multi-tenancy, larger networks, and secure sharing of data.
NATS 2.0 was created to address problems in large scale distributed computing.
It is difficult at best to combine identity management end-to-end (or end-to-edge), with data sharing, while adhering to policy and compliance. Current distributed systems increase significantly in operational complexity as they scale upward. Problems arise around service discovery, connectivity, scaling for volume, and application onboarding and updates. Disaster recovery is difficult, especially as systems have evolved to operate in silos defined by technology rather than business needs. As complexity increases, systems become expensive to operate in terms of time and money. They become fragile making it difficult to deploy services and applications hindering innovation, increasing time to value and total cost of ownership.
We decided to:
Reduce total cost of ownership: Users want reduced TCO for their
distributed systems. This is addressed by an easy to use technology that
can operate at global scale with simple configuration and a resilient
and cloud-native architecture.
Decrease Time to Value: As systems scale, time to value increases.
Operations resist change due to risk in touching a complex and fragile
system. Providing isolation contexts can help mitigate this.
Support manageable large scale deployments: No data silos defined by
software, instead easily managed through software to provide exactly what the
business needs. We wanted to provide easy to configure disaster recovery.
Decentralize security: Provide security supporting one
technology end-to-end where organizations may self-manage making it
easier to support a massive number of endpoints.
To achieve this, we added a number of new features that are transparent to existing clients with 100% backward client compatibility.
Accounts are securely isolated communication contexts that allow multi-tenancy spanning a NATS deployment. Accounts allow users to bifurcate technology from business driven use cases, where data silos are created by design, not software limitations. When a client connects, it specifies an account or will default to authentication with a global account.
At least some services need to share data outside of their account. Data can be securely shared between accounts with secure services and streams. Only mutual agreement between account owners permit data flow, and the import account has complete control over its own subject space.
This means within an account, limitations may be set and subjects can be used without worry of collisions with other groups or organizations. Development groups choose any subjects without affecting the rest of the system, and open up accounts to export or import only the services and streams they need.
Accounts are easy, secure, and cost effective. There is one NATS deployment to manage, but organizations and development teams can self manage with more autonomy reducing time to value with faster, more agile development practices.
Services and streams are mechanisms to share messages between accounts.
Think of a service as an RPC endpoint into an account. Behind that account there might be many microservices working in concert to handle requests, but from outside the account there is simply one subject exposed.
Service definitions share an endpoint:
Export a service to allow other accounts to import
Import a service to allow requests to be sent securely and seamlessly to another account
Use cases include most applications - anything that accepts a request and returns a response.
Stream definitions allow continuous data flow between accounts:
Export a stream to allow egress
Import a stream to allow ingress
Use cases include Observability, Metrics, and Data analytics. Any application or endpoint reading a stream of data.
Note that services and streams operate with zero client configuration or API changes. Services may even move between accounts, entirely transparent to end clients.
The system account publishes system messages under established subject patterns. These are internal NATS system messages that may be useful to operators.
Server initiated events and data include:
Client connection events
Account connection status
Authentication errors
Leaf node connection events
Server stats summary
Tools and clients with proper privileges can request:
Service statistics
Server discovery and metrics
Account servers will also publish messages when an account changes.
With this information and system metadata you can build useful monitoring and anomaly detection tools.
NATS 2.0 supports global deployments, allowing for global topologies that optimize for WANs while extend to the edge or devices.
While self healing features have been part of NATS 1.X releases, we ensured they continue to work in global deployments. These include:
Client and server connections automatically reconnect
Auto-Discovery where servers exchange server topology changes with each
other and with clients, in real time with zero configuration changes and
zero downtime while being entirely transparent to clients. Clients can
failover to servers they were not originally configured with.
NATS server clusters dynamically adjust to new or removed servers allowing
for seamless rolling upgrades and scaling up or down.
Conceptually, superclusters are clusters of NATS clusters. Create superclusters to deploy a truly global NATS network. Superclusters use a novel spline based technology with a unique approach to topology, keeping one hop semantics and optimizing WAN traffic through optimistic sends with interest graph pruning. Superclusters provide transparent, intelligent support for geo-distributed queue subscribers.
Superclusters inherently support disaster recovery. With geo-distributed queue subscribers, local clients are preferred, then an RTT is used to find the lowest latency NATS cluster containing a matching queue subscriber in the supercluster.
What does this mean?
Let's say you have a set of load balanced services in US East Coast (US-EAST), another set in the EU (EU-WEST), and a supercluster consisting of a NATS cluster in US-EAST connected to a NATS cluster in EU-WEST. Clients in the US would connect to a US-EAST, and services connected to that cluster would service those clients. Clients in Europe would automatically use services connected to EU-WEST. If the services in US-EAST disconnect, clients in US-EAST will begin using services in EU-WEST.
Once the Eastern US services have reconnected to US-EAST, those services will immediately begin servicing the Eastern US clients since they're local to the NATS cluster. This is automatic and entirely transparent to the client. There is no extra configuration in NATS servers.
This is zero configuration disaster recovery.
Leaf nodes are NATS servers running in a special configuration, allowing hub and spoke topologies to extend superclusters.
Leaf nodes can also bridge separate security domains. e.g. IoT, mobile, web. They are ideal for edge computing, IoT hubs, or data centers that need to be connected to a global NATS deployment. Local applications that communicate using the loopback interface with physical VM or Container security can leverage leaf nodes as well.
Leaf nodes:
Transparently and securely bind to a remote NATS account
Securely bridge specific local data to a wider NATS deployment
Are 100% transparent to clients which remain simple, lightweight, and easy to develop
Allow for a local security scheme while using new NATS security features globally
Can create a DMZ between a local NATS deployment and external NATS cluster or supercluster.
NATS 2.0 Security consists of defining Operators, Accounts, and Users within a NATS deployment.
An Operator provides the root of trust for the system, may represent
a company or enterprise
Creates Accounts for account administrators. An account represents
an organization, business unit, or service offering with a secure context
within the NATS deployment, for example an IT system monitoring group, a
set of microservices, or a regional IoT deployment. Account creation
would likely be managed by a central group.
Accounts define limits and may securely expose services and streams.
Account managers create Users with permissions
Users have specific credentials and permissions.
Operators are represented by a self signed JWT and is the only thing that
is required to be configured in the server. This JWT is usually signed by a
master key that is kept offline. The JWT will contain valid signing keys that
can be revoked with the master updating this JWT.
Operators will sign Account JWTs with various signing keys.
Accounts sign User JWTs, again with various signing keys.
Clients or leaf nodes present User credentials and a signed nonce when connecting.
The server uses resolvers to obtain JWTs and verify the client trust chain.
This allows for rapid change of permissions, authentication and limits, to a secure multi-tenant NATS system.
NATS is a system for publishing and listening for messages on named communication channels we call Subjects
. Fundamentally, NATS is an interest-based
messaging system, where the listener has to subscribe
to a subset of subjects
.
Location transparency Through subject-based addressing, NATS provides location transparency across a (large) cloud of routed NATS servers.
Subject subscriptions are automatically propagated within the server cloud.
Messages will be automatically routed to all interested subscribers, independent of location.
NATS provides two wildcards that can take the place of one or more elements in a dot-separated subject. Publishers will always send a message to a fully specified subject, without the wildcard. While subscribers can use these wildcards to listen to multiple subjects with a single subscription.
The .
character is used to create a subject hierarchy. For example, a world clock application might define the following to logically group related subjects:
There is no hard limit to subject size, but it is recommended to keep the maximum number of tokens in your subjects to a reasonable value. E.g. a maximum of 16 tokens and the subject length to less than 256 characters.
NATS can manage 10s of millions of subjects efficiently, therefore, you can use fine-grained addressing for your business entities. Subjects are ephemeral resources, which will disappear when no longer subscribed to.
Still, subject subscriptions need to be cached by the server in memory. Consider when increasing your subscribed subject count to more than one million you will need more than 1GB of server memory and it will grow linearly from there.
The message subject can be filtered with various means and through various configuration elements in your NATS server cluster. For example, but not limited to:
Security - allow/deny per user
Import/export between accounts
Automatic transformations
When inserting messages into JetStream streams
When sourcing/mirroring JetStream streams
When connecting leaf nodes (NATS edge servers)
...
A well-designed subject hierarchy will make the job a lot easier for those tasks.
There are only two hard problems in computer science: cache invalidation, naming things, and off-by-one errors. -- Unknown author
A subject hierarchy is a powerful tool for addressing your application resources. Most NATS users therefore encode business semantics into the subject name. You are free to choose a structure fit for your purpose, but you should refrain from over-complicating your subject design at the start of the project.
Some guidelines:
Use the first token(s) to establish a general namespace.
Use the final token(s)for identifiers
A subject should be used for more than one message.
Subscriptions should be stable (exist for receiving more than one message).
Use wildcard subscriptions over subscribing to individual subjects whenever feasible.
Name business or physical entities. Refrain from encoding too much data into the subject.
Encode (business) intent into the subject, not technical details.
Pragmatic:
Maybe not so useful:
NATS messages support headers. These can be used for additional metadata. There are subscription modes, which deliver headers only, allowing for efficient scanning of metadata in the message flow.
The first wildcard is *
which will match a single token. For example, if an application wanted to listen for eastern time zones, they could subscribe to time.*.east
, which would match time.us.east
and time.eu.east
. Note that *
can not match a substring within a token time.New*.east
.
The second wildcard is >
which will match one or more tokens, and can only appear at the end of the subject. For example, time.us.>
will match time.us.east
and time.us.east.atlanta
, while time.us.*
would only match time.us.east
since it can't match more than one token.
Subject to your security configuration, wildcards can be used for monitoring by creating something called a wire tap. In the simplest case, you can create a subscriber for >
. This application will receive all messages -- again, subject to security settings -- sent on your NATS cluster.
The wildcard *
can appear multiple times in the same subject. Both types can be used as well. For example, *.*.east.>
will receive time.us.east.atlanta
.
For compatibility across clients and ease of maintaining configuration files, we recommend using alphanumeric characters, -
(dash) and _
(underscore) ASCII characters for subject and other entity names created by the user.
UTF-8 (UTF8) characters are supported in subjects. Please use UTF-8 characters at your own risk. Using multilingual names for technical entities can create many issues for editing, configuration files, display, and cross-border collaboration.
The rules and recommendations here apply to ALL system names, subjects, streams, durables, buckets, keys (in key-value stores), as NATS will create API subjects that contain those names. NATS will enforce these constraints in most cases, but we recommend not relying on this.
Allowed characters: Any Unicode character except null
, space, .
, *
and >
Recommended characters: (a
- z
), (A
- Z
), (0
- 9
), -
and _
(names are case sensitive, and cannot contain whitespace).
Naming Conventions If you want to delimit words, use either CamelCase as in MyServiceOrderCreate
or -
and _
as in my-service-order-create
Special characters: The period .
(which is used to separate the tokens in the subject) and *
and also >
(the *
and >
are used as wildcards) are reserved and cannot be used.
Reserved names: By convention subject names starting with a $
are reserved for system use (e.g. subject names starting with $SYS
or $JS
or $KV
, etc...). Many system subjects also use _
(underscore) (e.g. _INBOX , KV_ABC, OBJ_XYZ etc.)
Good names
Deprecated subject names
Forbidden stream names
By default, for the sake of efficiency, subject names are not verified during message publishing. In particular, when generating subjects programmatically, this will result in illegal subjects which cannot be subscribed to. E.g. subjects containing wildcards may be ignored.
To enable subject name verification, activate pedantic
mode in the client connection options.
We have provided Walkthroughs for you to try NATS (and JetStream) on your own. In order to follow along with the walkthroughs, you could choose one of these options:
The nats
CLI tool must be installed, and a local NATS server must be installed (or you can use a remote server you have access to).
You can use Synadia's NGS.
You could even use the demo server from where you installed NATS. This is accessible via nats://demo.nats.io
(this is a NATS connection URL; not a browser URL. You pass it to a NATS client application).
Alternatively if you already know how to use NATS on a remote server, you only need to pass the server URL to nats
using the -s
option or preferably create a context using nats context add
, to specify the server URL(s) and credentials file containing your user JWT.
To start a simple demonstration server locally, simply run:
(or nats-server -m 8222
if you want to enable the HTTP monitoring functionality)
When the server starts successfully, you will see the following messages:
The NATS server listens for client connections on TCP Port 4222.
Software applications and services need to exchange data. NATS is an infrastructure that allows such data exchange, segmented in the form of messages. We call this a "message oriented middleware".
With NATS, application developers can:
Effortlessly build distributed and scalable client-server applications.
Store and distribute data in realtime in a general manner. This can flexibly be achieved across various environments, languages, cloud providers and on-premises systems.
Developers use one of the NATS client libraries in their application code to allow them to publish, subscribe, request and reply between instances of the application or between completely separate applications. Those applications are generally referred to as 'client applications' or sometimes just as 'clients' throughout this manual (since from the point of view of the NATS server, they are clients).
The NATS services are provided by one or more NATS server processes that are configured to interconnect with each other and provide a NATS service infrastructure. The NATS service infrastructure can scale from a single NATS server process running on an end device (the nats-server
process is less than 20 MB in size!) all the way to a public global super-cluster of many clusters spanning all major cloud providers and all regions of the world such as Synadia's NGS.
To connect a NATS client application with a NATS service, and then subscribe or publish messages to subjects, it only needs to be configured with:
NATS makes it easy for applications to communicate by sending and receiving messages. These messages are addressed and identified by subject strings, and do not depend on network location.
Data is encoded and framed as a message and sent by a publisher. The message is received, decoded, and processed by one or more subscribers.
With this simple design, NATS lets programs share common message-handling code, isolate resources and interdependencies, and scale by easily handling an increase in message volume, whether those are service requests or stream data.
NATS offers multiple qualities of service, depending on whether the application uses just the Core NATS functionality or also leverages the added functionalities enabled by NATS JetStream (JetStream is built into nats-server
but may not be enabled on all service infrastructures).
At most once QoS: Core NATS offers an at most once quality of service. If a subscriber is not listening on the subject (no subject match), or is not active when the message is sent, the message is not received. This is the same level of guarantee that TCP/IP provides. Core NATS is a fire-and-forget messaging system. It will only hold messages in memory and will never write messages directly to disk.
Core NATS is the foundational functionality in a NATS system. It operates on a publish-subscribe model using subject/topic-based addressing. This model offers two significant advantages: location independence and a default many-to-many (M:N) communication pattern. These fundamental concepts enable powerful and innovative solutions for common development patterns, such as microservices, without requiring additional technologies like load balancers, API gateways, or DNS configuration.
This simple walkthrough demonstrates some ways in which subscribers listen on subjects, and publishers send messages on specific subjects.
In a shell or command prompt session, start a client subscriber program.
Here, <subject>
is a subject to listen on. It helps to use unique and well thought-through subject strings because you need to ensure that messages reach the correct subscribers even when wildcards are used.
For example:
You should see the message: Listening on [msg.test]
In another shell or command prompt, create a NATS publisher and send a message.
Where <subject>
is the subject name and <message>
is the text to publish.
For example:
You'll notice that the publisher sends the message and prints: Published [msg.test] : 'NATS MESSAGE'.
The subscriber receives the message and prints: [#1] Received on [msg.test]: 'NATS MESSAGE'.
If the receiver does not get the message, you'll need to check if you are using the same subject name for the publisher and the subscriber.
You'll notice that the subscriber receives the message. Note that a message count is incremented each time your subscribing client receives a message on that subject.
In a new shell or command prompt, start a new NATS subscriber.
Verify that both subscribing clients receive the message.
In a new shell or command prompt session, create a new subscriber that listens on a different subject.
Subscriber 1 and Subscriber 2 receive the message, but Subscriber 3 does not. Why? Because Subscriber 3 is not listening on the message subject used by the publisher.
Change the last subscriber to listen on msg.* and run it:
Note: NATS supports the use of wildcard characters for message subscribers only. You cannot publish a message using a wildcard subject.
This time, all three subscribing clients should receive the message.
Do try out a few more variations of substrings and wildcards to test your understanding.
Publish-subscribe pattern with the NATS CLI
NATS implements a publish-subscribe message distribution model for one-to-many communication. A publisher sends a message on a subject and any active subscriber listening on that subject receives the message. Subscribers can also register interest in wildcard subjects that work a bit like a regular expression (but only a bit). This one-to-many pattern is sometimes called a fan-out.
Messages are composed of:
A subject.
A payload in the form of a byte array.
Any number of header fields.
An optional 'reply' address field.
Messages have a maximum size (which is set in the server configuration with max_payload
). The size is set to 1 MB by default, but can be increased up to 64 MB if needed (though we recommend keeping the max message size to something more reasonable like 8 MB).
When subscribers register themselves to receive messages from a publisher, the 1:N fan-out pattern of messaging ensures that any message sent by a publisher, reaches all subscribers that have registered. NATS provides an additional feature named "queue", which allows subscribers to register themselves as part of a queue. Subscribers that are part of a queue, form the "queue group".
As an example, consider message delivery occurring in the 1:N pattern to all subscribers based on the subject name (delivery happens even to subscribers that are not part of a queue group). If a subscriber is registered based on a queue name, it will always receive messages it is subscribed to, based on the subject name. However, if more subscribers are added to the same queue name, they become a queue group, and only one randomly chosen subscriber of the queue group will consume a message each time a message is received by the queue group. Such distributed queues are a built-in load balancing feature that NATS provides.
Advantages
Ensures application fault tolerance
Workload processing can be scaled up or down
Scale your consumers up or down without duplicate messages
No extra configuration required
Queue groups are defined by the application and their queue subscribers, rather than the server configuration
Queue subscribers are ideal for scaling services. Scale up is as simple as running another application, scale down is terminating the application with a signal that drains the in flight requests. This flexibility and lack of any configuration changes makes NATS an excellent service communication technology that can work with all platform technologies.
When a request is made to a service (request/reply) and the NATS Server knows there are no services available (since there are no client applications currently subscribing to the subject in a queue-group) the server will send a “no-responders” protocol message back to the requesting client which will break from blocking API calls. This allows applications to react immediately. This further enables building a highly responsive system at scale, even in the face of application failures and network partitions.
When connecting to a globally distributed NATS super-cluster, there is an automatic service geo-affinity due to the fact that a service request message will only be routed to another cluster (i.e. another region) if there are no listeners on the cluster available to handle the request locally.
PKI (NKeys encoded ) and signed JWTs create a hierarchy of Operators, Accounts, and Users creating a scalable and flexible distributed security mechanism.
In other middleware systems subjects may be called topics
, channels
, streams
(Note that in NATS the term stream
is used for a message storage).
What is a subject? At its simplest, a subject is just a string of characters that form a name the publisher and subscriber can use to find each other. More commonly are used to scope messages into semantic namespaces.
Please check the on naming for subjects here.
Messages with no subscribers to their subject are automatically discarded (Please see the feature for message persistence).
Please refer to the .
If you are going to run a server locally you need to first install it and start it. Please refer to the
URL: A . This is a string (in a URL format) that specifies the IP address and port where the NATS server(s) can be reached, and what kind of connection to establish (plain TCP, TLS, or Websocket).
Authentication (if needed): details for the application to identify itself with the NATS server(s). NATS supports multiple authentication schemes (username/password, decentralized JWT, token, TLS certificates and Nkey with challenge).
At-least / exactly once QoS: If you need higher qualities of service (at least once and exactly once), or functionalities such as persistent streaming, de-coupled flow control, and Key/Value Store, you can use , which is built in to the NATS server (but needs to be enabled). Of course, you can also always build additional reliability into your client applications yourself with proven and scalable reference designs such as acks and sequence numbers.
NATS systems can be enhanced with , which adds persistence capabilities. While Core NATS provides best-effort, at-most-once message delivery, JetStream introduces at-least-once and exactly-once semantics.
NATS is a messaging system . Subscribers listening on a subject receive messages published on that subject. If the subscriber is not actively listening on the subject, the message is not received. Subscribers can use the wildcard tokens such as *
and >
to match a single token or to match the tail of a subject.
If you have not already done so, you need to the nats
CLI Tool and optionally the nats-server on your machine.
Queue group names follow the same naming rules as . Foremost, they are case sensitive and cannot contain whitespace. Consider structuring queue groups hierarchically using a period .
. Some server functionalities like can use on them.
With a stream can also be used as a queue by setting the retention policy to WorkQueuePolicy
and leveraging to get easy horizontal scalability of the processing (or using an explicit ack push consumer with a queue group of subscribers).
Try NATS queue subscriptions on your own, using a live server by walking through the .
The NATS.io team is continually working to bring you features that enhance your NATS experience. Below, you will find summaries of new NATS implementations. Release notes for the latest patch releases are available on GitHub Releases
See https://nats.io/about/#roadmap
Check out the:
Check out the:
Please check out the announcement post on the blog and the detailed release notes in the server repo.
Support for a min_version
in the leafnodes{}
that would reject servers with a lower version. Note that this would work only for servers that are v2.8.0 and above.
Server version in monitoring landing page.
Logging to /healthz
endpoint when failure occurs.
MQTT and Websocket blocks in the /varz
endpoint.
Consumer check added to healthz
endpoint.
Max stream bytes checks.
Ability to limit a consumer's MaxAckPending
value.
Allow streams and consumers to migrate between clusters. This feature is considered "beta".
New unique_tag
option in jetstream{}
configuration block to prevent placing a stream in the same availability zone twice.
Stream Alternates
field in StreamInfo
response. They provide a priority list of mirrors and the source in relation to where the request originated.
Deterministic subject tokens to partition mapping.
For full release information, see links below;
Release notes 2.8.0
Full list of Changes 2.7.4...2.8.0
See important note if using LeafNode regarding domains.
Ability to configure account limits (max_connections
, max_subscriptions
, max_payload
, max_leafnodes
) in server configuration file.
Overflow placement for streams. A stream can now be placed in the closest cluster from the origin request if it can be placed there.
Support for ephemeral Pull consumers (client libraries will need to be updated to allow those).
New consumer configuration options
For Pull Consumers: MaxRequestBatch
to limit the batch size any client can request MaxRequestExpires
to limit the expiration any client can request
For ephemeral consumers: InactiveThreshold
duration that instructs the server to cleanup ephemeral consumers that are inactive for that long.
Ability to configure max_file_store
and max_memory_store
in the jetstream{}
block as strings with the following suffixes K
, M
, G
and T
, for instance: max_file_store: "256M"
.
Support for the JWT field MaxBytesRequired
, which defines a per-account maximum bytes for assets.
Support for websocket protocol. MQTT clients must connect to the opened websocket port and add /mqtt
to the URL path.
Ability to rate-limit the clients connections by adding the connection_rate_limit: <number of connections per seconds>
in the tls{}
top-level block.
For full release information, see links below;
Release notes 2.7.0
Full list of Changes 2.6.6...2.7.0
See important note if upgrading from a version prior to NATS Server v2.4.0.
See important notes if upgrading from a version prior to v2.5.0.
JetStream's reserved memory and memory used from accounts with reservations in /jsz
and /varz
endpoints
Hardened systemd service
For full release information, see links below;
Release notes 2.6.0
Full list of Changes 2.5.0...2.6.0
See important note if upgrading from a version prior to NATS Server v2.4.0.
MQTTClient
in the /connz
connections report and system events CONNECT and DISCONNECT. Ability to select on mqtt_client
.
Sessions are now all stored inside a single stream, as opposed to individual streams, reducing resources usage.
Due to the aforementioned improvement described above, when an MQTT client connects for the first time after an upgrade to this server version, the server will migrate all individual $MQTT_sess_<xxxx>
streams to a new $MQTT_sess
stream for the user's account.
For full release information, see links below;
Release notes 2.5.0
Full list of Changes 2.4.0...2.5.0
With the latest release of the NATS server, we have fixed bugs around queue subscriptions and have restricted undesired behavior that could be confusing or introduce data loss by unintended/undefined behavior of client applications. If you are using queue subscriptions on a JetStream Push Consumer or have created multiple push subscriptions on the same consumer, you may be affected and need to upgrade your client version along with the server version. We’ve detailed the behavior with different client versions below.
With a NATS Server prior to v2.4.0 and client libraries prior to these versions: NATS C client v3.1.0, Go client v1.12.0, Java client 2.12.0-SNAPSHOT, NATS.js v2.2.0, NATS.ws v1.3.0, NATS.deno v1.2.0, NATS .NET 0.14.0-pre2:
It was possible to create multiple non-queue subscription instances for the same JetStream durable consumer. This is not correct since each instance will receive the same copy of a message and acknowledgment is therefore meaningless since the first instance to acknowledge the message will prevent other instances to control if/when a message should be acknowledged.
Similar to the first issue, it was possible to create many different queue groups for one single JetStream consumer.
For queue subscriptions, if no consumer nor durable name was provided, the libraries would create ephemeral JetStream consumers, which meant that each member of the same group would receive the same message as the other members, which was not the expected behavior. Users assumed that 2 members subscribing to “foo” with the queue group named “bar” would load-balance the consumption of messages from the stream/consumer.
It was possible to create a queue subscription on a JetStream consumer configured with heartbeat and/or flow control. This does not make sense because by definition, queue members would receive some (randomly distributed) messages, so the library would think that heartbeats are missed, and flow control would also be disrupted.
If above client libraries are not updated to the latest but the NATS Server is upgraded to v2.4.0:
It is still possible to create multiple non-queue subscription instances for the same JetStream durable consumer. Since the check is performed by the library (with the help of a new field called PushBound
in the consumer information object set by the server), this misbehavior is still possible.
Queue subscriptions will not receive any message. This is because the server now has a new field DeliverGroup
in the consumer configuration, which won’t be set for existing JetStream consumers and by the older libraries, and detects interest (and starts delivering) only when a subscription on the deliver subject for a queue subscription matching the “deliver group” name is found. Since the JetStream consumer is thought to be a non-deliver-group consumer, the opposite happens: the server detects a core NATS queue subscription on the “deliver subject”, therefore does not trigger delivery on the JetStream consumer’s “deliver subject”.
The 2 other issues are still present because those checks are done in the updated libraries.
If the above client libraries are updated to the latest version, but the NATS Server is still to version prior to v2.4.0 (that is, up to v2.3.4):
It is still possible to create multiple non-queue subscription instances for the same JetStream durable consumer. This is because the JetStream consumer’s information retrieved by the library will not have the PushBound
boolean set by the server, therefore will not be able to alert the user that they are trying to create multiple subscription instances for the same JetStream consumer.
Queue subscriptions will fail because the consumer information returned will not contain the DeliverGroup
field. The error will be likely to the effect that the user tries to create a queue subscription to a non-queue JetStream consumer. Note that if the application creates a queue subscription for a non-yet created JetStream consumer, then this call will succeed, however, adding new members or restarting the application with the now existing JetStream consumer will fail.
Creating queue subscriptions without a named consumer/durable will now result in the library using the queue name as the durable name.
Trying to create a queue subscription with a consumer configuration that has heartbeat and/or flow control will now return an error message.
For completeness, using the latest client libraries and NATS Server v2.4.0:
Trying to start multiple non-queue subscriptions instances for the same JetStream consumer will now return an error to the effect that the user is trying to create a “duplicate subscription”. That is, there is already an active subscription on that JetStream consumer. It is now only possible to create a queue group for a JetStream consumer created for that group. The DeliverGroup
field will be set by the library or need to be provided when creating the consumer externally.
Trying to create a queue subscription without a durable nor consumer name results in the library creating/using the queue group as the JetStream consumer’s durable name.
Trying to create a queue subscription with a consumer configuration that has heartbeat and/or flow control will now return an error message.
Note that if the server v2.4.0 recovers existing JetStream consumers that were created prior to v2.4.0 (and with older libraries), none of them will have a DeliverGroup
, so none of them can be used for queue subscriptions. They will have to be recreated.
Domain to the content of a PubAck
protocol
PushBound
boolean in ConsumerInfo
to indicate that a push consumer is already bound to an active subscription
DeliverGroup
string in ConsumerConfig
to specify which deliver group (or queue group name) the consumer is created for
Warning log statement in situations where catchup for a stream resulted in an error
The ability for normal accounts to access scoped connz
information
Operator option resolver_pinned_accounts
to ensure users are signed by certain accounts
For full release information, see links below;
Release notes 2.4.0
Full list of Changes 2.3.4...2.4.0
Richer API errors. JetStream errors now contain an ErrCode that uniquely describes the error.
Ability to send more advanced Stream purge requests that can purge all messages for a specific subject
Stream can now be configured with a per-subject message limit
Encryption of JetStream data at rest
For full release information, see links below;
Release notes 2.3.0
Full list of Changes 2.2.6...2.3.0
See NATS 2.2 for new features.
Monitoring endpoints as listed in the table below are accessible as system services using the following subject pattern:
$SYS.REQ.SERVER.<id>.<endpoint-name>
(request server monitoring endpoint corresponding to endpoint name.)
$SYS.REQ.SERVER.PING.<endpoint-name>
(from all server request server monitoring endpoint corresponding to endpoint name - will return multiple messages)
For more information on monitoring endpoints see NATS Server Configurations System Events.
no_auth_user
ConfigurationConfiguration of no_auth_user
allows you to refer to a configured user/account when no credentials are provided.
For more information and examples, see Securing NATS
For full release information, see links below;
Release notes 2.1.7
Full list of Changes 2.1.6...2.1.7
This release adds the ability to specify TLS configuration for the account resolver.
trace_verbose
and command line parameters -VV
and -DVV
added. See NATS Logging Configuration
We've added the option to include subscription details in monitoring endpoints /routez
and /connz
. For instance /connz?subs=detail
will now return not only the subjects of the subscription, but the queue name (if applicable) and some other details.
Release notes 2.1.6
Full list of Changes 2.1.4...2.1.6
NATS introduces logfile_size_limit
allowing auto-rotation of log files when the size is greater than the configured limit set in logfile_size_limit
as a number of bytes. You can provide the size with units, such as MB, GB, etc. The backup files will have the same name as the original log file with the suffix .yyyy.mm.dd.hh.mm.ss.micros. For more information see Configuring Logging in the NATS Server Configuration section.
Release notes 2.1.4
Full list of Changes 2.1.2...2.1.4
Queue Permissions allow you to express authorization for queue groups. As queue groups are integral to implementing horizontally scalable microservices, control of who is allowed to join a specific queue group is important to the overall security model. Original PR - https://github.com/nats-io/nats-server/pull/1143
More information on Queue Permissions can be found in the Developing with NATS section.
As services and service mesh functionality has become prominent, we have been looking at ways to make running scalable services on NATS.io a great experience. One area we have been looking at is observability. With publish/subscribe systems, everything is inherently observable, however we realized it was not as simple as it could be. We wanted the ability to transparently add service latency tracking to any given service with no changes to the application. We also realized that global systems, such as those NATS.io can support, needed something more than a single metric. The solution was to allow any sampling rate to be attached to an exported service, with a delivery subject for all collected metrics. We collect metrics that show the requestor’s view of latency, the responder’s view of latency and the NATS subsystem itself, even when requestor and responder are in different parts of the world and connected to different servers in a NATS supercluster.
Release notes 2.1.0
Full list of Changes 2.0.4...2.1.0
For services, the authorization for responding to requests usually included wildcards for _INBOX.> and possibly $GR.> with a supercluster for sending responses. What we really wanted was the ability to allow a service responder to only respond to the reply subject it was sent.
Exported Services were originally tied to a single response. We added the type for the service response and now support singletons (default), streams and chunked. Stream responses represent multiple response messages, chunked represents a single response that may have to be broken up into multiple messages.
Release notes 2.0.4
Full list of Changes 2.0.2...2.0.4
NATS has a built-in persistence engine called JetStream which enables messages to be stored and replayed at a later time. Unlike NATS Core which requires you to have an active subscription to process messages as they happen, JetStream allows the NATS server to capture messages and replay them to consumers as needed. This functionality enables a different quality of service for your NATS messages, and enables fault-tolerant and high-availability configurations.
JetStream is built into nats-server
. If you have a cluster of JetStream-enabled servers you can enable data replication and thus guard against failures and service disruptions.
JetStream was created to address the problems identified with streaming technology today - complexity, fragility, and a lack of scalability. Some technologies address these better than others, but no current streaming technology is truly multi-tenant, horizontally scalable, or supports multiple deployment models. No other technology that we are aware of can scale from edge to cloud using the same security context while having complete deployment observability for operations.
The JetStream persistence layer enables additional use cases typically not found in messaging systems. Being built on top of JetStream they inherit the core capabilities of JetStream, replication, security, routing limits, and mirroring.
Key Value Store A map (associative array) with atomic operations
Object Store File transfer, replications and storage API. Uses chunked transfers for scalability.
Key/Value and File transfer are capabilities are commonly found in in-memory databases or deployment tools. While NATS does not intend to compete with the feature set of such tools, it is our goal to provide the developer with reasonable complete set of data storage and replications features for use cases like micro service, edge deployments and server management.
To configure a nats-server
with JetStream refer to:
For runnable JetStream code examples, refer to NATS by Example.
JetStream was developed with the following goals in mind:
The system must be easy to configure and operate and be observable.
The system must be secure and operate well with NATS 2.0 security models.
The system must scale horizontally and be applicable to a high ingestion rate.
The system must support multiple use cases.
The system must self-heal and always be available.
The system must allow NATS messages to be part of a stream as desired.
The system must display payload agnostic behavior.
The system must not have third party dependencies.
One of the tenets of basic publish/subscribe messaging is that there is a required temporal coupling between the publishers and the subscribers: subscribers only receive the messages that are published when they are actively connected to the messaging system (i.e. they do not receive messages that are published while they are not subscribing or not running or disconnected). The traditional way for messaging systems to provide temporal decoupling of the publishers and subscribers is through the 'durable subscriber' functionality or sometimes through 'queues', but neither one is perfect:
durable subscribers need to be created before the messages get published
queues are meant for workload distribution and consumption, not to be used as a mechanism for message replay.
However, in many use cases, you do not need to 'consume exactly once' functionality but rather the ability to replay messages on demand, as many times as you want. This need has led to the popularity of some 'streaming' messaging platforms.
JetStream provides both the ability to consume messages as they are published (i.e. 'queueing') as well as the ability to replay messages on demand (i.e. 'streaming'). See retention policies below.
Replay policies
JetStream consumers support multiple replay policies, depending on whether the consuming application wants to receive either:
all of the messages currently stored in the stream, meaning a complete 'replay' and you can select the 'replay policy' (i.e. the speed of the replay) to be either:
instant (meaning the messages are delivered to the consumer as fast as it can take them).
original (meaning the messages are delivered to the consumer at the rate they were published into the stream, which can be very useful for example for staging production traffic).
the last message stored in the stream, or the last message for each subject (as streams can capture more than one subject).
starting from a specific sequence number.
starting from a specific start time.
Retention policies and limits
JetStream enables new functionalities and higher qualities of service on top of the base 'Core NATS' functionality. However, practically speaking, streams can't always just keep growing 'forever' and therefore JetStream supports multiple retention policies as well as the ability to impose size limits on streams.
Limits
You can impose the following limits on a stream
Maximum message age.
Maximum total stream size (in bytes).
Maximum number of messages in the stream.
Maximum individual message size.
You can also set limits on the number of consumers that can be defined for the stream at any given point in time.
You must also select a discard policy which specifies what should happen once the stream has reached one of its limits and a new message is published:
discard old means that the stream will automatically delete the oldest message in the stream to make room for the new messages.
discard new means that the new message is discarded (and the JetStream publish call returns an error indicating that a limit was reached).
Retention policy
You can choose what kind of retention you want for each stream:
limits (the default) is to provide a replay of messages in the stream.
work queue (the stream is used as a shared queue and messages are removed from it as they are consumed) is to provide the exactly-once consumption of messages in the stream.
interest (messages are kept in the stream for as long as there are consumers that haven't delivered the message yet) is a variation of work queue that only retains messages if there is interest (consumers currently defined on the stream) for the message's subject.
Note that regardless of the retention policy selected, the limits (and the discard policy) always apply.
Subject mapping transformations
JetStream also enables the ability to apply subject mapping transformations to messages as they are ingested into a stream.
You can choose the durability as well as the resilience of the message storage according to your needs.
Memory storage.
File storage.
Replication (1 (none), 2, 3) between nats servers for Fault Tolerance.
JetStream uses a NATS optimized RAFT distributed quorum algorithm to distribute the persistence service between NATS servers in a cluster while maintaining immediate consistency (as opposed to eventual consistency) even in the face of failures.
For writes (publications to a stream), the formal consistency model of NATS JetStream is Linearizable. On the read side (listening to or replaying messages from streams) the formal models don't really apply because JetStream does not support atomic batching of multiple operations together (so the only kind of 'transaction' is the persisting, replicating and voting of a single operation on the stream) but in essence, JetStream is serializable because messages are added to a stream in one global order (which you can control using compare and publish).
Do note, while we do guarantee immediate consistency when it comes to monotonic writes and monotonic reads. We don't guarantee read your writes at this time, as reads through direct get requests may be served by followers or mirrors. More consistent results can be achieved by sending get requests to the stream leader.
JetStream can also provide encryption at rest of the messages being stored.
In JetStream the configuration for storing messages is defined separately from how they are consumed. Storage is defined in a Stream and consuming messages is defined by multiple Consumers.
Stream replication factor
A stream's replication factor (R, often referred to as the number 'Replicas') determines how many places it is stored allowing you to tune to balance risk with resource usage and performance. A stream that is easily rebuilt or temporary might be memory-based with a R=1 and a stream that can tolerate some downtime might be file-based R-1.
Typical usage to operate in typical outages and balance performance would be a file-based stream with R=3. A highly resilient, but less performant and more expensive configuration is R=5, the replication factor limit.
Rather than defaulting to the maximum, we suggest selecting the best option based on the use case behind the stream. This optimizes resource usage to create a more resilient system at scale.
Replicas=1 - Cannot operate during an outage of the server servicing the stream. Highly performant.
Replicas=2 - No significant benefit at this time. We recommend using Replicas=3 instead.
Replicas=3 - Can tolerate the loss of one server servicing the stream. An ideal balance between risk and performance.
Replicas=4 - No significant benefit over Replicas=3 except marginally in a 5 node cluster.
Replicas=5 - Can tolerate simultaneous loss of two servers servicing the stream. Mitigates risk at the expense of performance.
Mirroring and Sourcing between streams
JetStream also allows server administrators to easily mirror streams, for example between different JetStream domains in order to offer disaster recovery. You can also define a stream that 'sources' from one or more other streams.
JetStream provides decoupled flow control over streams, the flow control is not 'end to end' where the publisher(s) are limited to publish no faster than the slowest of all the consumers (i.e. the lowest common denominator) can receive but is instead happening individually between each client application (publishers or consumers) and the nats server.
When using the JetStream publish calls to publish to streams there is an acknowledgment mechanism between the publisher and the NATS server, and you have the choice of making synchronous or asynchronous (i.e. 'batched') JetStream publish calls.
On the subscriber side, the sending of messages from the NATS server to the client applications receiving or consuming messages from streams is also flow controlled.
Because publications to streams using the JetStream publish calls are acknowledged by the server the base quality of service offered by streams is 'at least once', meaning that while reliable and normally duplicate free there are some specific failure scenarios that could result in a publishing application believing (wrongly) that a message was not published successfully and therefore publishing it again, and there are failure scenarios that could result in a client application's consumption acknowledgment getting lost and therefore in the message being re-sent to the consumer by the server. Those failure scenarios while being rare and even difficult to reproduce do exist and can result in perceived 'message duplication' at the application level.
Therefore, JetStream also offers an 'exactly once' quality of service. For the publishing side, it relies on the publishing application attaching a unique message or publication ID in a message header and on the server keeping track of those IDs for a configurable rolling period of time in order to detect the publisher publishing the same message twice. For the subscribers a double acknowledgment mechanism is used to avoid a message being erroneously re-sent to a subscriber by the server after some kinds of failures.
JetStream consumers are 'views' on a stream, they are subscribed to (or pulled) by client applications to receive copies of (or to consume if the stream is set as a working queue) messages stored in the stream.
Fast push consumers
Client applications can choose to use fast un-acknowledged push
(ordered) consumers to receive messages as fast as possible (for the selected replay policy) on a specified delivery subject or to an inbox. Those consumers are meant to be used to 'replay' rather than 'consume' the messages in a stream.
Horizontally scalable pull consumers with batching
Client applications can also use and share pull
consumers that are demand-driven, support batching and must explicitly acknowledge message reception and processing which means that they can be used to consume (i.e. use the stream as a distributed queue) as well as process the messages in a stream.
Pull consumers can and are meant to be shared between applications (just like queue groups) in order to provide easy and transparent horizontal scalability of the processing or consumption of messages in a stream without having (for example) to worry about having to define partitions or worry about fault-tolerance.
Note: using pull consumers doesn't mean that you can't get updates (new messages published into the stream) 'pushed' in real-time to your application, as you can pass a (reasonable) timeout to the consumer's Fetch call and call it in a loop.
Consumer acknowledgments
While you can decide to use un-acknowledged consumers trading quality of service for the fastest possible delivery of messages, most processing is not idem-potent and requires higher qualities of service (such as the ability to automatically recover from various failure scenarios that could result in some messages not being processed or being processed more than once) and you will want to use acknowledged consumers. JetStream supports more than one kind of acknowledgment:
Some consumers support acknowledging all the messages up to the sequence number of the message being acknowledged, some consumers provide the highest quality of service but require acknowledging the reception and processing of each message explicitly as well as the maximum amount of time the server will wait for an acknowledgment for a specific message before re-delivering it (to another process attached to the consumer).
You can also send back negative acknowledgements.
You can even send in progress acknowledgments (to indicate that you are still processing the message in question and need more time before acking or nacking it).
The JetStream persistence layer enables the Key Value store: the ability to store, retrieve and delete value
messages associated with a key
into a bucket
.
You can subscribe to changes in a Key Value on the bucket or individual key level with watch
and optionally retrieve a history
of the values (and deletions) that have happened on a particular key.
The Key Value store supports atomic create
and update
operations. This enables pessimistic locks (by creating a key and holding on to it) and optimistic locks (using CAS - compare and set).
The Object Store is similar to the Key Value Store. The key being replaced by a file name and value being designed to store arbitrarily large objects
(e.g. files, even if they are very large) rather than 'values' that are message-sized (i.e. limited to 1Mb by default). This is achieved by chunking messages.
Note that JetStream completely replaces the STAN legacy NATS streaming layer.
NATS supports request-reply messaging. In this tutorial you explore how to exchange point-to-point messages using NATS.
If you have not already done so, you need to install the nats
CLI Tool and optionally, the nats-server on your machine.
Start two terminal sessions. These will be used to run the NATS request and reply clients.
You should see the message: Listening on [help.please]
This means that the NATS receiver client is listening for request messages on the "help.please" subject. In NATS, the receiver is a subscriber.
The NATS requestor client makes a request by sending the message "I need help!" on the “help.please” subject.
The NATS receiver client receives the message, formulates the reply ("OK, I CAN HELP!!!"), and sends it to the inbox of the requester.
NATS Comparison to Kafka, Rabbit, gRPC, and others
This feature comparison is a summary of a few of the major components in several of the popular messaging technologies of today. This is by no means an exhaustive list and each technology should be investigated thoroughly to decide which will work best for your implementation.
In this comparison, we will be featuring NATS, Apache Kafka, RabbitMQ, Apache Pulsar, and gRPC.
NATS
Core NATS: 48 known client types, 11 supported by maintainers, 18 contributed by the community. NATS Streaming: 7 client types supported by maintainers, 4 contributed by the community. NATS servers can be compiled on architectures supported by Golang. NATS provides binary distributions.
gRPC
13 client languages.
Kafka
18 client types supported across the community and by Confluent. Kafka servers can run on platforms supporting java; very wide support.
Pulsar
7 client languages, 5 third-party clients - tested on macOS and Linux.
Rabbit
At least 10 client platforms that are maintainer-supported with over 50 community supported client types. Servers are supported on the following platforms: Linux, Windows NT.
NATS
Streams and Services through built-in publish/subscribe, request-reply, and load-balanced queue subscriber patterns. Dynamic request permissioning and request subject obfuscation is supported.
gRPC
One service, which may have streaming semantics, per channel. Load Balancing for a service can be done either client-side or by using a proxy.
Kafka
Streams through publish/subscribe. Load balancing can be achieved with consumer groups. Application code must correlate requests with replies over multiple topics for a service (request-reply) pattern.
Pulsar
Streams through publish/subscribe. Multiple competing consumer patterns support load balancing. Application code must correlate requests with replies over multiple topics for a service (request-reply) pattern.
Rabbit
Streams through publish/subscribe, and services with a direct reply-to feature. Load balancing can be achieved with a Work Queue. Applications must correlate requests with replies over multiple topics for a service (request-reply) pattern.
NATS
At most once, at least once, and exactly once is available in JetStream.
gRPC
At most once.
Kafka
At least once, exactly once.
Pulsar
At most once, at least once, and exactly once.
Rabbit
At most once, at least once.
NATS
NATS supports true multi-tenancy and decentralized security through accounts and defining shared streams and services.
gRPC
N/A
Kafka
Multi-tenancy is not supported.
Pulsar
Multi-tenancy is implemented through tenants; built-in data sharing across tenants is not supported. Each tenant can have its own authentication and authorization scheme.
Rabbit
Multi-tenancy is supported with vhosts; data sharing is not supported.
NATS
NATS supports TLS, NATS credentials, NKEYS (NATS ED25519 keys), username and password, or simple token.
gRPC
TLS, ALT, Token, channel and call credentials, and a plug-in mechanism.
Kafka
Supports Kerberos and TLS. Supports JAAS and an out-of-box authorizer implementation that uses ZooKeeper to store connection and subject.
Pulsar
TLS Authentication, Athenz, Kerberos, JSON Web Token Authentication.
Rabbit
TLS, SASL, username and password, and pluggable authorization.
NATS
Account limits including number of connections, message size, number of imports and exports. User-level publish and subscribe permissions, connection restrictions, CIDR address restrictions, and time of day restrictions.
gRPC
Users can configure call credentials to authorize fine-grained individual calls on a service.
Kafka
Supports JAAS, ACLs for a rich set of Kafka resources including topics, clusters, groups, and others.
Pulsar
Permissions may be granted to specific roles for lists of operations such as produce and consume.
Rabbit
ACLs dictate permissions for configure, write, and read operations on resources like exchanges, queues, transactions, and others. Authentication is pluggable.
NATS
Supports memory and file persistence. Messages can be replayed by time, count, or sequence number, and durable subscriptions are supported. With NATS streaming, scripts can archive old log segments to cold storage.
gRPC
N/A
Kafka
Supports file-based persistence. Messages can be replayed by specifying an offset, and durable subscriptions are supported. Log compaction is supported as well as KSQL.
Pulsar
Supports tiered storage including file, Amazon S3 or Google Cloud Storage (GCS). Pulsar can replay messages from a specific position and supports durable subscriptions. Pulsar SQL and topic compaction is supported, as well as Pulsar functions.
Rabbit
Supports file-based persistence. Rabbit supported queue-based semantics (vs log), so no message replay is available.
NATS
Core NATS supports full mesh clustering with self-healing features to provide high availability to clients. NATS streaming has warm failover backup servers with two modes (FT and full clustering). JetStream supports horizontal scalability with built-in mirroring.
gRPC
N/A. gRPC relies on external resources for HA/FT.
Kafka
Fully replicated cluster members are coordinated via Zookeeper.
Pulsar
Pulsar supports clustered brokers with geo-replication.
Rabbit
Clustering Support with full data replication via federation plugins. Clusters require low-latency networks where network partitions are rare.
NATS
The NATS network element (server) is a small static binary that can be deployed anywhere from large instances in the cloud to resource constrained devices like a Raspberry PI. NATS supports the Adaptive Edge architecture which allows for large, flexible deployments. Single servers, leaf nodes, clusters, and superclusters (cluster of clusters) can be combined in any fashion for an extremely flexible deployment amenable to cloud, on-premise, edge and IoT. Clients are unaware of topology and can connect to any NATS server in a deployment.
gRPC
gRPC is point to point and does not have a server or broker to deploy or manage, but always requires additional pieces for production deployments.
Kafka
Pulsar
Rabbit
Rabbit supports clusters and cross-cluster message propagation through a federation plugin. Clients are unaware of topology and may connect to any cluster. The server requires the Erlang VM and dependencies.
NATS
NATS supports exporting monitoring data to Prometheus and has Grafana dashboards to monitor and configure alerts. There are also development monitoring tools such as nats-top. Robust side car deployment or a simple connect-and-view model with NATS surveyor is supported.
gRPC
External components such as a service mesh are required to monitor gRPC.
Kafka
Kafka has a number of management tools and consoles including Confluent Control Center, Kafka, Kafka Web Console, Kafka Offset Monitor.
Pulsar
CLI tools, per-topic dashboards, and third-party tools.
Rabbit
CLI tools, a plugin-based management system with dashboards and third-party tools.
NATS
NATS separates operations from security. User and Account management in a deployment may be decentralized and managed through a CLI. Server (network element) configuration is separated from security with a command line and configuration file which can be reloaded with changes at runtime.
gRPC
External components such as a service mesh are required to manage gRPC.
Kafka
Kafka has a number of management tools and consoles including Confluent Control Center, Kafka, Kafka Web Console, Kafka Offset Monitor.
Pulsar
CLI tools, per-topic dashboards, and third-party tools.
Rabbit
CLI tools, a plugin-based management system with dashboards and third-party tools.
NATS
gRPC
Kafka
Kafka has a large number of integrations in its ecosystem, including stream processing (Storm, Samza, Flink), Hadoop, database (JDBC, Oracle Golden Gate), Search and Query (ElasticSearch, Hive), and a variety of logging and other integrations.
Pulsar
Pulsar has many integrations, including ActiveMQ, Cassandra, Debezium, Flume, Elasticsearch, Kafka, Redis, and others.
Rabbit
RabbitMQ has many plugins, including protocols (MQTT, STOMP), WebSockets, and various authorization and authentication plugins.
Request-Reply is a common pattern in modern distributed systems. A request is sent, and the application either waits on the response with a certain timeout, or receives a response asynchronously.
The increased complexity of modern systems necessitates features like location transparency, scale-up and scale-down, observability (measuring a system's state based on the data it generates) and more. In order to implement this feature-set, various other technologies needed to incorporate additional components, sidecars (processes or services that support the primary application) and proxies. NATS on the other hand, implemented Request-Reply much more easily.
NATS supports the Request-Reply pattern using its core communication mechanism — publish and subscribe. A request is published on a given subject using a reply subject. Responders listen on that subject and send responses to the reply subject. Reply subjects are called "inbox". These are unique subjects that are dynamically directed back to the requester, regardless of the location of either party.
Multiple NATS responders can form dynamic queue groups. Therefore, it's not necessary to manually add or remove subscribers from the group for them to start or stop being distributed messages. It’s done automatically. This allows responders to scale up or down as per demand.
NATS applications "drain before exiting" (processing buffered messages before closing the connection). This allows the applications to scale down without dropping requests.
Since NATS is based on publish-subscribe, observability is as simple as running another application that can view requests and responses to measure latency, watch for anomalies, direct scalability and more.
The power of NATS even allows multiple responses, where the first response is utilized and the system efficiently discards the additional ones. This allows for a sophisticated pattern to have multiple responders, reduce response latency and jitter.
Try NATS request-reply on your own, using a live server by walking through the request-reply walkthrough.
When a request is sent to a subject that has no subscribers, it can be convenient to know about it right away. For this use-case, a NATS client can opt-into no_responder messages. This requires a server and client that support headers. When enabled, a request sent to a subject with no subscribers will immediately receive a reply that has no body, and a 503
status.
Most clients will represent this case by raising or returning an error. For example:
NATS supports a form of load balancing using queue groups. Subscribers register a queue group name. A single subscriber in the group is randomly selected to receive the message.
If you have not already done so, you need to install the nats
CLI Tool and optionally the nats-server on your machine.
The nats reply
instances don't just subscribe to the subject but also automatically join a queue group ("NATS-RPLY-22"
by default)
In a new window
In a new window
You should see that only one of the my-queue group subscribers receives the message and replies it, and you can also see which one of the available queue-group subscribers processed the request from the reply message received (i.e. service instance A, B or C)
You should see that a different queue group subscriber receives the message this time, chosen at random among the 3 queue group members.
You can also send any number of requests back-to-back. From the received messages, you'll see the distribution of those requests amongst the members of the queue-group. For example: nats request foo --count 10 "Request {{Count}}"
You can at any time start yet another service instance, or kill one and see how the queue-group automatically takes care of adding/removing those instances from the group.
Queue groups using the NATS CLI
Streams are message stores, each stream defines how messages are stored and what the limits (duration, size, interest) of the retention are. Streams consume normal NATS subjects, any message published on those subjects will be captured in the defined storage system. You can do a normal publish to the subject for unacknowledged delivery, though it’s better to use the JetStream publish calls instead as the JetStream server will reply with an acknowledgement that it was successfully stored.
The diagram above shows the concept of storing all ORDERS.*
in the Stream even though there are many types of order related messages. We’ll show how you can selectively consume subsets of messages later. Relatively speaking the Stream is the most resource consuming component so being able to combine related data in this manner is important to consider.
Streams can consume many subjects. Here we have ORDERS.*
but we could also consume SHIPPING.state
into the same Stream should that make sense.
Streams support various retention policies which define when messages in the stream can be automatically deleted, such as when stream limits are hit (like max count, size or age of messages), or also more novel options that apply on top of the limits such as interest-based retention or work-queue semantics (see Retention Policy).
Upon reaching message limits, the server will automatically discard messages either by removing the oldest messages to make room for new ones (DiscardOld
) or by refusing to store new messages (DiscardNew
). For more details, see Discard Policy.
Streams support deduplication using a Nats-Msg-Id
header and a sliding window within which to track duplicate messages. See the Message Deduplication section.
For examples on how to configure streams with your preferred NATS client, see NATS by Example.
Below are the set of stream configuration options that can be defined. The Version
column indicates the version of the server the option was introduced. The Editable
column indicates the option can be edited after the stream created. See client-specific examples here.
Name
Identifies the stream and has to be unique within JetStream account. Names cannot contain whitespace, .
, *
, >
, path separators (forward or backwards slash), and non-printable characters.
2.2.0
No
The storage type for stream data.
2.2.0
No
2.2.0
Yes
Replicas
How many replicas to keep for each message in a clustered JetStream, maximum 5.
2.2.0
Yes
MaxAge
Maximum age of any message in the Stream, expressed in nanoseconds.
2.2.0
Yes
MaxBytes
Maximum number of bytes stored in the stream. Adheres to Discard Policy, removing oldest or refusing new messages if the Stream exceeds this size.
2.2.0
Yes
MaxMsgs
Maximum number of messages stored in the stream. Adheres to Discard Policy, removing oldest or refusing new messages if the Stream exceeds this number of messages.
2.2.0
Yes
MaxMsgSize
The largest message that will be accepted by the Stream. The size of a message is a sum of payload and headers.
2.2.0
Yes
MaxConsumers
Maximum number of Consumers that can be defined for a given Stream, -1
for unlimited.
2.2.0
No
NoAck
Default false
. Disables acknowledging messages that are received by the Stream. This is mandatory when archiving messages which have a reply subject set. E.g. requests in an Request/Reply communication. By default JetStream will acknowledge each message with an empty reply on the reply subject.
2.2.0
Yes
Declares the retention policy for the stream.
2.2.0
No
The behavior of discarding messages when any streams’ limits have been reached.
2.2.0
Yes
DuplicateWindow
The window within which to track duplicate messages, expressed in nanoseconds.
2.2.0
Yes
Used to declare where the stream should be placed via tags and/or an explicit cluster name.
2.2.0
Yes
If set, indicates this stream is a mirror of another stream.
2.2.0
No (if defined)
If defined, declares one or more streams this stream will source messages from.
2.2.0
Yes
MaxMsgsPerSubject
Limits maximum number of messages in the stream to retain per subject.
2.3.0
Yes
Description
A verbose description of the stream.
2.3.3
Yes
Sealed
Sealed streams do not allow messages to be deleted via limits or API, sealed streams can not be unsealed via configuration update. Can only be set on already created streams via the Update API.
2.6.2
Yes (once)
DenyDelete
Restricts the ability to delete messages from a stream via the API.
2.6.2
No
DenyPurge
Restricts the ability to purge messages from a stream via the API.
2.6.2
No
Allows the use of the Nats-Rollup
header to replace all contents of a stream, or subject in a stream, with a single new message.
2.6.2
Yes
If set, messages stored to the stream will be immediately republished to the configured subject.
2.8.3
Yes
AllowDirect
If true, and the stream has more than one replica, each replica will respond to direct get requests for individual messages, not only the leader.
2.9.0
Yes
MirrorDirect
If true, and the stream is a mirror, the mirror will participate in a serving direct get requests for individual messages from origin stream.
2.9.0
Yes
DiscardNewPerSubject
If true, applies discard new semantics on a per subject basis. Requires DiscardPolicy
to be DiscardNew
and the MaxMsgsPerSubject
to be set.
2.9.0
Yes
Metadata
A set of application-defined key-value pairs for associating metadata on the stream.
2.10.0
Yes
Compression
If file-based and a compression algorithm is specified, the stream data will be compressed on disk. Valid options are nothing (empty string) or s2
for Snappy compression.
2.10.0
Yes
FirstSeq
If specified, a new stream will be created with its initial sequence set to this value.
2.10.0
No
Applies a subject transform (to matching messages) before storing the message.
2.10.0
Yes
ConsumerLimits
Sets default limits for consumers created for a stream. Those can be overridden per consumer.
2.10.0
Yes
The storage types include:
File
(default) - Uses file-based storage for stream data.
Memory
- Uses memory-based storage for stream data.
Note: a stream configured as a mirror cannot be configured with a set of subjects. A mirror implicitly sources a subset of the origin stream (optionally with a filter), but does not subscribe to additional subjects.
If no explicit subject is specified, the default subject will be the same name as the stream. Multiple subjects can be specified and edited over time. Note, if messages are stored by a stream on a subject that is subsequently removed from the stream config, consumers will still observe those messages if their subject filter overlaps.
The retention options include:
LimitsPolicy
(default) - Retention based on the various limits that are set including: MaxMsgs
, MaxBytes
, MaxAge
, and MaxMsgsPerSubject
. If any of these limits are set, whichever limit is hit first will cause the automatic deletion of the respective message(s). See a full code example.
WorkQueuePolicy
- Retention with the typical behavior of a FIFO queue. Each message can be consumed only once. This is enforced by only allowing one consumer to be created per subject for a work-queue stream (i.e. the consumers' subject filter(s) must not overlap). Once a given message is ack’ed, it will be deleted from the stream. See a full code example.
InterestPolicy
- Retention based on the consumer interest in the stream and messages. The base case is that there are zero consumers defined for a stream. If messages are published to the stream, they will be immediately deleted so there is no interest. This implies that consumers need to be bound to the stream ahead of messages being published to the stream. Once a given message is ack’ed by all consumers filtering on the subject, the message is deleted (same behavior as WorkQueuePolicy
). See a full code example.
If the InterestPolicy
or WorkQueuePolicy
is chosen for a stream, note that any limits, if defined, will still be enforced. For example, given a work-queue stream, if MaxMsgs
are set and the default discard policy of old, messages will be automatically deleted even if the consumer did not receive them.
WorkQueuePolicy
streams will only delete messages enforced by limits or when a message has been successfully Ack’d
by its consumer. Messages that have attempted redelivery and have reached MaxDelivery
attempts for the consumer will remain in the stream and must be manually deleted via the JetStream API.
The discard behavior applies only for streams that have at least one limit defined. The options include:
DiscardOld
(default) - This policy will delete the oldest messages in order to maintain the limit. For example, if MaxAge
is set to one minute, the server will automatically delete messages older than one minute with this policy.
DiscardNew
- This policy will reject new messages from being appended to the stream if it would exceed one of the limits. An extension to this policy is DiscardNewPerSubject
which will apply this policy on a per-subject basis within the stream.
Refers to the placement of the stream assets (data) within a NATS deployment, be it a single cluster or a supercluster. A given stream, including all replicas (not mirrors), are bound to a single cluster. So when creating or moving a stream, a cluster will be chosen to host the assets.
Without declaring explicit placement for a stream, by default, the stream will be created within the cluster that the client is connected to assuming it has sufficient storage available.
By declaring stream placement, where these assets are located can be controlled explicitly. This is generally useful to co-locate with the most active clients (publishers or consumers) or may be required for data sovereignty reasons.
Placement is supported in all client SDKs as well as the CLI. For example, adding a stream via the the CLI to place a stream in a specific cluster looks like this:
For this to work, all servers in a given cluster must define the name
field within the cluster
server configuration block.
If you have multiple clusters that form a supercluster, then each is required to have a different name.
Another placement option are tags. Each server can have its own set of tags, defined in configuration, typically describing properties of geography, hosting provider, sizing tiers, etc. In addition, tags are often used in conjunction with the jetstream.unique_tag
config option to ensure that replicas must be placed on servers having different values for the tag.
For example, a server A, B, and C in the above cluster might all the same configuration except for the availability zone they are deployed to.
Now we can create a stream by using tags, for example indicating we want a stream in us-east1.
If we had a second cluster in Google Cloud with the same region tag, the stream could be placed in either the AWS or GCP cluster. However, the unique_tag
constraint ensures each replica will be placed in a different AZ in the cluster that was selected implicitly by the placement tags.
Although less common, note that both the cluster and tags can be used for placement. This would be used if a single cluster contains servers have different properties.
When a stream is configured with a source
or mirror
, it will automatically and asynchronously replicate messages from the origin stream. There are several options when declaring the configuration.
A source or mirror stream can have its own retention policy, replication, and storage type. Changes to to the source or mirror,e.g. deleting messages or publishing, do not reflect on the origin stream.
Sources
is a generalization of the Mirror
and allows for sourcing data from one or more streams concurrently. We suggest to use Sources
in new configurations. If you require the target stream to act as a read-only replica:
Configure the stream without listen subjects or
Temporarily disable the listen subjects through client authorizations.
A stream defining Sources
is a generalized replication mechanism and allows for sourcing data from one or more streams concurrently as well as allowing direct write/publish by clients. Essentially the source streams and client writes are aggregated into a single interleaved stream. Subject transformation and filtering allow for powerful data distribution architectures.
A mirror can source its messages from exactly one stream and a clients can not directly write to the mirror. Although messages cannot be published to a mirror directly by clients, messages can be deleted on-demand (beyond the retention policy), and consumers have all capabilities available on regular streams.
For details see:
If enabled, the AllowRollup
stream option allows for a published message having a Nats-Rollup
header indicating all prior messages should be purged. The scope of the purge is defined by the header value, either all
or sub
.
The Nats-Rollup: all
header will purge all prior messages in the stream. Whereas the sub
value will purge all prior messages for a given subject.
A common use case for rollup is for state snapshots, where the message being published has accumulated all the necessary state from the prior messages, relative to the stream or a particular subject.
If enabled, the RePublish
stream option will result in the server re-publishing messages received into a stream automatically and immediately after a successful write, to a distinct destination subject.
For high scale needs where, currently, a dedicated consumer may add too much overhead, clients can establish a core NATS subscription to the destination subject and receive messages that were appended to the stream in real-time.
The fields for configuring republish include:
Source
- An optional subject pattern which is a subset of the subjects bound to the stream. It defaults to all messages in the stream, e.g. >
.
Destination
- The destination subject messages will be re-published to. The source and destination must be a valid subject mapping.
HeadersOnly
- If true, the message data will not be included in the re-published message, only an additional header Nats-Msg-Size
indicating the size of the message in bytes.
For each message that is republished, a set of headers are automatically added.
If configured, the SubjectTransform
will perform a subject transform to matching subjects of messages received by the stream and transform the subject, before storing it in the stream. The transform configuration specifies a Source
and Destination
field, following the rules of subject transform.
Consider this architecture
While it is an incomplete architecture it does show a number of key points:
Many related subjects are stored in a Stream
Consumers can have different modes of operation and receive just subsets of the messages
Multiple Acknowledgement modes are supported
A new order arrives on ORDERS.received
, gets sent to the NEW
Consumer who, on success, will create a new message on ORDERS.processed
. The ORDERS.processed
message again enters the Stream where a DISPATCH
Consumer receives it and once processed it will create an ORDERS.completed
message which will again enter the Stream. These operations are all pull
based meaning they are work queues and can scale horizontally. All require acknowledged delivery ensuring no order is missed.
All messages are delivered to a MONITOR
Consumer without any acknowledgement and using Pub/Sub semantics - they are pushed to the monitor.
As messages are acknowledged to the NEW
and DISPATCH
Consumers, a percentage of them are Sampled and messages indicating redelivery counts, ack delays and more, are delivered to the monitoring system.
JetStream, the persistence layer of NATS, not only allows for the higher qualities of service and features associated with 'streaming', but it also enables some functionalities not found in messaging systems.
Create a bucket, which corresponds to a stream in the underlying storage. Define KV/Stream limits as appropriate
Use the operation below.
You can use KV buckets to perform the typical operations you would expect from an immediately consistent key/value store:
put: associate a value with a key
get: retrieve the value associated with a key
delete: clear any value associated with a key
purge: clear all the values associated with all keys
keys: get a copy of all of the keys (with a value or operation associated with it)
create: associate the value with a key only if there is currently no value associated with that key (i.e. compare to null and set)
update: compare and set (aka compare and swap) the value for a key
You can set limits for your buckets, such as:
the maximum size of the bucket
the maximum size for any single value
a TTL: how long the store will keep values for
Finally, you can even do things that typically can not be done with a Key/Value Store:
watch: watch for changes happening for a key, which is similar to subscribing (in the publish/subscribe sense) to the key: the watcher receives updates due to put or delete operations on the key pushed to it in real-time as they happen
watch all: watch for all the changes happening on all the keys in the bucket
history: retrieve a history of the values (and delete operations) associated with each key over time (by default the history of buckets is set to 1, meaning that only the latest value/operation is stored)
If you are running a local nats-server
stop it and restart it with JetStream enabled using nats-server -js
(if that's not already done)
You can then check that JetStream is enabled by using
If you see the below then JetStream is not enabled
Let's start by creating a stream to capture and store the messages published on the subject "foo".
Enter nats stream add <Stream name>
(in the examples below we will name the stream "my_stream"), then enter "foo" as the subject name and hit return to use the defaults for all the other stream attributes:
You can then check the information about the stream you just created:
Let's now start a publisher
As messages are being published on the subject "foo" they are also captured and stored in the stream, you can check that by using nats stream info my_stream
and even look at the messages themselves using nats stream view my_stream
or nats stream get my_stream
Now at this point if you create a 'Core NATS' (i.e. non-streaming) subscriber to listen for messages on the subject 'foo', you will only receive the messages being published after the subscriber was started, this is normal and expected for the basic 'Core NATS' messaging. In order to receive a 'replay' of all the messages contained in the stream (including those that were published in the past) we will now create a 'consumer'
We can administratively create a consumer using the 'nats consumer add ' command, in this example we will name the consumer "pull_consumer", and we will leave the delivery subject to 'nothing' (i.e. just hit return at the prompt) because we are creating a 'pull consumer' and select all
for the start policy, you can then just use the defaults and hit return for all the other prompts. The stream the consumer is created on should be the stream 'my_stream' we just created above.
You can check on the status of any consumer at any time using nats consumer info
or view the messages in the stream using nats stream view my_stream
or nats stream get my_stream
, or even remove individual messages from the stream using nats stream rmm
Now that the consumer has been created and since there are messages in the stream we can now start subscribing to the consumer:
This will print out all the messages in the stream starting with the first message (which was published in the past) and continuing with new messages as they are published until the count is reached.
Note that in this example we are creating a pull consumer with a 'durable' name, this means that the consumer can be shared between as many consuming processes as you want. For example instead of running a single nats consumer next
with a count of 1000 messages you could have started two instances of nats consumer
each with a message count of 500 and you would see the consumption of the messages from the consumer distributed between those instances of nats
Once you have iterated over all the messages in the stream with the consumer, you can get them again by simply creating a new consumer or by deleting that consumer (nats consumer rm
) and re-creating it (nats consumer add
).
You can clean up a stream (and release the resources associated with it (e.g. the messages stored in the stream)) using nats stream purge
You can also delete a stream (which will also automatically delete all of the consumers that may be defined on that stream) using nats stream rm
Streams with source and mirror configurations are best managed through a client API. If you intend to create such a configuration from command line with NATS CLI you should use a JSON configuration.
Minimal example
With additional options
Minimal example
With additional options
When a stream is configured with a source
or mirror
, it will automatically and asynchronously replicate messages from the origin stream.
source
or mirror
are designed to be robust and will recover from a loss of connection. They are suitable for geographic distribution over high latency and unreliable connections. E.g. even a leaf node starting and connecting intermittently every few days will still receive or send messages over the source/mirror link.
There are several options available when declaring the configuration.
Name
- Name of the origin stream to source messages from.
StartSeq
- An optional start sequence of the origin stream to start mirroring from.
StartTime
- An optional message start time to start mirroring from. Any messages that are equal to or greater than the start time will be included.
FilterSubject
- An optional filter subject which will include only messages that match the subject, typically including a wildcard. Note, this cannot be used with SubjectTransforms
.
Domain
- An optional JetStream domain of where the origin stream exists. This is commonly used in a hub cluster and leafnode topology.
The stream using a source or mirror configuration can have its own retention policy, replication, and storage type.
Changes to to the stream using source or mirror,e.g. deleting messages or publishing, do not reflect back on the origin stream from which the data was received.
Deletes in the origin stream are NOT replicated through a source
or mirror
agreement.
Sources
is a generalization of the Mirror
and allows for sourcing data from one or more streams concurrently. If you require the target stream to act as a read-only replica:
Configure the stream without listen subjects or
Temporarily disable the listen subjects through client authorizations.
All configurations are made on the receiving side. The stream from which data is sourced and mirrored does not need to be configured. No cleanup is required on the origin side if the receiver disappears.
A stream can be the origin (source) for multiple streams. This is useful for geographic distribution or for designing "fan out" topologies where data needs to be distributed reliable to a large number (up to millions) of client connections.
Leaf nodes and leaf node domains are explicitly supported through the API prefix
A stream defining Sources
is a generalized replication mechanism and allows for sourcing data from one or more streams concurrently. A stream with sources can still act as a regular stream allowing direct write/publish by local clients to the stream. Essentially the source streams and local client writes are aggregated into a single interleaved stream. Combined with subject transformation and filtering sourcing allows to design sophisticated data distribution architectures.
Sourcing messages does not retain sequence numbers. But it retain the in stream sequence of messages . Between streams sourced to the same target, the sequence of messages is undefined.
A mirror can source its messages from exactly one stream and a clients can not directly write to the mirror. Although messages cannot be published to a mirror directly by clients, messages can be deleted on-demand (beyond the retention policy), and consumers have all capabilities available on regular streams.
Mirrored messages retains the sequence numbers and timestamps of the origin stream.
Source and mirror contracts are designed with one-way (geographic) data replication in mind. Neither configuration provides a full synchronization between streams, which would include deletes or replication of other stream attributes.
The content of the stream from which a source or mirror is drawn needs to be reasonable stable. Quickly deleting messages after publishing them may result in inconsistent replication due to the asynchronous nature of the replication process.
Sources and Mirror try to be be efficient in replicating messages and are lenient towards the source/mirror origin being unreachable (event for extended periods of time), e.g. when using leaf nodes, which are connected intermittently. For sake of efficiency the recovery interval in case of a disconnect is 10-20s.
Mirror and source agreements do not create a visible consumer in the origin stream.
Source and mirror work with origin stream with workqueue retention. The source/mirror will act as a consumer removing messages from the origin stream.
The intended usage is for moving messages conveniently from one location to another (e.g. from a leaf node). It does not trivially implement a distributed workqueue (where messages are distributed to multiple streams sourcing from the same origin), although with the help of subject filtering a distributed workqueue can be approximated.
If you try to create additional (conflicting) consumers on the origin workqueue stream the behavior becomes undefined. A workqueue allows only one consumer per subject. If the source/mirror connection is active local clients trying to create additional consumers will fail. In reverse a source/mirror cannot be created when there is already a local consumer for the same subjects.
Source and mirror for interest based streams is not supported. Jetstream does not forbid this configuration but the behavior is undefined and may change in the future.
A consumer is a stateful view of a stream. It acts as an interface for clients to consume a subset of messages stored in a stream and will keep track of which messages were delivered and acknowledged by clients.
While Streams are responsible for storing the published messages, the consumer is responsible for tracking the delivery and acknowledgments. This tracking ensures that if a message is not acknowledged (un-acked or 'nacked'), the consumer will automatically attempt to re-deliver it. JetStream consumers support various acknowledgment types and policies. If a message is not acknowledged within a user-specified number of delivery attempts, an advisory notification is emitted.
Consumers can be push-based where messages will be delivered to a specified subject or pull-based which allows clients to request batches of messages on demand. The choice of what kind of consumer to use depends on the use-case.
If there is a need to process messages in an application controlled manner and easily scale horizontally, you would use a 'pull consumer'. A simple client application that wants a replay of messages from a stream sequentially you would use an 'ordered push consumer'. An application that wants to benefit from load balancing or acknowledge messages individually will use a regular push consumer.
We recommend pull consumers for new projects. In particular when scalability, detailed flow control or error handling are a concern.
Ordered consumers are the convenient default type of push & pull consumers designed for applications that want to efficiently consume a stream for data inspection or analysis.
Always ephemeral
No acknowledgements (if gap is detected, consumer is recreated)
Automatic flow control/pull processing
Single-threaded dispatching
No load balancing
In addition to the choice of being push or pull, a consumer can also be ephemeral or durable. A consumer is considered durable when an explicit name is set on the Durable
field when creating the consumer, or when InactiveThreshold
is set.
Durables and ephemeral have the same message delivery semantics but an ephemeral consumer will not have persisted state or fault tolerance (server memory only) and will be automatically cleaned up (deleted) after a period of inactivity, when no subscriptions are bound to the consumer.
By default, consumers will have the same replication factor as the stream they consume, and will remain even when there are periods of inactivity (unless InactiveThreshold
is set explicitly). Consumers can recover from server and client failure.
Below are the set of consumer configuration options that can be defined. The Version
column indicates the version of nats-server in which the option was introduced. The Editable
column indicates the option can be edited after the consumer is created.
The policy choices include:
AckExplicit
: The default policy. Each individual message must be acknowledged. Recommended for most reliability and functionality.
AckNone
: No acknowledgment needed; the server assumes acknowledgment on delivery.
AckAll
: Acknowledge only the last message received in a series; all previous messages are automatically acknowledged. Will acknowledge all pending messages for all subscribers for Pull Consumer.
If an acknowledgment is required but not received within the AckWait
window, the message will be redelivered.
Warning: The server may consider an acknowledgment arriving out of the window. For instance, in a queue situation, if a first process fails to acknowledge within the window and the message has been redelivered to another consumer, the acknowledgment from the first consumer will be considered.
The policy choices include:
DeliverAll
: Default policy. Start receiving from the earliest available message in the stream.
DeliverLast
: Start with the last message added to the stream, or the last message matching the consumer's filter subject if defined.
DeliverLastPerSubject
: Start with the latest message for each filtered subject currently in the stream.
DeliverNew
: Start receiving messages created after the consumer was created.
DeliverByStartSequence
: Start at the first message with the specified sequence number. The consumer must specify OptStartSeq
defining the sequence number.
DeliverByStartTime
: Start with messages on or after the specified time. The consumer must specify OptStartTime
defining the start time.
The MaxAckPending
capability provides flow control and applies to both push and pull consumers. For push consumers, MaxAckPending
is the only form of flow control. For pull consumers, client-driven message delivery creates implicit one-to-one flow control with subscribers.
For high throughput, set MaxAckPending
to a high value. For applications with high latency due to external services, use a lower value and adjust AckWait
to avoid re-deliveries.
A filter subject provides server-side filtering of messages before delivery to clients.
For example, a stream factory-events
with subject factory-events.*.*
can have a consumer factory-A
with a filter factory-events.A.*
to deliver only events for factory A
.
A consumer can have a singular FilterSubject
or plural FilterSubjects
. Multiple filters can be applied, such as [factory-events.A.*, factory-events.B.*]
or specific event types [factory-events.*.item_produced, factory-events.*.item_packaged]
.
Warning: For granular consumer permissions, a single filter uses
$JS.API.CONSUMER.CREATE.{stream}.{consumer}.{filter}
to restrict users to specific filters. Multiple filters use the general$JS.API.CONSUMER.DURABLE.CREATE.{stream}.{consumer}
, which does not include the{filter}
token. Use a different strategy for granular permissions.
NATS is a client/server system in the fact that you have 'NATS client applications' (applications using one of the NATS client libraries) that connect to 'NATS servers' that provide the NATS service. The NATS servers work together to provide a NATS service infrastructure to their client applications.
NATS is extremely flexible and scalable and allows the service infrastructure to be as small as a single process running locally on your local machine and as large as an 'Internet of NATS' of Leaf Nodes, and Leaf Node clusters all interconnected in a secure way over a global shared NATS super-cluster.
Regardless of the size and complexity of the NATS service infrastructure being used, the only configuration needed by the client applications being the location (NATS URLs) of one or more NATS servers and depending on the required security, their credentials.
Note that if your application is written in Golang then you even have the option of embedding the NATS server functionality into the application itself (however you need to then configure your application instances with nats-server configuration information).
You will typically start by running a single instance of nats-server on your local development machine, and have your applications connect to it while you do your application development and local testing.
Next you will probably want to start testing and running those applications and servers in a VPC, or a region or in some on-prem location, so you will deploy either single NATS server or clusters of NATS servers in your VPCs/regions/on-prem/etc... locations and in each location have the applications connect their local nats-server or nats-server cluster. You can then connect those local nats-servers or local nats-server clusters together by making them leaf nodes connecting to a 'backbone' cluster or super-cluster, or by connecting them directly together via gateway connections.
If you have very many client applications (i.e. applications deployed on end-user devices all over the Internet, or for example many IoT devices) or many servers in many locations you will then scale your NATS service infrastructure by deploying clusters of NATS servers in multiple locations and multiple cloud providers and VPCs, and connecting those clusters together into a global super-cluster and then devise a scheme to intelligently direct your client applications to the right 'closest' NATS server cluster.
You can deploy and run your own NATS service infrastructure of nats-server instances, composed of servers, clusters of servers, super-cluster and leaf node NATS servers.
JetStream, the persistence layer of NATS, not only allows for the higher qualities of service and features associated with 'streaming', but it also enables some functionalities not found in messaging systems.
One such feature is the Object store functionality, which allows client applications to create buckets
(corresponding to streams) that can store a set of files. Files are stored and transmitted in chunks, allowing files of arbitrary size to be transferred safely over the NATS infrastructure.
Note: Object store is not a distributed storage system. All files in a bucket will need to fit on the target file system.
The Object Store implements a chunking mechanism, allowing you to for example store and retrieve files (i.e. the object) of any size by associating them with a path or file name as the key.
add
a bucket
to hold the files.
put
Add a file to the bucket
get
Retrieve the file and store it to a designated location
del
Delete a file
watch
Subscribe to changes in the bucket. Will receive notifications on successful put
and del
operations.
The Key/Value Store is a JetStream feature, so we need to verify it is enabled by
which may return
In this case, you should enable JetStream.
If you are running a local nats-server
stop it and restart it with JetStream enabled using nats-server -js
(if that's not already done)
You can then check that JetStream is enabled by using
A 'KV bucket' is like a stream; you need to create it before using it, as in nats kv add <KV Bucket Name>
:
Now that we have a bucket, we can assign, or 'put', a value to a specific key:
which should return the key's value Value1
We can fetch, or 'get', the value for a key "Key1":
You can always delete a key and its value by using
It is harmless to delete a non-existent key (check this!!).
K/V Stores can also be used in concurrent design patterns, such as semaphores, by using atomic 'create' and 'update' operations.
E.g. a client wanting exclusive use of a file can lock it by creating a key, whose value is the file name, with create
and deleting this key after completing use of that file. A client can increase the reslience against failure by using a timeout for the bucket
containing this key. The client can use update
with a revision number to keep the bucket
alive.
Updates can also be used for more fine-grained concurrency control, sometimes known as optimistic locking
, where multiple clients can try a task, but only one can successfully complete it.
Create a lock/semaphore with the create
operation.
Only one create
can succeed. First come, first serve. All concurrent attempts will result in an error until the key is deleted
We can also atomically update
, sometimes known as a CAS (compare and swap) operation, a key with an additional parameter revision
A second attempt with the same revision 13, will fail
An unusual functionality of a K/V Store is being able to 'watch' a bucket, or a specific key in that bucket, and receive real-time updates to changes in the store.
For the example above, run nats kv watch my-kv
. This will start a watcher on the bucket we have just created earlier. By default, the KV bucket has a history size of one, and so it only remembers the last change. In our case, the watcher should see a delete of the value associated with the key "Key1":
If we now concurrently change the value of 'my-kv' by
The watcher will see that change:
When you are finished using a bucket, you can delete the bucket, and its resources, by using the rm
operator:
If you are running a local nats-server
stop it and restart it with JetStream enabled using nats-server -js
(if that's not already done)
You can then check that JetStream is enabled by using
Which should output something like:
If you see the below instead then JetStream is not enabled
Just like you need to create streams before you can use them you need to first create an Object Store bucket
which outputs
By default the full file path is used as a key. Provide the key explicitly (e.g. a relative path ) with --name
By default, the file will be stored relative to the local path under its name (not the full path). To specify an output path use --output
You can seal a bucket, meaning that no further changes are allowed on that bucket
Using nats object rm myobjbucket
will delete the bucket and all the files stored in it.
Message headers are used in a variety of JetStream contexts, such de-duplication, auto-purging of messages, metadata from republished messages, and more.
Headers that can be set by a client when a message being published.
Headers set messages that are republished.
Headers that are implicitly added to messages sourced from other streams.
Headers added to messages when the consumer is configured to be "headers only" omitting the body.
Headers used for internal flow-control messages for a mirror.
Kafka supports clustering with mirroring to loosely coupled remote clusters. Clients are tied to partitions defined within clusters. Kafka servers require a JVM, eight cores, 64 GB to128 GB of RAM, two or more 8-TB SAS/SSD disks, and a 10-Gig NIC. __
Pulsar supports clustering and built-in geo-replication between clusters. Clients may connect to any cluster with an appropriately configured tenant and namespace. Pulsar requires a JVM and requires at least 6 Linux machines or VMs. 3 running ZooKeeper. 3 running a Pulsar broker and a BookKeeper bookie. __
NATS supports WebSockets, a Kafka bridge, an IBM MQ Bridge, a Redis Connector, Apache Spark, Apache Flink, CoreOS, Elastic, Elasticsearch, Prometheus, Telegraf, Logrus, Fluent Bit, Fluentd, OpenFAAS, HTTP, and MQTT, and .
There are a number of third party integrations including HTTP, JSON, Prometheus, Grift and others. __
A list of subjects to bind. Wildcards are supported. Cannot be set for streams.
introduces the nats
utility and how you can use it to create, monitor, and manage streams and consumers, but for completeness and reference this is how you'd create the ORDERS scenario. We'll configure a 1 year retention for order related messages:
One such feature is the Key/Value store functionality, which allows client applications to create buckets
and use them as immediately (as opposed to eventually) consistent, persistent (or maps).
Do note, while we do guarantee immediate consistency when it comes to and . We don't guarantee at this time, as reads through direct get requests may be served by followers or mirrors. More consistent results can be achieved by sending get requests to the underlying stream leader of the Key/Value store.
The key conforms to the same , i.e. it can be a dot-separated list of tokens (which means that you can then use wildcards to match hierarchies of keys when watching a bucket), and can only contain . The value can be any byte array
The following is a small walkthrough on creating a stream and a consumer and interacting with the stream using the .
SubjectTransforms
- An optional set of to apply when sourcing messages from the origin stream. Note, in this context, the Source
will act as a filter on the origin stream and the Destination
can optionally be provided to apply a transform. Since multiple subject transforms can be used, disjoint subjects can be sourced from the origin stream while maintaining the order of the messages. Note, this cannot be used with FilterSubject
.
Mirrors can be used for for (geographic) load distribution with the MirrorDirect
stream attribute. See:
Unlike , which provides an at most once delivery guarantee, a consumer in JetStream can provide an at least once delivery guarantee.
These options apply only to pull consumers. For configuration examples, see .
These options apply only to push consumers. For configuration examples, see .
You do not actually need to run your NATS service infrastructure, instead you can instead make use of a public NATS infrastructure offered by a NATS Service Provider such as , think of Synadia Cloud as being an 'Internet of NATS' (literally an "InterNATS") and of Synadia as being an "InterNATS Service Provider".
If using Kubernetes we recommend you use the .
Durable
If set, clients can have subscriptions bind to the consumer and resume until the consumer is explicitly deleted. A durable name cannot contain whitespace, .
, *
, >
, path separators (forward or backward slash), or non-printable characters.
2.2.0
No
A subject that overlaps with the subjects bound to the stream to filter delivery to subscribers. Note: This cannot be used with the FilterSubjects
field.
2.2.0
Yes
The requirement of client acknowledgments, either AckExplicit
, AckNone
, or AckAll
.
2.2.0
No
AckWait
The duration that the server will wait for an acknowledgment for any individual message once it has been delivered to a consumer. If an acknowledgment is not received in time, the message will be redelivered.
2.2.0
Yes
The point in the stream from which to receive messages: DeliverAll
, DeliverLast
, DeliverNew
, DeliverByStartSequence
, DeliverByStartTime
, or DeliverLastPerSubject
.
2.2.0
No
OptStartSeq
Used with the DeliverByStartSequence
deliver policy.
2.2.0
No
OptStartTime
Used with the DeliverByStartTime
deliver policy.
2.2.0
No
Description
A description of the consumer. This can be particularly useful for ephemeral consumers to indicate their purpose since a durable name cannot be provided.
2.3.3
Yes
InactiveThreshold
Duration that instructs the server to clean up consumers inactive for that long. Prior to 2.9, this only applied to ephemeral consumers.
2.2.0
Yes
Defines the maximum number of messages, without acknowledgment, that can be outstanding. Once this limit is reached, message delivery will be suspended. This limit applies across all of the consumer's bound subscriptions. A value of -1 means there can be any number of pending acknowledgments (i.e., no flow control). The default is 1000.
2.2.0
Yes
MaxDeliver
The maximum number of times a specific message delivery will be attempted. Applies to any message that is re-sent due to acknowledgment policy (i.e., due to a negative acknowledgment or no acknowledgment sent by the client). The default is -1 (redeliver until acknowledged). Messages that have reached the maximum delivery count will stay in the stream.
2.2.0
Yes
Backoff
A sequence of delays controlling the re-delivery of messages on acknowledgment timeout (but not on nak
). The sequence length must be less than or equal to MaxDelivery
. If backoff is not set, a timeout will result in immediate re-delivery. E.g., MaxDelivery=5
backoff=[5s, 30s, 300s, 3600s, 84000s]
will re-deliver a message 5 times over one day. When MaxDelivery is larger than the backoff list, the last delay in the list will apply for the remaining deliveries. Note that backoff is NOT applied to nak
ed messages. A nak
will result in immediate re-delivery unless nakWithDelay
is used to set the re-delivery delay explicitly.
2.7.1
Yes
ReplayPolicy
If the policy is ReplayOriginal
, the messages in the stream will be pushed to the client at the same rate they were originally received, simulating the original timing. If the policy is ReplayInstant
(default), the messages will be pushed to the client as fast as possible while adhering to the acknowledgment policy, Max Ack Pending, and the client's ability to consume those messages.
2.2.0
No
Replicas
Sets the number of replicas for the consumer's state. By default, when the value is set to zero, consumers inherit the number of replicas from the stream.
2.8.3
Yes
MemoryStorage
If set, forces the consumer state to be kept in memory rather than inherit the storage type of the stream (default is file storage). This reduces I/O from acknowledgments, useful for ephemeral consumers.
2.8.3
No
SampleFrequency
Sets the percentage of acknowledgments that should be sampled for observability, 0-100. This value is a string and allows both 30
and 30%
as valid values.
2.2.0
Yes
Metadata
A set of application-defined key-value pairs for associating metadata with the consumer.
2.10.0
Yes
A set of subjects that overlap with the subjects bound to the stream to filter delivery to subscribers. Note: This cannot be used with the FilterSubject
field.
2.10.0
Yes
MaxWaiting
The maximum number of waiting pull requests.
2.2.0
No
MaxRequestExpires
The maximum duration a single pull request will wait for messages to be available to pull.
2.7.0
Yes
MaxRequestBatch
The maximum batch size a single pull request can make. When set with MaxRequestMaxBytes
, the batch size will be constrained by whichever limit is hit first.
2.7.0
Yes
MaxRequestMaxBytes
The maximum total bytes that can be requested in a given batch. When set with MaxRequestBatch
, the batch size will be constrained by whichever limit is hit first.
2.8.3
Yes
DeliverSubject
The subject to deliver messages to. Setting this field decides whether the consumer is push or pull-based. With a deliver subject, the server will push messages to clients subscribed to this subject.
2.2.0
No
DeliverGroup
The queue group name used to distribute messages among subscribers. Analogous to a queue group in core NATS.
2.2.0
Yes
FlowControl
Enables per-subscription flow control using a sliding-window protocol. This protocol relies on the server and client exchanging messages to regulate when and how many messages are pushed to the client. This one-to-one flow control mechanism works in tandem with the one-to-many flow control imposed by MaxAckPending
across all subscriptions bound to a consumer.
2.2.0
Yes
IdleHeartbeat
If set, the server will regularly send a status message to the client during inactivity, indicating that the JetStream service is up and running. The status message will have a code of 100 and no reply address. Note: This mechanism is handled transparently by supported clients.
2.2.0
Yes
RateLimit
Throttles the delivery of messages to the consumer, in bits per second.
2.2.0
Yes
HeadersOnly
Delivers only the headers of messages in the stream, adding a Nats-Msg-Size
header indicating the size of the removed payload.
2.6.2
Yes
Nats-Msg-Id
Client-defined unique identifier for a message that will be used by the server apply de-duplication within the configured Duplicate Window
.
9f01ccf0-8c34-4789-8688-231a2538a98b
2.2.0
Nats-Expected-Stream
Used to assert the published message is received by some expected stream.
my-stream
2.2.0
Nats-Expected-Last-Msg-Id
Used to apply optimistic concurrency control at the stream-level. The value is the last expected Nats-Msg-Id
and the server will reject a publish if the current ID does not match.
9f01ccf0-8c34-4789-8688-231a2538a98b
2.2.0
Nats-Expected-Last-Sequence
Used to apply optimistic concurrency control at the stream-level. The value is the last expected sequence and the server will reject a publish if the current sequence does not match.
328
2.2.0
Nats-Expected-Last-Subject-Sequence
Used to apply optimistic concurrency control at the subject-level. The value is the last expected sequence and the server will reject a publish if the current sequence does not match for the message's subject.
38
2.3.1
Nats-Rollup
Used to apply a purge of all prior messages in the stream or at the subject-level.
all
for stream, sub
for subject
2.6.2
Nats-Stream
Name of the stream the message was republished from.
Nats-Stream: my-stream
2.8.3
Nats-Subject
The original subject of the message.
events.mouse_clicked
2.8.3
Nats-Sequence
The original sequence of the message.
193
2.8.3
Nats-Last-Sequence
The last sequence of the message having the same subject, otherwise zero if this is the first message for the subject.
190
2.8.3
Nats-Time-Stamp
The original timestamp of the message.
2023-08-23T19:53:05.762416Z
2.10.0
Nats-Stream-Source
Specifies the origin stream name, the subject and the sequence number plus the subject filter and destination transform of the message being sourced.
my-stream
2.2.0
Nats-Msg-Size
Indicates the message size in bytes.
1024
2.6.2
Nats-Last-Consumer
2.2.1
Nats-Last-Stream
2.2.1
Nats-Consumer-Stalled
2.4.0
Nats-Response-Type
2.6.4
A command line utility to interact with and manage NATS.
This utility replaces various past tools that were named in the form nats-sub
and nats-pub
, adds several new capabilities and supports full JetStream management.
Check out the repo for all the details: github.com/nats-io/natscli.
nats
Please refer to the installation section in the readme.
You can read about execution policies here.
Binaries are also available as GitHub Releases.
nats
nats help
nats help [<command>...]
or nats [<command>...] --help
Remember to look at the cheat sheets!
nats cheat
nats cheat --sections
nats cheat <section>>
nats context
nats account
nats pub
nats sub
nats request
nats reply
nats bench
nats events
nats rtt
nats server
nats latency
nats governor
nats stream
nats consumer
nats backup
nats restore
nats kv
nats errors
nats schema
The CLI has a number of configuration settings that can be passed either as command line arguments or set in environment variables.
Output extract
The server URL can be set using the --server
CLI flag, or the NATS_URL
environment variable, or using NATS Contexts.
The password can be set using the --password
CLI flag, or the NATS_PASSWORD
environment variable, or using NATS Contexts. For example: if you want to create a script that prompts the user for the system user password (so that for example it doesn't appear in ps
or history
or maybe you don't want it stored in the profile) and then execute one or more nats
commands you do something like:
A context is a named configuration that stores all of these settings. You can designate a default context and switch between contexts.
A context can be created with nats context create my_context_name
and then modified withnats context edit my_context_name
:
This context is stored in the file ~/.config/nats/context/my_context_name.json
.
A context can also be created by specifying settings with nats context save
List your contexts
We passed --select
to the local
one meaning it will be the default when nothing is set.
Select a context
Check the round trip time to the server (using the currently selected context)
You can also specify a context directly
All nats
commands are context aware and the nats context
command has various commands to view, edit and remove contexts.
Server URLs and Credential paths can be resolved via the nsc
command by specifying an URL, for example to find user new
within the orders
account of the acme
operator you can use this:
The server list and credentials path will now be resolved via nsc
, if these are specifically set in the context, the specific context configuration will take precedence.
The server supports hashing of passwords and authentication tokens using bcrypt
. To take advantage of this, simply replace the plaintext password in the configuration with its bcrypt
hash, and the server will automatically utilize bcrypt
as needed. See also: Bcrypted Passwords.
The nats
utility has a command for creating bcrypt
hashes. This can be used for a password or a token in the configuration.
To use the password on the server, add the hash into the server configuration file's authorization section.
Note the client will still have to provide the plain text version of the password, the server however will only store the hash to verify that the password is correct when supplied.
Publish-subscribe pattern using the NATS CLI
NATS supports several kinds of connectivity directly to the NATS servers.
Plain NATS connections
TLS encrypted NATS connections
WebSocket NATS connections
MQTT client connections
There is also a number of adapters available to bridge traffic to and from other messaging systems
JMS which can also be used to bridge MQ and RabbitMQ, since they both offer a JMS interface
NATS has a lot of security features:
Connections can be encrypted with TLS
Client connections can be authenticated in many ways:
You can also integrate NATS with your existing authentication/authorization system or create your own custom authentication using the Auth callout
Authenticated clients are identified as users and have a set of authorizations
You can use accounts for multi-tenancy: each account has its own independent 'subject namespace' and you control the import/export of both streams of messages and services between accounts, and any number of users that client applications can be authenticated as. The subjects or subject wildcards that a user is allowed to publish and/or subscribe to can be controlled either through server configuration or as part of signed JWTs.
JWT authentication/authorization administration is decentralized because each account private key holder can manage their users and their authorizations on their own, without the need for any configuration change on the NATS servers by minting their own JWTs and distributing them to the users. There is no need for the NATS server to ever store any user private keys as they only need to validate the signature chain of trust contained in the user JWT presented by the client application to validate that they have the proper public key for that user.
The JetStream persistence layer of NATS also provides encryption at rest.
The most common form of connecting to the NATS messaging system will be through an application built with any of the 40+ client libraries available for NATS.
The client application will connect to an instance of the NATS server, be it a single server, a cluster of servers or even a global super-cluster such as Synadia Cloud, sending and receiving messages via a range of subscribers contracts. If the application is written in GoLang the NATS server can even be embedded into a Go application.
Client APIs will also allow access to almost all server configuration tasks when using an account with sufficient permissions.
Besides using the client API to manage NATS servers, the NATS ecosystem also has many tools to interact with other applications and services over NATS and streams, support server configuration, enhance monitoring or tune performance such as:
General interaction and management
nats - The nats
Command Line Tool is the easiest way to interact with, test and manage NATS and JetStream from a terminal or from scripts. It's list of features are ever growing, so please download the latest version.
Security
nk - Generate NKeys for use with JSon Web Tokens (JWT) used with nsc
nsc - Configure Operators, Accounts, Users and permission offline to later push them to a production server. This is the preferred tools to create security configuration unless you are using Synadia Control Plane
nats account server - (legacy, replaced by the built-in NATS resolver) a custom security server. NAS can still be used as a reference implementation for you tailor-made security integration.
Monitoring
nats top - Monitor NATS Servers
prometheus-nats-exporter - Export NATS server metrics to Prometheus and a Grafana dashboard.
Benchmarking
see nats bench subcommand of the nats tool
nk
is a command line tool that generates nkeys
. NKeys are a highly secure public-key signature system based on Ed25519.
With NKeys the server can verify identity without ever storing secrets on the server. The authentication system works by requiring a connecting client to provide its public key and digitally sign a challenge with its private key. The server generates a random challenge with every connection request, making it immune to playback attacks. The generated signature is validated a public key, thus proving the identity of the client. If the public key validation succeeds, authentication succeeds.
NKey is an awesome replacement for token authentication, because a connecting client will have to prove it controls the private key for the authorized public key.
To get started with NKeys, you’ll need the nk
tool from https://github.com/nats-io/nkeys/tree/master/nk repository. If you have go installed, enter the following at a command prompt:
To generate a User NKEY:
The first output line starts with the letter S
for Seed. The second letter U
stands for User. Seeds are private keys; you should treat them as secrets and guard them with care.
The second line starts with the letter U
for User, and is a public key which can be safely shared.
To use nkey
authentication, add a user, and set the nkey
property to the public key of the user you want to authenticate. You are only required to use the public key and no other properties are required. Here is a snippet of configuration for the nats-server
:
To complete the end-to-end configuration and use an nkey
, the client is configured to use the seed, which is the private key.
NATS is fast and lightweight and places a priority on performance. the nats
CLI tool can, amongst many other things, be used for running benchmarks and measuring performance of your target NATS service infrastructure. In this tutorial you learn how to benchmark and tune NATS on your systems and environment.
Verify that the NATS server starts successfully, as well as the HTTP monitor:
Let's run a test to see how fast a single publisher can publish one million 16 byte messages to the NATS server.
The output tells you the number of messages and the number of payload bytes that the client was able to publish per second:
Now increase the number of messages published:
When using both publishers and subscribers, nats bench
reports aggregate, as well as individual publish and subscribe throughput performance.
Let's look at throughput for a single publisher with a single subscriber:
Note that the output shows the aggregate throughput as well as the individual publisher and subscriber performance:
When specifying multiple publishers, or multiple subscribers, nats bench
will also report statistics for each publisher and subscriber individually, along with min/max/avg and standard deviation.
Let's increase both the number of messages, and the number of subscribers.:
When more than 1 publisher is specified, nats bench
evenly distributes the total number of messages (-msgs
) across the number of publishers (-pub
).
Now let's increase the number of publishers and examine the output:
In one shell start a nats bench in 'reply mode' and let it run
And in another shell send some requests
In this case the average latency of request-reply between the two nats bench
processes over NATS was 1/8,601th of a second (116.2655505 microseconds).
You can now hit control-c to kill that nats bench --reply
process
Note: by default nats bench
subscribers in 'reply mode' join a queue group, so you can use nats bench
for example to simulate a bunch of load balanced server processes.
First let's publish some messages into a stream, nats bench
will automatically create a stream called benchstream
using default attributes.
We can now measure the speed of replay of messages stored in the stream to a consumer
By default nats bench --js
subscribers use 'ordered push' consumers, which are ordered, reliable and flow controlled but not 'acknowledged' meaning that the subscribers do not send an acknowledgement back to the server upon receiving each message from the stream. Ordered push consumers are the preferred way for a single application instance to get it's own copy of all (or some) of the data stored in a stream. However, you can also benchmark 'pull consumers', which are instead the preferred way to horizontally scale the processing (or consumption) of the messages in the stream where the subscribers do acknowledge the processing of every single message, but can leverage batching to increase the processing throughput.
Don't be afraid to test different JetStream storage and replication options (assuming you have access to a JetStream enabled cluster of servers if you want to go beyond --replicas 1
), and of course the number of publishing/subscribing threads and the publish or pull subscribe batch sizes.
Note: If you change the attributes of a stream between runs you will have to delete the stream (e.g. run nats stream rm benchstream
)
Once you have finished benchmarking streams, remember that if you have stored many messages in the stream (which is very easy and fast to do) your stream may end up using a certain amount of resources on the nats-server(s) infrastructure (i.e. memory and files) that you may want to reclaim.
You can instruct use the --purge
bench command flag to tell nats
to purge the stream of messages before starting its benchmark, or purge the stream manually using nats stream purge benchstream
or just delete it altogether using nats stream rm benchstream
.
NATS supports two types of revocations. Both of these are stored in the Account JWT, so that the nats-server can see the revocations and apply them.
Users are revoked by public key and time. Access to an export, called an activation, can be revoked for a specific account at a specific time. The use of time here can be confusing, but is designed to support the primary uses of revocation.
When a user or activation is revoked at time T, it means that any user JWT or activation token created before that time is invalid. If a new user JWT or new activation token is created after T it can be used. This allows an account owner to revoke a user and renew their access at the same time.
Let's look at an example. Suppose you created a user JWT with access to the subject "billing". Later you decide you don't want that user to have access to "billing". Revoke the user, say at noon on May 1st 2019, and create a new user JWT without access to "billing". The user can no longer log in with the old JWT because it is revoked, but they can log in with the new JWT because it was created after noon May 1st 2019.
nsc
provides a number of commands to create, remove or list revocations:
Both add commands take the flag --at
which defaults to 0, for now, which can be used to set the unix timestamp as described above. By default revocations are at the current time, but you can set them in the past for situations where you know when a problem occurred and was fixed.
Deleting a revocation is permanent and can allow an old activation or user JWT to be valid again. Therefore delete should only be used if you are sure the tokens in question have expired.
If your nats servers are configured to use the built-in NATS resolver, remember that you need to 'push' any account changes you may have done (locally) using nsc revocations
to the servers for those changes to take effect.
i.e. nsc push -i
or nsc push -a B -u nats://localhost
If there are any clients currently connected with as a user that gets added to the revocations, their connections will be immediately terminated as soon as you 'push' your revocations to a nats server.
As previously discussed, NKEYs are identities, and if someone gets a hold of an account or operator nkey they can do everything you can do as you.
NATS has strategies to let you deal with scenarios where your private keys escape out in the wild.
The first and most important line of defense is Signing Keys. Signing Keys allow you have multiple NKEY identities of the same kind (Operator or Account) that have the same degree of trust as the standard Issuer nkey.
The concept behind the signing key is that you can issue a JWT for an operator or an account that lists multiple nkeys. Typically the issuer will match the Subject of the entity issuing the JWT. With SigningKeys, a JWT is considered valid if it is signed by the Subject of the Issuer or one of its signing keys. This enables guarding the private key of the Operator or Account more closely while allowing Accounts, Users or Activation Tokens be signed using alternate private keys.
If an issue should arise where somehow a signing key escapes into the wild, you would remove the compromised signing key from the entity, add a new one, and reissue the entity. When a JWT is validated, if the signing key is missing, the operation is rejected. You are also on the hook to re-issue all JWTs (accounts, users, activation tokens) that were signed with the compromised signing key.
This is effectively a large hammer. You can mitigate the process a bit by having a larger number of signing keys and then rotating the signing keys to get a distribution you can easily handle in case of a compromise. In a future release, we’ll have a revocation process were you can invalidate a single JWT by its unique JWT ID (JTI). For now a sledge hammer you have.
With greater security process, there’s greater complexity. With that said, nsc
doesn’t track public or private signing keys. As these are only identities that when in use presume a manual use. That means that you the user will have to track and manage your private keys more closely.
Let’s get a feel for the workflow. We are going to:
Create an operator with a signing key
Create an account with a signing key
The account will be signed using the operator’s signing key
Create an user with the account’s signing key
All signing key operations revolve around the global nsc
flag -K
or --private-key
. Whenever you want to modify an entity, you have to supply the parent key so that the JWT is signed. Normally this happens automatically but in the case of signing keys, you’ll have to supply the flag by hand.
Creating the operator:
To add a signing key we have to first generate one with nsc
:
On a production environment private keys should be saved to a file and always referenced from the secured file.
Now we are going to edit the operator by adding a signing key with the --sk
flag providing the generated operator public key (the one starting with O
):
Check our handy work:
Now let’s create an account called A
and sign it with the generated operator private signing key. To sign it with the key specify the -K
flag and the private key or a path to the private key:
Let’s generate an account signing key, again we use nk
:
Let’s add the signing key to the account, and remember to sign the account with the operator signing key:
Let's take a look at the account
We can see that the signing key ADUQTJD4TF4O6LTTHCKDKSHKGBN2NECCHHMWFREPKNO6MPA7ZETFEEF7
was added to the account. Also the issuer is the operator signing key (specified by the -K
).
Now let’s create a user and sign it with the account signing key starting with ADUQTJD4TF4O
.
Check the user
As expected, the issuer is now the signing key we generated earlier. To map the user to the actual account, an Issuer Account
field was added to the JWT that identifies the public key of account A.
Scoped Signing Keys simplify user permission management. Previously if you wanted to limit the permissions of users, you had to specify permissions on a per-user basis. With scoped signing keys, you associate a signing key with a set of permissions. This configuration lives on the account JWT and is managed with the nsc edit signing-key
command. You can add as many scoped signing keys as necessary.
To issue a user with a set of permissions, simply sign the user with the signing key having the permission set you want. The user configuration must not have any permissions assigned to it.
On connect, the nats-server will assign the permissions associated with that signing key to the user. If you update the permissions associated with a signing key, the server will immediately update permissions for users signed with that key.
Generate the signing key
Add a service to the account
Since the signing key has a unique role name within an account, it can be subsequently used for easier referencing.
To see the permissions for the user enter nsc describe user
- you will see in the report that the user is scoped, and has the permissions listed. You can inspect and modify the scoped permissions with nsc edit signing-key
- pushing updates to the account will reassign user permissions.
Available as of NATS 2.9.0
Although scoped signing keys are very useful and improve security, by limiting the scope of a particular signing key, the permissions that are set may be too rigid in multi-user setups. For example, given two users pam
and joe
, we may want to allow them to subscribe to their own namespaced subject in order to service requests, e.g. pam.>
and joe.>
. The permission structure is the same between these users, but they differ in the concrete subjects which are further scoped to some property about that user.
Template functions can be used to declare the structure within a scope signing key, but utilize basic templating so that each user that is created with the signing key has user-specific subjects.
The following template functions will expand when a user is created.
{{name()}}
- expands to the name of the user, e.g. pam
{{subject()}}
- expands to the user public nkey value of the user, e.g. UAC...
{{account-name()}}
- expands to the signing account name, e.g. sales
{{account-subject()}}
- expands to the account public nkey value, e.g. AXU...
{{tag(key)}}
- expands key:value
tags associated with the signing key
For example, given a scoped signing key with a templated --allow-sub
subject:
We can create two users in different teams.
The resulting --allow-sub
permission per user would be expanded to:
and
NATS account configurations are built using the nsc
tool. The NSC tool allows you to:
Create and edit Operators, Accounts, Users
Manage publish and subscribe permissions for Users
Define Service and Stream exports from an account
Reference Service and Streams from another account
Generate Activation tokens that grants access to a private service or stream
Generate User credential files
Describe Operators, Accounts, Users, and Activations
Push and pull account JWTs to an account JWTs server
Installing nsc
is easy:
Additional ways of installing nsc are described at nsc's github repository
The script will download the latest version of nsc
and install it into your system.
In case NSC is not initialized already do nsc init
Output of tree -L 2 nsc/
IMPORTANT: nsc
version 2.2.0 has been released. This version of nsc only supports nats-server
v2.2.0 and nats-account-server
v1.0.0. For more information please refer to the nsc 2.2.0 release notes.
You can find various task-oriented tutorials to working with the tool here:
For more specific browsing of the tool syntax, check out the nsc
tool documentation. It can be found within the tool itself:
Or an online version here.
To share services that other accounts can reach via request reply, you have to Export a Service. Services are associated with the account performing the replies and are advertised in the exporting accounts' JWT.
To add a service to your account:
To review the service export:
Importing a service enables you to send requests to the remote Account. To import a Service, you have to create an Import. To create an import you need to know:
The exporting account’s public key
The subject the service is listening on
You can map the service’s subject to a different subject
Self-imports are not valid; you can only import services from other accounts.
To learn how to inspect a JWT from an account server, check this article.
First let's create a second account to import the service into:
Add the import of the subject 'help'
Verifying our work:
Let's also add a user to make requests from the service:
If your nats servers are configured to use the built-in NATS resolver, remember that you need to 'push' any account changes you may have done (locally) using nsc add
to the servers for those changes to take effect.
e.g. nsc push -i
or nsc push -a B -u nats://localhost
To test the service, we can install the 'nats' CLI tool:
Set up a process to handle the request. This process will run from account 'A' using user 'U':
Remember you can also do:
Send the request:
The service receives the request:
And the response is received by the requestor:
Or more simply:
If you want to create a service that is only accessible to accounts you designate you can create a private service. The export will be visible in your account, but subscribing accounts will require an authorization token that must be created by you and generated specifically for the requesting account. The authorization token is simply a JWT signed by your account where you authorize the client account to import your service.
As before, we declared an export, but this time we added the --private
flag. The other thing to note is that the subject for the request has a wildcard. This enables the account to map specific subjects to specifically authorized accounts.
For the foreign account to import a private service and be able to send requests, you have to generate an activation token. The activation token in addition to granting permission to the account allows you to subset the service’s subject:
To generate a token, you’ll need to know the public key of the account importing the service. We can easily find the public key for account B by running:
The command took the account that has the export ('A'), the public key of account B, the subject where requests from account B will be handled, and an output file where the token can be stored. The subject for the export allows the service to handle all requests coming in on private.help.*, but account B can only request from a specific subject.
For completeness, the contents of the JWT file looks like this:
When decoded it looks like this:
The token can be shared directly with the client account.
If you manage many tokens for many accounts, you may want to host activation tokens on a web server and share the URL with the account. The benefit to the hosted approach is that any updates to the token would be available to the importing account whenever their account is updated, provided the URL you host them in is stable. When using a JWT account server, the tokens can be stored right on the server and shared by an URL that is printed when the token is generated.
Importing a private service is more natural than a public one because the activation token stores all the necessary details. Again, the token can be an actual file path or a remote URL.
Describe account B
When importing a service, you can specify the local subject you want to use to make requests. The local subject in this case is private.help
. However when the request is forwarded by NATS, the request is sent on the remote subject.
Testing a private service is no different than a public one:
To share messages you publish with other accounts, you have to Export a Stream. Exports are associated with the account performing the export and advertised in exporting account’s JWT.
To add a stream to your account:
Note that we have exported stream with a subject that contains a wildcard. Any subject that matches the pattern will be exported.
To review the stream export:
Messages this account publishes on a.b.c.>
will be forwarded to all accounts that import this stream.
Importing a stream enables you to receive messages that are published by a different Account. To import a Stream, you have to create an Import. To create an Import you need to know:
The exporting account’s public key
The subject where the stream is published
You can map the stream’s subject to a different subject
Self-imports are not valid; you can only import streams from other accounts.
With the required information, we can add an import to the public stream.
Notice that messages published by the remote account will be received on the same subject as they are originally published. Sometimes you would like to prefix messages received from a stream. To add a prefix specify
--local-subject
. Subscribers in our account can listen toabc.>
. For example if--local-subject abc
, The message will be received asabc.a.b.c.>
.
And verifying it:
Let's also add a user to make requests from the service:
If your NATS servers are configured to use the built-in NATS resolver, remember that you need to 'push' any account changes you may have done locally using nsc add
to the servers for those changes to take effect.
e.g. nsc push -i
or nsc push -a B -u nats://localhost
then
If you want to create a stream that is only accessible to accounts you designate, you can create a private stream. The export will be visible in your account, but subscribing accounts will require an authorization token that must be created by you and generated specifically for the subscribing account.
The authorization token is simply a JWT signed by your account where you authorize the client account to import your export.
This is similar to when we defined an export, but this time we added the --private
flag. The other thing to note is that the subject for the request has a wildcard. This enables the account to map specific subjects to specifically authorized accounts.
For a foreign account to import a private stream, you have to generate an activation token. In addition to granting permissions to the account, the activation token also allows you to subset the exported stream's subject.
To generate a token, you'll need to know the public key of the account importing the service. We can easily find the public key for account B by running:
The command took the account that has the export ('A'), the public key of account B, the subject where the stream will publish to account B.
For completeness, the contents of the JWT file look like this:
When decoded it looks like this:
The token can be shared directly with the client account.
If you manage many tokens for many accounts, you may want to host activation tokens on a web server and share the URL with the account. The benefit to the hosted approach is that any updates to the token would be available to the importing account whenever their account is updated, provided the URL you host them in is stable.
Importing a private stream is more natural than a public one as the activation token given to you already has all of the necessary details. Note that the token can be an actual file path or a remote URL.
Describe account B
Testing a private stream is no different than testing a public one:
then
From a single process to a global super-cluster with leaf node servers, you can always adapt your NATS service deployment to your needs. From servers and VPCs in many clouds, to partially connected small edge devices and everything in between, you can always easily extend and scale your NATS service as your needs grow.
The simplest version of a NATS service infrastructure is a single nats-server
process. The nats-server
binary is highly optimized, very lightweight and extremely efficient in its resources' usage.
Client applications establish a connection to the URL of that nats-server process (e.g. "nats://localhost"
).
If you need a fault-tolerant NATS service or if you need to scale your service capacity, you can cluster a set of nats-server processes together in a cluster.
Client applications establish and maintain a connection to (one of) the nats server URL(s) composing the cluster (e.g. "nats://server1","nats://server2",...
).
You can go further than a single cluster and have disaster recovery and get global deployments (e.g. on multiple locations or regions, multiple VPCs or multiple Cloud providers) by deploying multiple clusters and connecting them together via gateway connections (which are interest pruned).
Client applications establish a connection to (one of) the nats server URL(s) of one of the clusters (e.g. "nats://us-west-1.company.com","nats://us-west-2.company.com",...
).
You can easily 'extend' the NATS service provided by a cluster or super-cluster by deploying 'locally' one or more leaf node nats servers that proxy and route traffic between their client applications and the NATS service infrastructure. The context of 'locality' in this case is not just physical: it could mean a location, an edge device or a single development machine, but it could also service a VPC, a group of server processes for a specific application or different accounts, or even a business unit. Leaf node NATS servers can be configured to connect to their cluster over a WebSocket connection (rather than TLS or plain TCP).
Leaf nodes appear to the cluster as a single account connection. Leaf nodes can provide continuous NATS service for their clients, even while being temporarily disconnected from the cluster(s). You can even enable JetStream on the leaf nodes in order to create local streams that are mirrored (mirroring is store and forward and therefore can recover from connectivity outages) to global streams in the upstream cluster(s).
Client applications are configured with the URLs of their 'local' leaf node server(s) and establish a connection to (one of) the leaf node server(s) (e.g. "nats://leaf-node-1","nats://leaf-node-2",...
).
NATS Clusters
NATS Super-clusters
NATS Leaf Nodes
NATS Service Geo-affinity in Queues
Subject mapping and transforms is a powerful feature of the NATS server. Transformations (we will use mapping and transform interchangeably) apply to various situations when messages are generated and ingested, acting as translations and in some scenarios as filters.
Mapping and transforms is an advanced topic. Before proceeding, please ensure you understand NATS concepts like clusters, accounts and streams.
Transforms can be defined (for details see below):
On the root of the config file (applying to the default $G account). Applying to all matching messages entering through client or leaf node connection into this account. Non-matching subjects will be unchanged.
On the individual account level following the same rules as above.
On subjects, which are imported into an account.
In JetStream context:
On messages imported by streams
On messages republished by JetStream
On messages copied to a stream via a source or mirror. For this purpose, the transform acts as a filter.
Transforms may be used for:
Translating between namespaces. E.g. when mapping between accounts, but also when clusters and leaf nodes implement different semantics for the same subject.
Suppressing subjects. E.g. Temporarily for testing.
For backward compatibility after changing the subject naming hierarchy.
Merging subjects together.
Disambiguation and isolation on super-clusters or leaf nodes, by using different transforms in different clusters and leaf nodes.
Testing. E.g. merging a test subject temporarily into a production subject or rerouting a production subject away from a production consumer.
Partitioning subjects and JetStream streams
Filtering messages copied (sourced/mirrored) into a JetStream stream
Chaos testing and sampling. Mappings can be weighted. Allowing for a certain percentage of messages to be rerouted, simulating loss, failure etc.
...
Priority and sequence of operations
Transforms are applied as soon as a message enters the scope in which the transform was defined (cluster, account, leaf node, stream) and independent of how they arrived (publish by client, passing through gateway, stream import, stream source/mirror). And before any routing or subscription interest is applied. The message will appear as if published from the transformed subject under all circumstances.
Transforms are not applied recursively in the same scope. This is necessary to prevent trivial loops. In the example below only the first matching rule is applied.
Transforms are applied in sequence as they pass through different scopes. For example:
A subject is transformed while being published
Routed to a leaf node and transformed when received on the leaf node
Imported into a stream and stored under a transformed name
Republished from the stream to Core NATS under a final target subject
On a central cluster:
OR
On a leaf cluster
A stream config on the leaf cluster
Security
When using config file-based account management (not using JWT security), you can define the core NATS account level subject transforms in server configuration files, and simply need to reload the configuration whenever you change a transform for the change to take effect.
When using operator JWT security (distributed security) with the built-in resolver you define the transforms and the import/exports in the account JWT, so after modifying them, they will take effect as soon as you push the updated account JWT to the servers.
Testing and debugging
You can easily test individual subject transform rules using the nats
CLI tool command nats server mapping
. See examples below.
From NATS server 2.11 (and NATS versions published thereafter) the handling of subjects, including mappings can be observed with nats trace
In the example below a message is first disambiguated from orders.device1.order1
-> orders.hub.device1.order1
. Then imported into a stream and stored under its original name.
The example of foo:bar
is straightforward. All messages the server receives on subject foo
are remapped and can be received by clients subscribed to bar
.
When no subject is provided the command will operate in interactive mode:
Example server config. Note that the mappings below apply only to the default $G account.
Mapping a full wildcard
With accounts. While this mapping applies to a specific account.
A full wildcard token can be used ONCE in source expression and must be present on the destination expression as well exactly once.
Example: Prefixing a subject:
Wildcard tokens may be referenced by position number in the destination mapping using (only for versions 2.8.0 and above of nats-server
). Syntax: {{wildcard(position)}}
. E.g. {{wildcard(1)}}
references the first wildcard token, {{wildcard(2)}}
references the second wildcard token, etc..
Example: with this transform "bar.*.*" : "baz.{{wildcard(2)}}.{{wildcard(1)}}"
, messages that were originally published to bar.a.b
are remapped in the server to baz.b.a
. Messages arriving at the server on bar.one.two
would be mapped to baz.two.one
, and so forth. Try it for yourself using nats server mapping
.
An older style deprecated mapping syntax using $1
.$2
en lieu of {{wildcard(1)}}.{{wildcard(2)}}
may be seen in other examples.
You can drop tokens from the subject by not using all the wildcard tokens in the destination transform, with the exception of mappings defined as part of import/export between accounts in which case all the wildcard tokens must be used in the transform destination.
Import/export mapping must be mapped bidirectionally unambiguous.
There are two ways you can split tokens:
You can split a token on each occurrence of a separator string using the split(separator)
transform function.
Examples:
Split on '-': nats server mapping "*" "{{split(1,-)}}" foo-bar
returns foo.bar
.
Split on '--': nats server mapping "*" "{{split(1,--)}}" foo--bar
returns foo.bar
.
You can split a token in two at a specific location from the start or the end of the token using the SplitFromLeft(wildcard index, offset)
and SplitFromRight(wildcard index, offset)
transform functions (note that the upper camel case on all subject transform function names is optional you can also use all lowercase function names if you prefer).
Examples:
Split the token at 4 from the left: nats server mapping "*" "{{splitfromleft(1,4)}}" 1234567
returns 1234.567
.
Split the token at 4 from the right: nats server mapping "*" "{{splitfromright(1,4)}}" 1234567
returns 123.4567
.
You can slice tokens into multiple parts at a specific interval from the start or the end of the token by using the SliceFromLeft(wildcard index, number of characters)
and SliceFromRight(wildcard index, number of characters)
mapping functions.
Examples:
Split every 2 characters from the left: nats server mapping "*" "{{slicefromleft(1,2)}}" 1234567
returns 12.34.56.7
.
Split every 2 characters from the right: nats server mapping "*" "{{slicefromright(1,2)}}" 1234567
returns 1.23.45.67
.
Deterministic token partitioning allows you to use subject-based addressing to deterministically divide (partition) a flow of messages where one or more of the subject tokens is mapped into a partition key. Deterministically means, the same tokens are always mapped into the same key. The mapping will appear random and may not be fair
for a small number of subjects.
For example: new customer orders are published on neworders.<customer id>
, you can partition those messages over 3 partition numbers (buckets), using the partition(number of partitions, wildcard token positions...)
function which returns a partition number (between 0 and number of partitions-1) by using the following mapping "neworders.*" : "neworders.{{wildcard(1)}}.{{partition(3,1)}}"
.
Note that multiple token positions can be specified to form a kind of composite partition key. For example, a subject with the form foo.*.*
can have a partition transform of foo.{{wildcard(1)}}.{{wildcard(2)}}.{{partition(5,1,2)}}
which will result in five partitions in the form foo.*.*.<n>
, but using the hash of the two wildcard tokens when computing the partition number.
This particular transform means that any message published on neworders.<customer id>
will be mapped to neworders.<customer id>.<a partition number 0, 1, or 2>
. i.e.:
neworders.customerid1
neworders.customerid1.0
neworders.customerid2
neworders.customerid2.2
neworders.customerid3
neworders.customerid3.1
neworders.customerid4
neworders.customerid4.2
neworders.customerid5
neworders.customerid5.1
neworders.customerid6
neworders.customerid6.0
The transform is deterministic because (as long as the number of partitions is 3) 'customerid1' will always map to the same partition number. The mapping is hash-based, its distribution is random but tends towards 'perfectly balanced' distribution (i.e. the more keys you map the more the number of keys for each partition will tend to converge to the same number).
You can partition on more than one subject wildcard token at a time, e.g.: {{partition(10,1,2)}}
distributes the union of token wildcards 1 and 2 over 10 partitions.
foo.1.a
foo.1.a.1
foo.1.b
foo.1.b.0
foo.2.b
foo.2.b.9
foo.2.a
foo.2.a.2
What this deterministic partition transform enables is the distribution of the messages that are subscribed to using a single subscriber (on neworders.*
) into three separate subscribers (respectively on neworders.*.0
, neworders.*.1
and neworders.*.2
) that can operate in parallel.
The core NATS queue-groups and JetStream durable consumer mechanisms to distribute messages amongst a number of subscribers are partition-less and non-deterministic, meaning that there is no guarantee that two sequential messages published on the same subject are going to be distributed to the same subscriber. While in most use cases a completely dynamic, demand-driven distribution is what you need, it does come at the cost of guaranteed ordering because if two subsequent messages can be sent to two different subscribers which would then both process those messages at the same time at different speeds (or the message has to be re-transmitted, or the network is slow, etc.) and that could result in potential 'out of order' message delivery.
This means that if the application requires strictly ordered message processing, you need to limit distribution of messages to 'one at a time' (per consumer/queue-group, i.e. using the 'max acks pending' setting), which in turn hurts scalability because it means no matter how many workers you have subscribed, only one is doing any processing work at a time.
Being able to evenly split (i.e. partition) subjects in a deterministic manner (meaning that all the messages on a particular subject are always mapped to the same partition) allows you to distribute and scale the processing of messages in a subject stream while still maintaining strict ordering per subject. For example, inserting a partition number as a token in the message subject as part of the stream definition and then using subject filters to create a consumer per partition (or set of partitions).
Another scenario for deterministic partitioning is in the extreme message publication rate scenarios where you are reaching the limits of the throughput of incoming messages into a stream capturing messages using a wildcard subject. This limit can be ultimately reached at very high message rate due to the fact that a single nats-server process is acting as the RAFT leader (coordinator) for any given stream and can therefore become a limiting factor. In that case, distributing (i.e. partitioning) that stream into a number of smaller streams (each one with its own RAFT leader and therefore all these RAFT leaders are spread over all of the JetStream-enabled nats-servers in the cluster rather than a single one) in order to scale.
Yet another use case where deterministic partitioning can help is if you want to leverage local data caching of data (context or potentially heavy historical data for example) that the subscribing process need to access as part of the processing of the messages.
Traffic can be split by percentage from one subject transform to multiple subject transforms.
Here's an example for canary deployments, starting with version 1 of your service.
Applications would make requests of a service at myservice.requests
. The responders doing the work of the server would subscribe to myservice.requests.v1
. Your configuration would look like this:
All requests to myservice.requests
will go to version 1 of your service.
When version 2 comes along, you'll want to test it with a canary deployment. Version 2 would subscribe to myservice.requests.v2
. Launch instances of your service.
Update the configuration file to redirect some portion of the requests made to myservice.requests
to version 2 of your service.
For example the configuration below means 98% of the requests will be sent to version 1 and 2% to version 2.
Once you've determined Version 2 is stable you can switch 100% of the traffic over to it and you can then shut down the version 1 instance of your service.
Traffic shaping is also useful in testing. You might have a service that runs in QA that simulates failure scenarios which could receive 20% of the traffic to test the service requestor.
myservice.requests.*: [{ destination: myservice.requests.{{wildcard(1)}}, weight: 80% }, { destination: myservice.requests.fail.{{wildcard(1)}}, weight: 20% }
Alternatively, introduce loss into your system for chaos testing by mapping a percentage of traffic to the same subject. In this drastic example, 50% of the traffic published to foo.loss.a
would be artificially dropped by the server.
foo.loss.>: [ { destination: foo.loss.>, weight: 50% } ]
You can both split and introduce loss for testing. Here, 90% of requests would go to your service, 8% would go to a service simulating failure conditions, and the unaccounted for 2% would simulate message loss.
myservice.requests: [{ destination: myservice.requests.v3, weight: 90% }, { destination: myservice.requests.v3.fail, weight: 8% }]
the remaining 2% is "lost"
If you are running a super-cluster you can define transforms that apply only to messages being published from a specific cluster.
For example if you have 3 clusters named east
central
and west
and you want to map messages published on foo
in the east
cluster to foo.east
, those published in the central
cluster to foo.central
and so on for west
you can do so by using the cluster
keyword in the mapping source and destination.
This means that the application can be portable in terms of deployment and does not need to be configured with the name of the cluster it happens to be connected in order to compose the subject: it just publishes to foo
and the server will map it to the appropriate subject based on the cluster it's running in.
You can define subject mapping transforms as part of the stream configuration.
Transforms can be applied in multiple places in the stream configuration:
You can apply a subject mapping transformation as part of a stream mirror
You can apply a subject mapping transformation as part of a stream source
You can apply an overall stream ingress subject mapping transformation that applies to all matching messages regardless of how they are ingested into the stream
You can also apply a subject mapping transformation as part of the re-publishing of messages
Note that when used in Mirror, Sources or Republish, the subject transforms are filters with optional transformation, while when used in the Stream configuration it only transforms the subjects of the matching messages and does not act as a filter.
For sources
and republish
transforms the src
expression will act as a filter. Non-matching subjects will be ignored.
For the stream level subject_transform
non-matching subjects will stay untouched.
The nats-top tool provides a dynamic real-time view of a NATS server. nats-top can display a variety of system summary information about the NATS server, such as subscription, pending bytes, number of messages, and more, in real time. For example:
nats-top can be installed using go install
. For example:
With newer versions of Go, you will be required to use go install github.com/nats-io/nats-top@latest
.
NOTE: You may have to run the above command as user sudo
depending on your setup. If you receive an error that you cannot install nats-top because your $GOPATH is not set, when in fact it is set, use command sudo -E go get github.com/nats-io/nats-top
to install nats-top. The -E
flag tells sudo to preserve the current user's environment.
Once installed, nats-top can be run with the command nats-top
and optional arguments.
Optional arguments inclde the following:
While in nats-top view, you can use the following commands.
Use the o<option>
command to set the primary sort key to the <option>
value. The option value can be one of the following: cid
, subs
, pending
, msgs_to
, msgs_from
, bytes_to
, bytes_from
, lang
, version
.
You can also set the sort option on the command line using the -sort
flag. For example: nats-top -sort bytes_to
.
Use the n<limit>
command to set the sample size of connections to request from the server.
You can also set this on the command line using the -n num_connections
flag. For example: nats-top -n 1
.
Note that if n<limit>
is used in conjunction with -sort
, the server will respect both options allowing queries such as the following: Query for the connection with largest number of subscriptions: nats-top -n 1 -sort subs
.
Use the s
command to toggle displaying connection subscriptions.
Use the ?
command to show help message with options.
Use the q
command to quit nats-top.
Accounts, as represented by their JWTs, are signed by the operator. Some operators may use local copies of JWTs (i.e. using the memory resolver), but most should use the NATS account resolver built-in to 'nats-server' to manage their JWTs. Synadia uses a custom server for their JWTs that works similarly to the open-sourced account server.
There are a few special commands when dealing with server based operators:
Account JWTs can be pushed to the server using nsc push
Account JWTs can be pulled from a server using nsc pull
For managed operators this push/pull behavior is built into nsc
. Each time you edit your account JWT nsc
will push the change to a managed operator's server and pull the signed response. If this fails the JWT on disk may not match the value on the server. You can always push or pull the account again without editing it. Note - push only works if the operator JWT was configured with an account server URL.
The managed operator will not only sign your account JWT with its key, but may also edit the JWT to include limits to constrain your access to their NATS servers. Some operators may also add demonstration or standard imports. Generally you can remove these, although the operator gets the final call on all Account edits. As with any deployment, the managed operator doesn't track user JWTs.
To start using a managed operator you need to tell nsc
about it. There are a couple ways to do this. First you can manually tell nsc
to download the operator JWT using the add operator
command:
The operator JWT (or details) should be provided to you by the operator. The second way to add a managed operator is with the init
command:
You can use the name of an existing operator, or a well known one (currently only "synadia").
Once you add a managed operator you can add accounts to it normally, with the caveat that new accounts are pushed and pulled as described above.
To define a well known operator, you would tell nsc
about an operator that you want people in your environment to use by name with a simple environment variable of the form nsc_<operator name>_operator
the value of this environment variable should be the URL for getting the operator JWT. For example:
will tell nsc
that there is a well known operator named zoom with its JWT at https://account-server-host/jwt/v1/operator
. With this definition you can now use the -u
flag with the name "zoom" to add the operator to an nsc
store directory.
The operator JWT should have its account JWT server property set to point to the appropriate URL. For our example this would be:
You can also set one or more service urls. These allow the nsc tool
actions like pub and sub to work. For example:
Developing with NATS involves a blend of distributed application techniques, common NATS features, and library-specific syntax. Besides this guide, most libraries provide auto-generated API documentation, along with language and platform-specific examples, guides, and other resources.
Not all libraries have their own documentation, depending on the language community, but be sure to check out the client libraries' README for more information.
There are many other NATS client libraries and examples contributed and maintained by the community and available on GitHub, such as:
In order for a NATS client application to connect to the NATS service, and then subscribe or publish messages to subjects, it needs to be able to be configured with the details of how to connect to the NATS service infrastructure and of how to authenticate with it.
A 'NATS URL' is a string (in a URL format) that specifies the IP address and port where the NATS server(s) can be reached, and what kind of connection to establish:
TLS encrypted only TCP connection (i.e. NATS URLs starting with tls://...
)
TLS encrypted if the server is configured for it or plain un-encrypted TCP connection otherwise (i.e. NATS URLs starting with nats://...
)
Websocket connection (i.e. NATS URLs starting with ws://...
)
Note that when connecting to a NATS service infrastructure with clusters there is more than one URL and the application should allow for more than one URL to be specified in its NATS connect call (typically you pass a comma separated list of URLs as the URL, e.g. "nats://server1:port1,nats://server2:port2"
).
When connecting to a cluster it is best to provide the complete set of 'seed' URLs for the cluster.
If required: authentication details for the application to identify itself with the NATS server(s). NATS supports multiple authentication schemes:
Your application should expose a way to be configured at run time with the NATS URL(s) to use. If you want to use a secure infrastructure, the application must provide for the definition of either the credentials file (.creds) to use, or the means to encode the token, or Nkey, in the URL(s).
WebSocket and NATS
NATS WebSockets and React
The NATS client libraries can take a full URL, nats://demo.nats.io:4222
, to specify a specific server host and port to connect to.
Libraries are removing the requirement for an explicit protocol and may allow demo.nats.io:4222
or just demo.nats.io
. In the later example the default port 4222 will be used. Check with your specific client library's documentation to see what URL formats are supported.
For example, to connect to the demo server with a URL you can use:
You may need to run the following instead:
Result:
Run some NATS client programs and exchange messages.
In nats-top, enter the command o
followed by the option, such as bytes_to
. You see that nats-top sorts the BYTES_TO column in ascending order.
Use some different sort options to explore nats-top, such as:
cid
, subs
, pending
, msgs_to
, msgs_from
, bytes_to
, bytes_from
, lang
, version
You can also set the sort option on the command line using the -sort
flag. For example: nats-top -sort bytes_to
.
In nats-top, enter the command s
to toggle displaying connection subscriptions. When enabled, you see the subscription subject in nats-top table:
Use the q
command to quit nats-top.
For example, to query for the connection with largest number of subscriptions:
Result: nats-top displays only the client connection with the largest number of subscriptions:
NSC allows you to manage identities. Identities take the form of nkeys. Nkeys are a public-key signature system based on Ed25519 for the NATS ecosystem.
The nkey identities are associated with NATS configuration in the form of a JSON Web Token (JWT). The JWT is digitally signed by the private key of an issuer forming a chain of trust. The nsc
tool creates and manages these identities and allows you to deploy them to a JWT account server, which in turn makes the configurations available to nats-servers.
There’s a logical hierarchy to the entities:
Operators
are responsible for running nats-servers, and issuing account JWTs. Operators set the limits on what an account can do, such as the number of connections, data limits, etc.
Accounts
are responsible for issuing user JWTs. An account defines streams and services that can be exported to other accounts. Likewise, they import streams and services from other accounts.
Users
are issued by an account, and encode limits regarding usage and authorization over the account's subject space.
NSC allows you to create, edit, and delete these entities, and will be central to all account-based configuration.
In this guide, you’ll run end-to-end on some of the configuration scenarios:
Generate NKey identities and their associated JWTs
Make JWTs accessible to a nats-server
Configure a nats-server to use JWTs
Let’s run through the process of creating some identities and JWTs and work through the process.
Let’s create an operator called MyOperator
.
There is an additional switch --sys
that sets up the system account which is required for interacting with the NATS server. You can create and set the system account later.
With the above command, the tool generated an NKEY for the operator, stored the private key safely in its keystore.
Lets add a service URL to the operator. Service URLs specify where the nats-server is listening. Tooling such as nsc
can make use of that configuration:
Creating an account is just as easy:
As expected, the tool generated an NKEY representing the account and stored the private key safely in the keystore.
Finally, let's create a user:
As expected, the tool generated an NKEY representing the user, and stored the private key safely in the keystore. In addition, the tool generated a credentials file. A credentials file contains the JWT for the user and the private key for the user. Credential files are used by NATS clients to identify themselves to the system. The client will extract and present the JWT to the nats-server and use the private key to verify its identity.
NSC manages three different directories:
The nsc home directory which stores nsc related data. By default nsc home lives in ~/.nsc
and can be changed via the $NSC_HOME
environment variable.
An nkeys directory, which stores all the private keys. This directory by default lives in ~/.nkeys
and can be changed via the $NKEYS_PATH
environment variable. The contents of the nkeys directory should be treated as secrets.
A stores directory, which contains JWTs representing the various entities. This directory lives in $NSC_HOME/nats
, and can be changed using the command nsc env -s <dir>
. The stores directory can stored under revision control. The JWTs themselves do not contain any secrets.
The stores directory contains a number of directories. Each named by an operator in question, which in turn contains all accounts and users:
These JWTs are the same artifacts that the NATS servers will use to check the validity of an account, its limits, and the JWTs that are presented by clients when they connect to the nats-server.
The nkeys directory contains all the private keys and credential files. As mentioned before, care must be taken to keep these files secure.
The structure keys directory is machine friendly. All keys are sharded by their kind O
for operators, A
for accounts, U
for users. These prefixes are also part of the public key. The second and third letters in the public key are used to create directories where other like-named keys are stored.
The nk
files themselves are named after the complete public key, and stored in a single string - the private key in question:
The private keys are encoded into a string, and always begin with an S
for seed. The second letter starts with the type of key in question. O
for operators, A
for accounts, U
for users.
In addition to containing keys, the nkeys directory contains a creds
directory. This directory is organized in a way friendly to humans. It stores user credential files or creds
files for short. A credentials file contains a copy of the user JWT and the private key for the user. These files are used by NATS clients to connect to a NATS server:
You can list the current entities you are working with by doing:
The different entity names are listed along with their public key, and whether the key is stored. Stored keys are those that are found in the nkeys directory.
In some cases you may want to view the private keys:
If you don't have the seed (perhaps you don't control the operator), nsc will decorate the row with a !
. If you have more than one account, you can show them all by specifying the --all
flag.
You can view a human readable version of the JWT by using nsc
:
Since the operator JWT is just a JWT you can use other tools, such as jwt.io to decode a JWT and inspect its contents. All JWTs have a header, payload, and signature:
All NATS JWTs will use the algorithm
ed25519 for signature. The payload will list different things. On our basically empty operator, we will only have standard JWT claim
fields:
jti
- a jwt id iat
- the timestamp when the JWT was issued in UNIX time iss
- the issuer of the JWT, in this case the operator's public key sub
- the subject or identity represented by the JWT, in this case the same operator type
- since this is an operator JWT, operator
is the type
NATS specific is the nats
object, which is where we add NATS specific JWT configuration to the JWT claim.
Because the issuer and subject are one and the same, this JWT is self-signed.
Again we can inspect the account:
Finally the user JWT:
The user id is the public key for the user, the issuer is the account. This user can publish and subscribe to anything, as no limits are set.
When a user connects to a nats-server, it presents it's user JWT and signs a nonce using its private key. The server verifies if the user is who they say they are by validating that the nonce was signed using the private key associated with the public key, representing the identify of the user. Next, the server fetches the issuer account and validates that the account was issued by a trusted operator completing the chain of trust verification.
Let’s put all of this together, and create a simple server configuration that accepts sessions from U
.
If you don’t have a nats-server installed, let’s do that now:
Let’s create a configuration that references our operator JWT and the nats-account-server as a resolver. You can use nsc
itself to generate the security part of the server configuration that you can just add to your nats-server
config file.
For example to use the NATS resolver (which is the recommended resolver configuration) use nsc generate config --nats-resolver
.
Edit this generated configuration as needed (e.g. adjust the location where the server will store the JWTs in resolver.dir
) and paste it into your nats-server configuration (or save it to a file and import that file from within you server config file).
At minimum, the server requires the operator
JWT, which we have pointed at directly, and a resolver.
e.g.
And example server config myconfig.cfg
Now start this local test server using nats-server -c myconfig.cfg
The nats-server requires a designated account for operations and monitoring of the server, cluster, or supercluster. If you see this error message:
nats-server: using nats based account resolver - the system account needs to be specified in configuration or the operator jwt
Then there is no system account to interact with the server and you need to add one to the configuration or operator JWT. Let’s add one to the operator JWT using nsc
:
(and re-generate resolver.conf
)
Now start the local test server using: nats-server -c myconfig.cfg
In order for the nats servers to know about the account(s) you have created or changes to the attributes for those accounts, you need to push any new accounts or any changes to account attributes you may have done locally using nsc
into the built-in account resolver of the nats-server. You can do this using nsc push
:
For example to push the account named 'MyAccount' that you have just created into the nats server running locally on your machine use:
You can also use nsc pull -u nats://localhost
to pull the view of the accounts that the local NATS server has into your local nsc copy (i.e. in ~/.nsc
)
As soon as you 'push' an the account JWT to the server (that server's built-in NATS account resolver will take care of distributing that new (or new version of) the account JWT to the other nats servers in the cluster) then the changes will take effect and for example any users you may have created with that account will then be able to connect to any of the nats server in the cluster using the user's JWT.
Install the nats
CLI Tool if you haven't already.
Create a subscriber:
Publish a message:
Subscriber shows:
nats
contextIf you are going to use those credentials with nats
you should create a context so you don't have to pass the connection and authentication arguments each time:
To make it easier to work, you can use the NATS clients built right into NSC. These tools know how to find the credential files in the keyring. For convenience, the tools are aliased to sub
, pub
, req
, reply
:
See nsc tool -h
for more detailed information.
User authorization, as expected, also works with JWT authentication. With nsc
you can specify authorization for specific subjects to which the user can or cannot publish or subscribe. By default a user doesn't have any limits on the subjects that it can publish or subscribe to. Any message stream or message published in the account is subscribable by the user. The user can also publish to any subject or imported service. Note that authorization, if configured, must be specified on a per user basis.
When specifying limits it is important to remember that clients by default use generated "inboxes" to allow publish requests. When specifying subscribe and publish permissions, you need to enable clients to subscribe and publish to _INBOX.>
. You can further restrict it, but you'll be responsible for segmenting the subject space so as to not break request-reply communications between clients.
Let's say you have a service that your account clients can make requests to under q
. To enable the service to receive and respond to requests it requires permissions to subscribe to q
and publish permissions under _INBOX.>
:
As you can see, this client is now limited to publishing responses to _INBOX.>
addresses and subscribing to the service's request subject.
Similarly, we can limit a client:
Lets look at that new user
The client has the opposite permissions of the service. It can publish on the request subject q
, and receive replies on an inbox.
As your projects become more involved, you may work with one or more accounts. NSC tracks your current operator and account. If you are not in a directory containing an operator, account or user, it will use the last operator/account context.
To view your current environment:
If you have multiple accounts, you can use nsc env --account <account name>
to set the account as the current default. If you have defined NKEYS_PATH
or NSC_HOME
in the environment, you'll also see their current effective values. Finally, if you want to set the stores directory to anything other than the default, you can do nsc env --store <dir containing an operator>
. If you have multiple accounts, you can try having multiple terminals, each in a directory for a different account.
Some libraries also provide a special way to connect to a default url, which is generally nats://localhost:4222
:
When connecting to a cluster, there are a few things to think about.
Passing a URL for each cluster member (semi-optional)
The connection algorithm
The reconnect algorithm (discussed later)
Server provided URLs
After a client connects to the server, the server may provide a list of URLs for additional known servers. This allows a client to connect to one server and still have other servers available during reconnect.
To ensure the initial connection, your code should include a list of reasonable front line or seed servers. Those servers may know about other members of the cluster, and may tell the client about those members. But you don't have to configure the client to pass every valid member of the cluster in the connect method.
By providing the ability to pass multiple connect options, NATS can handle the possibility of a machine going down or being unavailable to a client. By adding the ability of the server to feed clients a list of known servers as part of the client-server protocol the mesh created by a cluster can grow and change organically while the clients are running.
Note, failure behavior is library dependent, please check the documentation for your client library on information about what happens if the connect fails.
Connections can be assigned a name which will appear in some of the server monitoring data. This name is not required, but is highly recommended as a friendly connection name will help in monitoring, error reporting, debugging, and testing.
is a -like tool for monitoring nats-server servers.
For a walkthrough with nats-top
check out the .
You can use nsc
to administer multiple operators. Operators can be thought of as the owners of nats-servers, and fall into two categories: local and managed. The key difference, pardon the pun, is that managed operators are ones which you don't have the nkey for. An example of a managed operator is the Synadia's .
, and
and
and many ...
(which can be passed as part of the NATS URL)
(where the application is configured with the location of 'credentials file' containing the JWT and private Nkey)
(where the application is configured with a Token string)
(where the client is configured to use a client TLS certificate and the servers are configured to map the TLS client certificates to users defined in the server configuration)
(where the client is configured with a Seed and User NKeys)
Besides the connectivity and security details, there are numerous options for a NATS connection ranging from to to setting in your application.
You can use to monitor in realtime NATS server connections and message statistics.
For the best experience, you will want to run multiple subscribers, at least 2 or 3. Refer to the .
To configure a server to use accounts, you need to configure it to select the type of account resolver it will use. The preferred option being to configure the server to use the built-in .
When a client library first tries to connect it will use the list of URLs provided to the connection options or function. These URLs are usually checked in random order as to not have every client connect to the same server. The first successful connection is used. Randomization can be .
-m monitor
Monitoring http port from nats-server.
-n num_connections
Limit the connections requested to the server (default 1024).
-d delay_in_secs
Screen refresh interval (default 1 second).
-sort by
Field to use for sorting the connections (see below).
Golang
Yes
Java
Yes
.NET
Yes
Rust
Yes
JavaScript
Yes
Python
Yes
C
Yes
Ruby
Elixir
Zig
Swift
You can use NATS to exchange information with and make requests to other applications. You can also use NATS to make your application into a distributed peer-to-peer application.
At a high level your application can use NATS to:
Send (Publish) information to other applications or instances of your application.
Receive (Subscribe) information (in real-time or whenever your application runs) from other applications or instances of your application.
Make a request to a server or the service provided by another application.
Store shared (consistent) state/data between other applications or instances of your application.
Be informed in real-time about any new data being sent, or any changes to the shared state/data.
Using NATS means that, as an application developer you never have to worry about:
Who sends the information that you want to receive.
Who is interested in the information you are sending to others, or how many are interested or where they are.
Where the service you are sending a request to is located, or how many currently active instances of that service there are.
How many partitions or servers there are in the cluster.
Security (just identify yourself).
Whether your application is up and running at the time the information is sent or not (using JetStream).
Flow control (using JetStream)
Higher qualities of services such as exactly-once (using JetStream)
Fault-tolerance, and which servers are up or down at any given time.
The topology of the NATS server infrastructure or how it is architected.
A NATS Client Application will use the NATS Client Library in the following way:
At initialization time it will first connect (securely if needed) to a NATS Service Infrastructure (i.e. one of the NATS servers).
Once successfully connected the application will:
Create messages and publish them to subjects or streams.
Subscribe to subject(s) or stream consumers to receive messages from other processes.
Publish request messages to a service and receive a reply message.
Receive request messages and send back replies or acknowledgements.
Associate and retrieve messages associated with keys in KV buckets.
Store and retrieve arbitrary large blobs with keys in the object store.
Finally, when the application terminates it should disconnect from the NATS Service Infrastructure.
See the following sections to learn more about those activities.
The first thing any application needs to do is connect to NATS. Depending on the way the NATS Service Infrastructure being used is configured the connection may need to be secured, and therefore the application also needs to be able to specify security credentials at connection time. An application can create as many NATS connections as needed (each connection being completely independent, it could for example connect twice as two different users), although typically most applications only make a single NATS connection.
Once you have obtained a valid connection, you can use that connection in your application to use all of the Core NATS functionalities such as subscribing to subjects, publishing messages, making requests (and getting a JetStream context).
Finally, the application will need to disconnect safely from NATS.
It is recommended that the application use connection event listeners in order to be alerted and log whenever connections, reconnections or disconnections happen. Note that in case of a disconnection from the NATS server process the client library will automatically attempt to reconnect to one of the other NATS servers in the cluster. You can also always check the current connection status.
The recommended way to disconnect is to use Drain() which will wait for any ongoing processing to conclude and clean everything properly, but if you need to close the connection immediately you can use close()
from your connection object.
Messages store the data that applications exchange with each other. A message has a subject, a data payload (byte array), and may also have a reply-to and header fields.
You get messages returned or passed to your callbacks from subscribing, or making requests. The publish (and request) operations typically just take a subject and a byte array data payload and create the message for you, but you can also create a message yourself (if you want to set some headers).
Some messages can be 'acknowledged' (for example message received from JetStream pull consumers), and there are multiple forms of acknowledgements (including negative acknowledgements, and acknowledgements indicating that your application has properly received the message but needs more time to process it).
Some libraries allow you to easily send and receive structured data.
Once your application has successfully connected to the NATS Server infrastructure, you can then start using the returned connection object to interact with NATS.
You can directly publish on a connection some data addressed by a subject (or publish a pre-created messages with headers).
Because of caching, if your application is highly sensitive to latency, you may want to flush after publishing.
Many of the client libraries use the PING/PONG interaction built into the NATS protocol to ensure that flush pushed all of the buffered messages to the server. When an application calls flush, most libraries will put a PING on the outgoing queue of messages, and wait for the server to respond with a PONG before saying that the flush was successful.
Even though the client may use PING/PONG for flush, pings sent this way do not count towards max outgoing pings.
The process of subscribing involves having the client library tell the NATS that an application is interested in a particular subject. When an application is done with a subscription it unsubscribes telling the server to stop sending messages.
Receiving messages with NATS can be library dependent, some languages, like Go or Java, provide synchronous and asynchronous APIs, while others may only support one type of subscription. In general, applications can receive messages asynchronously or synchronously.
You can always subscribe to more than one subject at a time using wildcards.
A client will receive a message for each matching subscription, so if a connection has multiple subscriptions using identical or overlapping subjects (say foo
and >
) the same message will be sent to the client multiple times.
You can also subscribe as part of a distributed queue group. All the subscribers with the same queue group name form the distributed queue. The NATS Servers automatically distributes the messages published on the matching subject(s) between the members of the queue group.
On a given subject there can be more than one queue group created by subscribing applications, each queue group being an independent queue and distributing its own copy of the messages between the queue group members.
One thing to keep in mind when making Core NATS subscriptions to subjects is that your application must be able to keep up with the flow of messages published on the subject(s) or it will otherwise become a slow consumer
When you no longer want to receive the messages on a particular subject you must call unsubscribe, or you can automatically unsubscribe after receiving a specific number of messages.
You can also use NATS to easily and transparently invoke services without needing to know about the location or number of servers for the service. The connection's request call publishes a message on the specified subject that contains a reply-to inbox subject and then waits for a reply message to be received by that inbox.
The server applications servicing those requests simply need to subscribe to the subject on which the requests are published, process the request messages they receive and reply to the message on the subject contained in the request message's Reply-to attribute.
Typically, there is no reason not to want to make your service distributed (i.e. scalable and fault-tolerant). This means that unless there's a specific reason not to, application servicing requests should subscribe to the request subject using the same queue group name. You can have more than one queue group present on a subject (for example you could have one queue group to distribute the processing of the requests between service instances, and another queue group to distribute the logging or monitoring of the requests being made to the service).
Some applications can make use of the extra functionalities enabled by JetStream (streams, KV Store, Object Store). Just like you use the Core NATS connection object to invoke Core NATS operations, you use a JetStream context to invoke JetStream operations. You can specify things like the timeout value for all the operations executed from the context. JS context are light-weight, so while it is safe to share a JS context between threads, for best performance do not be afraid to have a context per thread.
You can use streams for two broad use cases:
Temporal decoupling: the ability for a subscribing application to get on demand a replay of the messages stored in the stream due to past (and possibly future) publications.
Queuing: the ability for instances of the subscribing application to get, and safely process and remove (i.e. consume) from the stream individual or batches of messages, effectively using a stream as a distributed work queue.
Before you can use a stream to replay or consume messages published on a subject, it must be defined. The stream definition attributes specify
what is being stored (i.e. which subject(s) the stream monitors)
how it is being stored (e.g. file or memory storage, number of replicas)
how long the messages are stored (e.g. depending on limits, or on interest, or as a work queue): the retention policy
Streams can be (and often are) administratively defined ahead of time (for example using the NATS CLI Tool). The application can also manage streams (and consumers) programmatically.
Any message published, on a subject monitored by a stream gets stored in the stream. If your application publishes a message using the Core NATS publish call (from the connection object) on a stream's subject, the message will get stored in the stream, the Core NATS publishers do not know or care whether there is a stream for that subject or not. However, if you know that there is going to be a stream defined for that subject you will get higher quality of service by publishing using the JetStream Context's publish call (rather than the connection's publish call). This is because JetStream publications will receive an acknowledgement (or not) from the NATS Servers when the message has been positively received and stored in the stream (while Core NATS publications are not acknowledged by the NATS Servers). This difference is also the reason why there are both synchronous and asynchronous versions of the JetStream publish operation.
Stream consumers are how application get messages from stream. To make another analogy to database concepts a consumers can be seen as a kind of 'views' (on a stream):
Consumers can have a subject filter to select messages from the stream according to their subject names.
Consumers have an ack policy which defines whether application must acknowledge the reception and processing of the messages being sent to them by the consumers (note that explicit acknowledgements are required for some types of streams and consumer to work properly). As well as how long to wait for acknowledgements for and how many times the consumer should try to re-deliver un-acknowledged messages for.
Consumers have a deliver policy specifying where in the stream the consumer should start delivering messages from.
Consumers have a replay policy to specify the speed at which messages are being replayed at by the consumer.
Consumers also have a small amount of state on the NATS Server to store some message sequence numbers 'cursors'. You can have as many consumers as you need per stream.
Client applications either create ephemeral consumers, or define/find durable consumers. Applications either subscribe to 'push' consumers (consumers defined with a delivery subject and optionally a queue group name for that delivery subject), or fetch on demand (including an optional prefetch) from 'pull' consumers (consumers defined without a delivery subject or queue group name as they don't need any while providing the same functionality).
Ephemeral consumers are, as the name suggest, not meant to last and are automatically cleaned up by the NATS Servers when the application instance that created them shuts down. Ephemeral consumers are created on-demand by individual application instances and are used only by the application instance that created them.
Applications typically use ephemeral ordered push consumers to get they own copy of the messages stored in a stream whenever they want.
Durable consumers are, as the name suggest, meant to be 'always on', and used (shared) by multiple instances of the client application or by applications that get stopped and restarted multiple times and need to maintain state from one run of the application to another.
Durable consumers can be managed administratively using the NATS CLI Tool, or programmatically by the application itself. A consumer is created as a durable consumer simply by specifying a durable name at creation time.
Applications typically use durable pull consumers to distribute and scale horizontally the processing (or consumption) of the messages in a stream.
Some types of consumers (e.g. pull consumers) require the application receiving messages from the consumer to explicitly acknowledge the reception and processing of those messages. The application can invoke one of the following acknowledgement functions on the message received from the consumer:
ack()
to positively acknowledge the reception and processing of the message
term()
to indicate that the message could not be and will never be able to be processed and should not be sent again later. Use term when the request is invalid.
nack()
to negatively acknowledge the processing of the message, indicating that the message should be sent again. Use nack when the request is valid but you are unable to process it. If this inability to process happens because of a temporary condition, you should also close your subscription temporarily until you are able to process again.
inProgress()
to indicate that the processing of the message is still on-going and more time is needed (before the message is considered for being sent again)
Besides temporal decoupling and queuing, JetStream also enables higher qualities of service compared to Core NATS. Defining a stream on a subject and using consumers brings the quality of service up to at least once, meaning that you are guaranteed to get the message (even if your application is down at publication time) but there are some corner case failure scenarios in which you could result in message duplication due to double publication of the message, or double processing of a message due to acknowledgement loss or crashing after processing but before acknowledging. You can enable and use message de-duplication and double-acking to protect against those failure scenarios and get exactly once quality of service.
The Key Value Store functionality is implemented on top of JetStream, but offers a different interface in the form of keys and values rather than subject names and messages. You can use a bucket to put (including compare and set), get and delete a value (a byte array like a message payload) associated with a key (a string, like a subject). It also allows you to 'watch' for changes to the bucket as they happen. And finally it allows you to maintain a history of the values associated with a key over time, as well as get a specific revision of the value.
NOTICE: Technology Preview
The Object Store is similar to the Key Value Store but meant to be used where the values can be of any arbitrary large size, as opposed to limited to the maximum size of a NATS message, as it the case with the Key Value Store.
For this example, start the server using:
You can encrypt passwords to pass to nats-server
using the simple NATS CLI tool:
and use the hashed password in the server config. The client still uses the plain text version.
The code uses localhost:4222 so that you can start the server on your machine to try them out.
When logging in with a password nats-server
will take either a plain text password or an encrypted password.
Most clients make it easy to pass the user name and password by accepting them in the URL for the server. This standard format is:
nats://user:password@server:port
Using this format, you can connect to a server using authentication as easily as you connected with a URL:
The 2.0 version of NATS server introduces a new challenge response authentication option. This challenge response is based on a wrapper we call NKeys. The server can use these keys in several ways for authentication. The simplest is for the server to be configured with a list of known public keys and for the clients to respond to the challenge by signing it with its private key. (A printable private NKey is referred to as seed). This challenge-response ensures security by ensuring that the client has the private key, but also protects the private key from the server, which never has access to it!
Handling challenge response may require more than just a setting in the connection options, depending on the client library.
While authentication limits which clients can connect, TLS can be used to encrypt traffic between client/server and check the server’s identity. Additionally - in the most secure version of TLS with NATS - the server can be configured to verify the client's identity, thus authenticating it. When started in TLS mode, a nats-server
will require all clients to connect with TLS. Moreover, if configured to connect with TLS, client libraries will fail to connect to a server without TLS.
Using TLS to connect to a server that verifies the client's identity is straightforward. The client has to provide a certificate and private key. The NATS client will use these to prove it's identity to the server. For the client to verify the server's identity, the CA certificate is provided as well.
Use example certificates created in self signed certificates for testing.
Clients (such as Go, Java, Javascript, Ruby and Type Script) support providing a URL containing the tls
protocol to the NATS connect call. This will turn on TLS without the need for further code changes. However, in that case there is likely some form of default or environmental settings to allow the TLS libraries of your programming language to find certificate and trusted CAs. Unless these settings are taken into accounts or otherwise modified, this way of connecting is very likely to fail.
The 2.0 version of NATS server introduced the idea of decentralized authentication based on JSON Web Tokens (JWT). Clients interact with this new scheme using a user JWT and corresponding NKey private key. To help make connecting with a JWT easier, the client libraries support the concept of a credentials file. This file contains both the private key and the JWT and can be generated with the nsc
tool. The contents will look like the following and should be protected because it contains a private key. This credentials file is unused and only for example purposes.
Given a creds file, a client can authenticate as a specific user belonging to a specific account:
Each library has its own, language preferred way, to pass connection options. One of the most common options is a connect timeout. It limits how long it can take to establish a connection to a server. Should multiple URLs be provided, this timeout applies to each cluster member individually. To set the maximum time to connect to a server to 10 seconds:
Tokens are basically random strings, much like a password, and can provide a simple authentication mechanism in some situations. However, tokens are only as safe as they are secret so other authentication schemes can provide more security in large installations. It is highly recommended to use one of the other NATS authentication mechanisms.
For this example, start the server using:
The code uses localhost:4222 so that you can start the server on your machine to try them out.
Some client libraries will allow you to pass the token as part of the server URL using the form:
nats://token@server:port
Again, once you construct this URL you can connect as if this was a normal URL.
Applications can set the maximum reconnect attempts per server. This includes the server provided to the client's connect call, as well as the server the client discovered through another server. Once reconnect to a server fails the specified amount of times in a row, it will be removed from the connect list. After a successful reconnect to a server, the client will reset that server's failed reconnect attempt count. If a server was removed from the connect list, it can be rediscovered on connect. This effectively resets the connect attempt count as well. If the client runs out of servers to reconnect, it will close the connection and raise an error.
This section contains miscellaneous functionalities and options for connect.
While the client can't control the maximum payload size, clients may provide a way for applications to obtain the configured max_payload
after the connection is made. This will allow the application to chunk or limit data as needed to pass through the server.
The NATS server provides a pedantic mode that performs extra checks on the protocol.
One example of such a check is if a subject used for publishing contains a wildcard character. The server will not use it as wildcard and therefore omits this check.
By default, this setting is off but you can turn it on to test your application:
The protocol between the client and the server is fairly simple and relies on a control line and sometimes a body. The control line contains the operations being sent, like PING or PONG, followed by a carriage return and line feed, CRLF or "\r\n". The server has a max_control_line
option that can limit the maximum size of a control line. For PING and PONG this doesn't come into play, but for messages that contain subject names and possibly queue group names, the control line length can be important as it effectively limits the possibly combined length. Some clients will try to limit the control line size internally to prevent an error from the server. These clients may or may not allow you to set the size being used, but if they do, the size should be set to match the server configuration.
It is not recommended to set this to a value that is higher than the one of other clients or the nats-server.
For example, to set the maximum control line size to 2k:
Clients can request verbose mode from NATS server. When requested by a client, the server will reply to every message from that client with either a +OK or an error -ERR. However, the client will not block and wait for a response. Errors will be sent without verbose mode as well and client libraries handle them as documented.
This functionality is only used for debugging the client library or the nats-server themselves. By default the server sets it to on, but every client turns it off.
To turn on verbose mode:
When a server goes down, there is a possible anti-pattern called the Thundering Herd where all of the clients try to reconnect immediately, thus creating a denial of service attack. In order to prevent this, most NATS client libraries randomize the servers they attempt to connect to. This setting has no effect if only a single server is used, but in the case of a cluster, randomization, or shuffling, will ensure that no one server bears the brunt of the client reconnect attempts.
However, if you want to disable the randomization process for connect and reconnect, so that servers are always checked in the same order, you can do that in most libraries with a connection option:
You can disable automatic reconnect with connection options:
All the client libraries maintained on the nats.io GitHub page will automatically attempt to re-connect if their current server connection gets disconnected for any reason. Upon re-connection the client library will automatically re-establish all the subscriptions, there is nothing for the application programmer to do.
Unless specifically disabled client will try to re-connect to one of the servers it knows about, either through the URLs provided in the connect
call or the URLs provided by the NATS system during earlier connects. This feature allows NATS applications and the NATS system itself to self-heal and reconfigure itself with no additional configuration or intervention.
You can adjust the wait time between connections attempts, the maximum number of reconnection attempts, and adjust the size of the reconnection buffer.
Your application can register callback to receive events to be notified about the following connection events:
ClosedCB ConnHandler
The ClosedCB handler is called when a client will no longer be connected.
DisconnectedCB ConnHandler
The DisconnectedCB handler is called whenever the connection is disconnected. It will not be called if DisconnectedErrCB is set DEPRECATED: Use DisconnectedErrCB instead which passes error that caused the disconnect event.
DisconnectedErrCB ConnErrHandler
The DisconnectedErrCB handler is called whenever the connection is disconnected. Disconnected error could be nil, for instance when user explicitly closes the connection. NOTE: DisconnectedCB will not be called if DisconnectedErrCB is set
ReconnectedCB ConnHandler
The ReconnectedCB handler is called whenever the connection is successfully reconnected.
DiscoveredServersCB ConnHandler
The DiscoveredServersCB handler is called whenever a new server has joined the cluster.
AsyncErrorCB ErrHandler
The AsyncErrorCB handler is called whenever asynchronous connection errors happen (e.g. slow consumer errors)
Timeout time.Duration
Timeout sets the timeout for a Dial operation on a connection. Default is 2 * time.Second
PingInterval time.Duration
PingInterval is the period at which the client will be sending ping commands to the server, disabled if 0 or negative. Default is 2 * time.Minute
MaxPingsOut int
MaxPingsOut is the maximum number of pending ping commands that can be awaiting a response before raising an ErrStaleConnection error. Default is 2
Besides the error and advisory callbacks mentioned above you can also set a few reconnection attributes in the connection options:
AllowReconnect bool
AllowReconnect enables reconnection logic to be used when we encounter a disconnect from the current server. Default is true
MaxReconnect int
MaxReconnect sets the number of reconnect attempts that will be tried before giving up. If negative, then it will never give up trying to reconnect. Default is 60
ReconnectWait time.Duration
ReconnectWait sets the time to backoff after attempting to (and failing to) reconnect. Default is 2 * time.Second
CustomReconnectDelayCB ReconnectDelayHandler
CustomReconnectDelayCB is invoked after the library tried every URL in the server list and failed to reconnect. It passes to the user the current number of attempts. This function returns the amount of time the library will sleep before attempting to reconnect again. It is strongly recommended that this value contains some jitter to prevent all connections to attempt reconnecting at the same time.
ReconnectJitter time.Duration
ReconnectJitter sets the upper bound for a random delay added to ReconnectWait during a reconnect when no TLS is used. Note that any jitter is capped with ReconnectJitterMax. Default is 100 * time.Millisecond
ReconnectJitterTLS time.Duration
ReconnectJitterTLS sets the upper bound for a random delay added to ReconnectWait during a reconnect when TLS is used. Note that any jitter is capped with ReconnectJitterMax. Default is 1 * time.Second
ReconnectBufSize int
ReconnectBufSize is the size of the backing bufio during reconnect. Once this has been exhausted publish operations will return an error. Default is 8 * 1024 * 1024
RetryOnFailedConnect bool
RetryOnFailedConnect sets the connection in reconnecting state right away if it can't connect to a server in the initial set. The MaxReconnect and ReconnectWait options are used for this process, similarly to when an established connection is disconnected. If a ReconnectHandler is set, it will be invoked on the first successful reconnect attempt (if the initial connect fails), and if a ClosedHandler is set, it will be invoked if it fails to connect (after exhausting the MaxReconnect attempts). Default is false
It doesn’t make much sense to try to connect to the same server over and over. To prevent this sort of thrashing, and wasted reconnect attempts, especially when using TLS, libraries provide a wait setting. Generally clients make sure that between two reconnect attempts to the same server at least a certain amount of time has passed. The concrete implementation depends on the library used.
This setting not only prevents wasting client resources, it also alleviates a thundering herd situation when additional servers are not available.
The Core NATS client libraries try as much as possible to be fire and forget, and you should use JetStream functionalities to get higher qualities of service that can deal with Core NATS messages being dropped due to the server connection being interrupted. That said, one of the features that may be included in the library you are using is the ability to buffer outgoing messages when the connection is down.
During a short reconnect, the client can allow applications to publish messages that, because the server is offline, will be cached in the client. The library will then send those messages once reconnected. When the maximum reconnect buffer is reached, messages will no longer be publishable by the client and an error will be returned.
Be aware, while the message appears to be sent to the application it is possible that it is never sent because the connection is never remade. Your applications should use patterns like acknowledgements or use the JetStream publish call to ensure delivery.
For clients that support this feature, you are able to configure the size of this buffer with bytes, messages or both.
As mentioned throughout this document, each client library may behave slightly differently. Please check the documentation for the library you are using.
Managing the interaction with the server is primarily the job of the client library but most of the libraries also provide some insight into what is happening under the covers.
For example, the client library may provide a mechanism to get the connection's current status:
Because reconnect is primarily under the covers, many libraries provide an event listener you can use to be notified of reconnect events. This event can be especially important for applications sending a lot of messages.
In general, applications can receive messages asynchronously or synchronously. Receiving messages with NATS can be library dependent.
Some languages, like Go or Java, provide synchronous and asynchronous APIs, while others may only support one type of subscription.
In all cases, the process of subscribing involves having the client library tell the NATS system that an application is interested in a particular subject. When an application is done with a subscription it unsubscribes telling the server to stop sending messages.
A client will receive a message for each matching subscription, so if a connection has multiple subscriptions using identical or overlapping subjects (say foo
and >
) the same message will be sent to the client multiple times.
Note: The client API (asynchronous) subscribe call can return before the subscription is actually fully established at the nats-server. Call Flush()
on the connection right after you call subscribe if you need to synchronize with the subscription being ready at the server level.
NATS is designed to move messages through the server quickly. As a result, NATS depends on the applications to consider and respond to changing message rates. The server will do a bit of impedance matching, but if a client is too slow the server will eventually cut them off by closing the connection. These cut off connections are called slow consumers.
One way some of the libraries deal with bursty message traffic is to buffer incoming messages for a subscription. So if an application can handle 10 messages per second and sometimes receives 20 messages per second, the library may hold the extra 10 to give the application time to catch up. To the server, the application will appear to be handling the messages and consider the connection healthy. Most client libraries will notify the application that there is a SlowConsumer error and discard messages.
Receiving and dropping messages from the server keeps the connection to the server healthy, but creates an application requirement. There are several common patterns:
Use request-reply to throttle the sender and prevent overloading the subscriber
Use a queue with multiple subscribers splitting the work
Persist messages with something like NATS streaming
Libraries that cache incoming messages may provide two controls on the incoming queue, or pending messages. These are useful if the problem is bursty publishers and not a continuous performance mismatch. Disabling these limits can be dangerous in production and although setting these limits to 0 may help find problems, it is also a dangerous proposition in production.
Check your libraries documentation for the default settings, and support for disabling these limits.
The incoming cache is usually per subscriber, but again, check the specific documentation for your client library.
The first way that the incoming queue can be limited is by message count. The second way to limit the incoming queue is by total size. For example, to limit the incoming cache to 1,000 messages or 5mb whichever comes first:
When a slow consumer is detected and messages are about to be dropped, the library may notify the application. This process may be similar to other errors or may involve a custom callback.
Some libraries, like Java, will not send this notification on every dropped message because that could be noisy. Rather the notification may be sent once per time the subscriber gets behind. Libraries may also provide a way to get a count of dropped messages so that applications can at least detect a problem is occurring.
NATS client applications use a PING/PONG protocol to check that there is a working connection to the NATS service. Periodically the client will send PING messages to the server, which responds with a PONG. This period is configured by specifying a ping interval on the client connection settings.
The connection will be closed as stale when the client reaches a number of pings which recieved no pong in response, which is configured by specifying the maximum pings outstanding on the client connection settings.
The ping interval and the maximum pings outstanding work together to specify how quickly the client connection will be notified of a problem. This will also help when there is a remote network partition where the operating system does not detect a socket error. Upon connection close, the client will attempt to reconnect. When it knows about other servers, these will be tried next.
In the presence of traffic, such as messages or client side pings, the server will not initiate the PING/PONG interaction.
On connections with significant traffic, the client will often figure out there is a problem between PINGS, and as a result the default ping interval is typically on the order of minutes. To close an unresponsive connection after 100s, set the ping interval to 20s and the maximum pings outstanding to 5:
By default a NATS connection will echo messages if the connection also has interest in the published subject. This means that if a publisher on a connection sends a message to a subject any subscribers on that same connection will receive the message. Clients can opt to turn off this behavior, such that regardless of interest, the message will not be delivered to subscribers on the same connection.
The NoEcho option can be useful in BUS patterns where all applications subscribe and publish to the same subject. Usually a publish represents a state change that the application already knows about, so in the case where the application publishes an update it does not need to process the update itself.
Keep in mind that each connection will have to turn off echo, and that it is per connection, not per application. Also, turning echo on and off can result in a major change to your applications communications protocol since messages will flow or stop flowing based on this setting and the subscribing code won't have any indication as to why.
The client libraries provide a means to unsubscribe a previous subscription request.
This process requires an interaction with the server, so for an asynchronous subscription there may be a small window of time where a message comes through as the unsubscribe is processed by the library. Ignoring that slight edge case, the client library will clean up any outstanding messages and tell the server that the subscription is no longer used.
Asynchronous subscriptions use callbacks of some form to notify an application when a message arrives. These subscriptions are usually easier to work with, but do represent some form of internal work and resource usage, i.e. threads, by the library. Check your library's documentation for any resource usage associated with asynchronous subscriptions.
Note: For a given subscription, messages are dispatched serially, one message at a time. If your application does not care about processing ordering and would prefer the messages to be dispatched concurrently, it is the application's responsibility to move them to some internal queue to be picked up by threads/go routines.
The following example subscribes to the subject updates
and handles the incoming messages:
Synchronous subscriptions require the application to wait for messages. This type of subscription is easy to set-up and use, but requires the application to deal with looping if multiple messages are expected. For situations where a single message is expected, synchronous subscriptions are sometimes easier to manage, depending on the language.
For example, to subscribe to the subject updates
and receive a single message you could do:
Incoming messages have an optional reply-to field. If that field is set, it will contain a subject to which a reply is expected.
For example, the following code will listen for that request and respond with the time.
While the connection status is interesting, it is perhaps more interesting to know when the status changes. Most, if not all, of the NATS client libraries provide a way to listen for events related to the connection and its status.
The actual API for these listeners is language dependent, but the following examples show a few of the more common use cases. See the API documentation for the client library you are using for more specific instructions.
Connection events may include the connection being closed, disconnected or reconnected. Reconnecting involves a disconnect and connect, but depending on the library implementation may also include multiple disconnects as the client tries to find a server, or the server is rebooted.
When working with a cluster, servers may be added or changed. Some of the clients allow you to listen for this notification:
The client library may separate server-to-client errors from events. Many server events are not handled by application code and result in the connection being closed. Listening for the errors can be very useful for debugging problems.
NATS provides a special form of unsubscribe that is configured with a message count and takes effect when that many messages are sent to a subscriber. This mechanism is very useful if only a single message is expected.
The message count you provide is the total message count for a subscriber. So if you unsubscribe with a count of 1, the server will stop sending messages to that subscription after it has received one message. If the subscriber has already received one or more messages, the unsubscribe will be immediate. This action based on history can be confusing if you try to auto-unsubscribe on a long running subscription, but is logical for a new one.
Auto-unsubscribe is based on the total messages sent to a subscriber, not just the new ones. Most of the client libraries also track the max message count after an auto-unsubscribe request. On reconnect, this enables clients to resend the unsubscribe with an updated total.
The following example shows unsubscribe after a single message:
For example, you can subscribe using *
and then act based on the actual subject.
or do something similar with >
:
The following example can be used to test these two subscribers. The *
subscriber should receive at most 2 messages, while the >
subscriber receives 4. More importantly the time.*.east
subscriber won't receive on time.us.east.atlanta
because that won't match.
Keep in mind that queue groups in NATS are dynamic and do not require any server configuration.
As an example, to subscribe to the queue workers
with the subject updates
:
If you run this example with the publish examples that send to updates
, you will see that one of the instances gets a message while the others you run won't. But the instance that receives the message will change.
There is no special code to subscribe with a . Wildcards are a normal part of the subject name. However, it is a common technique to use the subject provided with the incoming message to determine what to do with the message.
Subscribing to a is only slightly different than subscribing to a subject alone. The application simply includes a queue name with the subscription. The server will load balance between all members of the queue group. In a cluster setup, every member has the same chance of receiving a particular message.
For performance reasons, most if not all, of the client libraries will buffer outgoing data so that bigger chunks can be written to the network at one time. This may be as simple as a byte buffer that stores a few messages before being pushed to the network.
These buffers do not hold messages forever, generally they are designed to hold messages in high throughput scenarios, while still providing good latency in low throughput situations.
It is the libraries job to make sure messages flow in a high performance manner. But there may be times when an application needs to know that a message has "hit the wire." In this case, applications can use a flush call to tell the library to move data through the system.
Many of the client libraries use the PING/PONG interaction built into the NATS protocol to ensure that flush pushed all of the buffered messages to the server. When an application calls flush, most libraries will put a PING on the outgoing queue of messages, and wait for the server to respond with a PONG before saying that the flush was successful.
Even though the client may use PING/PONG for flush, pings sent this way do not count towards max outgoing pings.
This feature is the ability to drain connections or subscriptions and then close the connection. Closing a connection (using close()
), or unsubscribing from a subscription, are generally considered immediate requests. When you close or unsubscribe the library will halt messages in any pending queue or cache for subscribers. When you drain a subscription or connection, it will process any inflight and cached/pending messages before closing.
Drain provides clients that use queue subscriptions with a way to bring down applications without losing any messages. A client can bring up a new queue member, drain and shut down the old queue member, all without losing messages sent to the old client. Without drain, there is the possibility of lost messages due to delivery timing.
The libraries can provide drain on a connection or on a subscriber, or both.
For a connection the process is essentially:
Drain all subscriptions
Stop new messages from being published
Flush any remaining published messages
Close
The API for drain can generally be used instead of close:
As an example of draining a connection:
The mechanics of drain for a subscription are simpler:
Unsubscribe
Process all cached or inflight messages
Clean up
The API for drain can generally be used instead of unsubscribe:
Because draining can involve messages flowing to the server, for a flush and asynchronous message processing, the timeout for drain should generally be higher than the timeout for a simple message request-reply or similar.
The optional reply-to field when publishing a message can be used on the receiving side to respond. The reply-to subject is often called an inbox, and most libraries may provide a method for generating unique inbox subjects. Most libraries also provide for the request-reply pattern with a single call. For example to send a request to the subject time
, with no content for the messages, you might:
The pattern of sending a message and receiving a response is encapsulated in most client libraries into a request method. Under the covers this method will publish a message with a unique reply-to subject and wait for the response before returning.
In the older versions of some libraries a completely new reply-to subject is created each time. In newer versions, a subject hierarchy is used so that a single subscriber in the client library listens for a wildcard, and requests are sent with a unique child subject of a single subject.
The primary difference between the request method and publishing with a reply-to is that the library is only going to accept one response, and in most libraries the request will be treated as a synchronous action. The library may even provide a way to set the timeout.
For example, updating the previous publish example we may request time
with a one second timeout:
You can think of request-reply in the library as a subscribe, get one message, unsubscribe pattern. In Go this might look something like:
You can expand the request-reply pattern into something often called scatter-gather. To receive multiple messages, with a timeout, you could do something like the following, where the loop getting messages is using time as the limitation, not the receipt of a single message:
Or, you can loop on a counter and a timeout to try to get at least N responses:
Client libraries may provide tools to help receive structured data, like JSON. The core traffic to the NATS server will always be opaque byte arrays. The server does not process message payloads in any form. For libraries that don't provide helpers, you can always encode and decode data before sending the associated bytes to the NATS client.
For example, to receive JSON you could do:
Recently we have agreed upon an initial specification for a services protocol so that we can add first-class services support to NATS clients and support this in our tooling. This services protocol is an agreement between clients and tooling and doesn't require any special functionality from the NATS server or JetStream.
To check if the NATS client in your favorite language supports the new services API, make sure you check the docs and GitHub repository for that client. The services API is relatively new and not all clients may support it yet.
To see the services API in action in different languages, take a look at the NATS By Example samples.
There are a few high level concepts in the services API worth understanding before you start developing your own services.
The service is the highest level abstraction and refers to a group of logically related functionality. Services are required to have names and versions that conform to the semver rules. Services are discoverable within a NATS system.
A service endpoint is the entity with which clients interact. You can think of an endpoint as a single operation within a service. All services must have at least 1 endpoint.
A group is a collection of endpoints. These are optional and can provide a logical association between endpoints as well as an optional common subject prefix for all endpoints.
The services API supports 3 operations for discoverability and observability. While the NATS client will take care of responding on these subjects, it is still the developer's responsibility to respond to requests made the service's actual endpoints.
PING
- Requests made on the $SRV.PING.>
subject gather replies from running services. This facilitates service listing by tooling.
STATS
- Requests made on the $SRV.STATS.>
subject query statistics from services. Available stats include total requests, total errors, and total processing time.
INFO
- Requests made on the $SRV.INFO.>
subject obtain the service definition and metadata, including groups, endpoints, etc.
Some client libraries provide helpers to send structured data while others depend on the application to perform any encoding and decoding and just take byte arrays for sending. The following example shows how to send JSON but this could easily be altered to send a protocol buffer, YAML or some other format. JSON is a text format so we also have to encode the string in most languages to bytes. We are using UTF-8, the JSON standard encoding.
Take a simple stock ticker that sends the symbol and price of each stock:
NATS sends and receives messages using a protocol that includes a target subject, an optional reply subject and an array of bytes. Some libraries may provide helpers to convert other data formats to and from bytes, but the NATS server will treat all messages as opaque byte arrays.
All of the NATS clients are designed to make sending a message simple. For example, to send the string “All is Well” to the “updates” subject as a UTF-8 string of bytes you would do:
As the Key Value Store is built on top of the JetStream persistence layer you obtain a KeyValueManager object from your JetStream context.
The key must be in the same format as a NATS subject, i.e. it can be a dot separated list of tokens (which means that you can then use wildcards to match hierarchies of keys when watching a bucket), and can only contain valid characters. The value can be any byte array.
You can create as many independent key/value store instance, called 'buckets', as you need. Buckets are typically created, purged or deleted administratively (e.g. using the nats
CLI tool), but this can also be done using one of the following KeyValueManager calls:
You can do a get to get the current value on a key, or ask to get a specific revision of the value.
The key is always a string, you can simply use Put to store a byte array, or the convenience PutString
to put a string. For 'compare and set' functionality you can use Create
and Update
.
You can delete a specific key, or purge the whole key/value bucket.
You can get the list of all the keys currently having a value associated using Keys()
The JetStream key/value store has a feature you don't usually find in key/value stores: the ability to keep a history of the values associated with a key (rather than just the current value). The depth of the history is specified when the key/value bucket is created, and the default is a history depth of 1 (i.e. no history).
Watching a key/value bucket is like subscribing to updates: you provide a callback and you can watch all of the keys in the bucket or specify which specific key(s) you want to be kept updated about.
Streams store data on disk, but we cannot store all data forever, so we need ways to control their size automatically.
There are 3 features that come into play when Streams decide how long they store data.
The Retention Policy
describes based on what criteria a set will evict messages from its storage:
LimitsPolicy
Limits are set for how many messages, how big the storage and how old messages may be.
WorkQueuePolicy
Messages are kept until they are consumed: meaning delivered ( by the consumer filtering on the message's subject (in this mode of operation you can not have any overlapping consumers defined on the Stream - each subject captured by the stream can only have one consumer at a time)) to a subscribing application and explicitly acknowledged by that application.
InterestPolicy
Messages are kept as long as there are Consumers on the stream (matching the message's subject if they are filtered consumers) for which the message has not yet been ACKed. Once all currently defined consumers have received explicit acknowledgement from a subscribing application for the message it is then removed from the stream.
In all Retention Policies the basic limits apply as upper bounds, these are MaxMsgs
for how many messages are kept in total, MaxBytes
for how big the set can be in total and MaxAge
for what is the oldest message that will be kept. These are the only limits in play with LimitsPolicy
retention.
One can then define additional ways a message may be removed from the Stream earlier than these limits. In WorkQueuePolicy
the messages will be removed as soon as the Consumer received an Acknowledgement. In InterestPolicy
messages will be removed as soon as all Consumers of the stream for that subject have received an Acknowledgement for the message.
In both WorkQueuePolicy
and InterestPolicy
the age, size and count limits will still apply as upper bounds.
A final control is the Maximum Size any single message may have. NATS have it's own limit for maximum size (1 MiB by default), but you can say a Stream will only accept messages up to 1024 bytes using MaxMsgSize
.
The Discard Policy
sets how messages are discarded when limits set by LimitsPolicy
are reached. The DiscardOld
option removes old messages making space for new, while DiscardNew
refuses any new messages.
The WorkQueuePolicy
mode is a specialized mode where a message, once consumed and acknowledged, is removed from the Stream.
JetStream support idempotent message writes by ignoring duplicate messages as indicated by the Nats-Msg-Id
header.
Here we set a Nats-Msg-Id:1
header which tells JetStream to ensure we do not have duplicates of this message - we only consult the message ID not the body.
and in the output you can see that the duplicate publications were detected and only one message (the first one) is actually stored in the stream
The default window to track duplicates in is 2 minutes, this can be set on the command line using --dupe-window
when creating a stream, though we would caution against large windows.
Streams support acknowledging receiving a message, if you send a Request()
to a subject covered by the configuration of the Stream the service will reply to you once it stored the message. If you just publish, it will not. A Stream can be set to disable Acknowledgements by setting NoAck
to true
in it's configuration.
Consumers have 3 acknowledgement modes:
AckExplicit
This requires every message to be specifically acknowledged, it's the only supported option for pull-based Consumers
AckAll
In this mode if you acknowledge message 100
it will also acknowledge message 1
-99
, this is good for processing batches and to reduce ack overhead
AckNone
No acknowledgements are supported
To understand how Consumers track messages we will start with a clean ORDERS
Stream and DISPATCH
Consumer.
The Set is entirely empty
The Consumer has no messages outstanding and has never had any (Consumer sequence is 1).
We publish one message to the Stream and see that the Stream received it:
As the Consumer is pull-based, we can fetch the message, ack it, and check the Consumer state:
The message got delivered and acknowledged - Acknowledgement floor
is 1
and 1
, the sequence of the Consumer is 2
which means its had only the one message through and got acked. Since it was acked, nothing is pending or redelivering.
We'll publish another message, fetch it but not Ack it this time and see the status:
Get the next message from the consumer (but do not acknowledge it)
Show the consumer info
Now we can see the Consumer has processed 2 messages (obs sequence is 3, next message will be 3) but the Ack floor is still 1 - thus 1 message is pending acknowledgement. Indeed this is confirmed in the Pending messages
.
If I fetch it again and again do not ack it:
Show the consumer info again
The Consumer sequence increases - each delivery attempt increases the sequence - and our redelivered count also goes up.
Finally, if I then fetch it again and ack it this time:
Show the consumer info
Having now Acked the message there are no more pending.
Additionally, there are a few types of acknowledgements:
AckAck
nil, +ACK
Acknowledges a message was completely handled
AckNak
-NAK
Signals that the message will not be processed now and processing can move onto the next message, NAK'd message will be retried
AckProgress
+WPI
When sent before the AckWait period indicates that work is ongoing and the period should be extended by another equal to AckWait
AckNext
+NXT
Acknowledges the message was handled and requests delivery of the next message to the reply subject. Only applies to Pull-mode.
AckTerm
+TERM
Instructs the server to stop redelivery of a message without acknowledging it as successfully processed
So far all of the examples were the AckAck
type of acknowledgement, by replying to the Ack with the body as indicated in Bytes
you can pick what mode of acknowledgement you want. Note that this description is documenting the internal JetStream protocol. Client libraries offer APIs for performing all the above acknowledgments using specific APIs where you don't worry about the internal protocol payloads.
All of these acknowledgement modes, except AckNext
, support double acknowledgement - if you set a reply subject when acknowledging the server will in turn acknowledge having received your ACK.
The +NXT
acknowledgement can have a few formats: +NXT 10
requests 10 messages and +NXT {"no_wait": true}
which is the same data that can be sent in a Pull Request.
JetStream supports Exactly Once publication and consumption by combining Message Deduplication and double acks.
On the publishing side you can avoid duplicate message ingestion using the Message Deduplication feature.
Consumers can be 100% sure a message was correctly processed by requesting the server Acknowledge having received your acknowledgement (sometimes referred to as double-acking) by calling the message's AckSync()
(rather than Ack()
) function which sets a reply subject on the Ack and waits for a response from the server on the reception and processing of the acknowledgement. If the response received from the server indicates success you can be sure that the message will never be re-delivered by the consumer (due to a loss of your acknowledgement).
When setting up a Consumer you can decide where to start, the system supports the following for the DeliverPolicy
:
all
Delivers all messages that are available
last
Delivers the latest message, like a tail -n 1 -f
new
Delivers only new messages that arrive after subscribe time
by_start_time
Delivers from a specific time onward. Requires OptStartTime
to be set
by_start_sequence
Delivers from a specific stream sequence. Requires OptStartSeq
to be set
Regardless of what mode you set, this is only the starting point. Once started it will always give you what you have not seen or acknowledged. So this is merely how it picks the very first message.
Let's look at each of these, first we make a new Stream ORDERS
and add 100 messages to it.
Now create a DeliverAll
pull-based Consumer:
Now create a DeliverLast
pull-based Consumer:
Now create a MsgSetSeq
pull-based Consumer:
And finally a time-based Consumer. Let's add some messages a minute apart:
Then create a Consumer that starts 2 minutes ago:
So far, all the Consumers you have seen were Durable, meaning they exist even after you disconnect from JetStream. In our Orders scenario, though the MONITOR
a Consumer could very well be a short-lived thing there just while an operator is debugging the system, there is no need to remember the last seen position if all you are doing is wanting to observe the real-time state.
In this case, we can make an Ephemeral Consumer by first subscribing to the delivery subject, then creating a durable and giving it no durable name. An Ephemeral Consumer exists as long as any subscription is active on its delivery subject. It is automatically be removed, after a short grace period to handle restarts, when there are no subscribers.
Terminal 1:
Terminal 2:
The --ephemeral
switch tells the system to make an Ephemeral Consumer.
Typically, what you want is if a new Consumer is made the selected messages are delivered to you as quickly as possible. You might want to replay messages at the rate they arrived though, meaning if messages first arrived 1 minute apart, and you make a new Consumer it will get the messages a minute apart.
This is useful in load testing scenarios etc. This is called the ReplayPolicy
and have values of ReplayInstant
and ReplayOriginal
.
You can only set ReplayPolicy
on push-based Consumers.
Now let's publish messages into the Set 10 seconds apart:
And when we consume them they will come to us 10 seconds apart:
In the earlier sections we saw that samples are being sent to a monitoring system. Let's look at that in depth; how the monitoring system works and what it contains.
As messages pass through a Consumer you'd be interested in knowing how many are being redelivered and how many times but also how long it takes for messages to be acknowledged.
Consumers can sample Ack'ed messages for you and publish samples so your monitoring system can observe the health of a Consumer. We will add support for this to NATS Surveyor.
You can configure a Consumer for sampling bypassing the --sample 80
option to nats consumer add
, this tells the system to sample 80% of Acknowledgements.
When viewing info of a Consumer you can tell if it's sampled or not:
Output contains
JetStream file storage is very efficient, storing as little extra information about the message as possible.
We do store some message data with each message, namely:
Message headers
The subject it was received on
The time it was received
The message payload
A hash of the message
The message sequence
A few other bits like the length of the subject and the length of headers
Without any headers the size is:
A 5 byte hello
message without headers will take 39 bytes.
With headers:
So if you are publishing many small messages the overhead will be, relatively speaking, quite large, but for larger messages the overhead is very small. If you publish many small messages it's worth trying to optimize the subject length.
Streams and durable consumers can be defined administratively outside the application (typically using the NATS CLI Tool) in which case the application only needs to know about the well-known names of the durable consumers it wants to use. But you can also manage streams and consumers programmatically.
Common stream management operations are:
Add a stream. Adding a stream is an idempotent function, which means that if a stream does not exist, it will be created, and if a stream already exists, then the add operation will succeed only if the existing stream matches exactly the attributes specified in the 'add' call.
Delete a stream.
Purge a stream (delete all the messages stored in the stream)
Get or remove a specific message from a stream by sequence number
Add or update (or delete) a consumer
Get info and statistics on streams/consumers/account. Get/remove/get information on individual messages stored in a stream.
In modern systems, applications can expose services or produce and consume data streams. A basic aspect of publish-subscribe messaging is temporal coupling: the subscribers need to be up and running to receive the message when it is published. At a high level, if observability is required, applications need to consume messages in the future, need to consume at their own pace, or need all messages, then JetStream's streaming functionalities provide the temporal de-coupling between publishers and consumers.
Using streaming and its associated higher qualities of service is the facet of messaging with the highest cost in terms of compute and storage.
Streaming is ideal when:
Data producers and consumers are highly decoupled. They may be online at different times and consumers must receive messages.
A historical record of the data in the stream is required. This is when a replay of data is required by a consumer.
The last message on a stream is required for initialization and the producer may be offline.
A-priori knowledge of consumers is not available, but consumers must receive messages. This is often a false assumption.
The data in messages being sent have a lifespan beyond that of the intended application lifespan.
Applications need to consume data at their own pace.
You want decoupled flow control between the publishers and the consumers of the stream
You need 'exactly once' quality of service with de-duplication of publications and double-acknowledged consumption
Note that no assumptions should ever be made of who will receive and process data in the future, or for what purpose.
Using core NATS is ideal as the fast request path for scalable services where there is tolerance for message loss or when applications themselves handle message delivery guarantees.
These include:
Service patterns where there is a tightly coupled request-reply
A request is made, and the application handles error cases upon timeout
(resends, errors, etc). __Relying on a messaging system to resend here is
considered an anti-pattern.__
Where only the last message received is important and new messages will be received frequently enough for applications to tolerate a lost message. This might be a stock ticker stream, frequent exchange of messages in a service control plane, or device telemetry.
Message TTL is low, where the value of the data being transmitted degrades or expires quickly.
The expected consumer set for a message is available a-priori and consumers are expected to be live. The request-reply pattern works well here or consumers can send an application level acknowledgement.
Control plane messages.
You can use 'Add Stream' to idempotently define streams and their attributes (i.e. source subjects, retention and storage policies, limits)
You can use 'Purge' to purge the messages in a stream
You can use 'Delete' to delete a stream
There is interoperability between 'Core NATS' and JetStream in the fact that the streams are listening to core NATS messages. However you will notice that the NATS client libraries' JetStream calls include some 'Publish' calls and so may be wondering what is the difference between a 'Core NATS Publish' and a 'JetStream Publish'.
So yes, when a 'Core NATS' application publishes a message on a Stream's subject, that message will indeed get stored in the stream, but that's not really the intent as you are then publishing with the lower quality of service provided by Core NATS. So, while it will definitely work to just use the Core NATS Publish call to publish to a stream, look at it more as a convenience that you can use to help ease the migration of your applications to use streaming rather the desired end state or ideal design.
Instead, it is better for applications to use the JetStream Publish calls (which Core NATS subscribers not using Streams will still receive like any other publication) when publishing to a stream as:
JetStream publish calls are acknowledged by the JetStream enabled servers, which allows for the following higher qualities of service
If the publisher receives the acknowledgement from the server it can safely discard any state it has for that publication, the message has not only been received correctly by the server, but it has also been successfully persisted.
Whether you use the synchronous or the asynchronous JetStream publish calls, there is an implied flow control between the publisher and the JetStream infrastructure.
You can have 'exactly-once' quality of service by the JetStream publishing application inserting a unique publication ID in a header field of the message.
You can create push or pull consumers:
Push consumers (specifically ordered push consumers) are the best way for an application to receive its own complete copy of the selected messages in the stream.
Pull consumers are the best way to scale horizontally the processing (or consuming) of the selected messages in the stream using multiple client applications sharing the same pull consumer, and allow for the processing of messages in batches.
Consumers can be ephemeral or durable, and support different sets of acknowledgement policies; none, this sequence number, this sequence number and all before it.
You select which of the messages in the stream you want to have delivered to your consumer
all
from a sequence number
from a point in time
the last message
the last message(s) for all the subject(s) in the stream
And you can select the replay speed to be instant or to match the initial publication rate into the stream
Some consumers require the client application code to acknowledge the processing or consumption of the message, but there is more than one way to acknowledge (or not) a message
Ack
Acknowledges a message was completely handled
Nak
Signals that the message will not be processed now and processing can move onto the next message, NAK'd message will be retried
InProgress
When sent before the AckWait period indicates that work is ongoing and the period should be extended by another equal to AckWait
Term
Instructs the server to stop redelivery of a message without acknowledging it as successfully processed
Java
Consumers are how client applications get the messages stored in the streams. You can have many consumers on a single stream. Consumers are like a view on a stream, can filter messages and have some state (maintained by the servers) associated with them.
Consumers can be 'durable' or 'ephemeral'.
Durable consumer persist message delivery progress on the server side. A durable consumer can be retrieved by name and shared between client instance for load balancing. It can be made highly available through replicas.
An ephemeral consumer does not persist delivery progress and will automatically be deleted when there are no more client instances connected.
Durable consumers are meant to be used by multiple instances of an application, either to distribute and scale out the processing, or to persist the position of the consumer over the stream between runs of an application.
Durable consumers as the name implies are meant to last 'forever' and are typically created and deleted administratively rather than by the application code which only needs to specify the durable's well known name to use it.
You create a durable consumer using the nats consumer add
CLI tool command, or programmatically by passing a durable name option to the subscription creation call.
Ephemeral consumers are meant to be used by a single instance of an application (e.g. to get its own replay of the messages in the stream).
Ephemeral consumers are not meant to last 'forever', they are defined automatically at subscription time by the client library and disappear after the application disconnect.
You (automatically) create an ephemeral consumer when you call the js.Subscribe function without specifying the Durable or Bind subscription options. Calling Drain on that subscription automatically deletes the underlying ephemeral consumer. You can also explicitly create an ephemeral consumer by not passing a durable name option to the jsm.AddConsumer call.
Ephemeral consumers otherwise have the same control over message acknowledged and re-delivery as durable consumers.
Clients implement two implementations of consumers identified as 'push' or 'pull'.
Push consumers receive messages on a specific subject where message flow is controlled by the server. Load balancing is supported through NATS core queue groups. The messages from the stream are distributed automatically between the subscribing clients to the push consumers.
Pull consumers request messages explicitly from the server in batches, giving the client full control over dispatching, flow control, pending (unacknowledged) messages and load balancing. Pull consuming client make fetch()
calls in a dispatch loop.
We recommend using pull consumers for new projects. In particular when scalability, detailed flow control or error handling are a design focus. Most client API have been updated to provide convenient interfaces for consuming messages through callback handler or iterators without the need to manage message retrieval.
fetch()
calls can be immediate or have a defined timeout, allowing for either controlled (1 by 1) consumption or realtime
delivery with minimal polling overhead.
Pull consumers create less CPU load on the NATS servers and therefore scale better (note that the push consumers are still quite fast and scalable, you may only notice the difference between the two if you have sustained high message rates).
A push consumer can also be used in some other use cases such as without a queue group, or with no acknowledgement or cumulative acknowledgements.
Ordered consumers are a convenient form of ephemeral push consumer for applications, that want to efficiently consume a stream for data inspection or analysis.
The API consumer is guaranteed delivery of messages in sequence and without gaps.
Always ephemeral - minimal overhead for the server
Single threaded in sequence dispatching
Client checks message sequence and will prevent gaps in the delivery
Can recover from server node failure and reconnect
Does not recover from client failure as it is ephemeral
JetStream consumers can ensure not just the reliability of message delivery but also the reliability of the processing of the messages, even in the face of client application or downstream failures. It does so by using message level acknowledgements and message re-deliveries.
Applications can:
Acknowledge the successfull processing of a message (Ack()
).
Acknowledge the successfull processing of a message and request an acknowledgement of the reception of the acknowledgement by the consumer (AckSync()
).
Indicate that the processing is still in progress and more time is needed (inProgress()
).
Negatively acknowledge a message, indicating that the client application is currently (temporarily) unable to process the message and that the consumer should attempt to re-deliver it (Nak()
).
Terminate a message (typically, because there is a problem with the data inside the message such that the client application is never going to be able to process it), indicating that the consumer should not attempt to re-deliver the message (Term()
).
After a message is sent from the consumer to a subscribing client application by the server an 'AckWait' timer is started. This timer is deleted when either a positive (Ack()
) or a termination (Term()
) acknowledgement is received from the client application. The timer gets reset upon reception of an in-progress (inProgress()
) acknowledgement.
If at the end of a period of time no acknowledgement has been received from the client application, the server will attempt to re-deliver the message. If there is more than one client application instance subscribing to the consumer, there is no guarantee that the re-delivery would be to any particular client instance.
You can control the timing of re-deliveries using either the single AckWait
duration attribute of the consumer, or as a sequence of durations in the BackOff
attribute (which overrides AckWait
).
You can also control the timing of re-deliveries when messages are negatively acknowledged with Nak()
, by passing a nakDelay()
option (or using NakWithDelay()
), otherwise the re-delivery attempt will happen right after the reception of the Nak by the server.
You can set a maximum number of delivery attempts using the consumer's MaxDeliver
setting.
Whenever a message reaches its maximum number of delivery attempts an advisory message is published on the $JS.EVENT.ADVISORY.CONSUMER.MAX_DELIVERIES.<STREAM>.<CONSUMER>
subject. The advisory message's payload (use nats schema info io.nats.jetstream.advisory.v1.max_deliver
for specific information) contains a stream_seq
field that contains the sequence number of the message in the stream.
Similarly, whenever a client application terminates delivery attempts for the message using AckTerm
an advisory message is published on the $JS.EVENT.ADVISORY.CONSUMER.MSG_TERMINATED.<STREAM>.<CONSUMER>
subject, and its payload (see nats schema info io.nats.jetstream.advisory.v1.terminated
) contains a stream_seq
field.
You can leverage those advisory messages to implement "Dead Letter Queue" (DLQ) types of functionalities. For example:
If you only need to know about each time a message is 'dead' (considered un-re-deliverable by the consumer), then listening to the advisories is enough.
If you also need to have access to the message in question then you can use the message's sequence number included in the advisory to retrieve that specific message by sequence number from the stream. If a message reaches its maximum level of delivery attempts, it will still stay in the stream until it is manually deleted or manually acknowledged.
are 'views' into a stream, with their own cursor. They are how client applications get messages from a stream (i.e. 'replayed') for processing or consumption. They can filter messages in the stream according to a 'filtering subject' and define which part of the stream is replayed according to a 'replay policy'.
Client applications 'subscribe' from consumers using the JetStream's Subscribe, QueueSubscribe or PullSubscribe (and variations) calls. Note that since the initial release of JetStream, clients have developed a more ergonomic API to work with to process messages.
Consumers have an specifying the level of reliability required. In increasing order of reliability the available policies are: 'none' for no application level acknowledgements, 'all' where acknowledging a specific message also implicitly acknowledges all previous messages in the stream, and 'explicit' where each message must be individually acknowledged.
When the consumer is set to require explicit acknowledgements the client applications are able to use more than one kind of to indicate successful (or not) reception and processing of the messages being received from the consumer.
The Object Store allows you to store data of any (i.e. large) size by implementing a chunking mechanism, allowing you to for example store and retrieve files (i.e. the object) of any size by associating them with a path and a file name (i.e. the key). You obtain a ObjectStoreManager object from your JetStream context.
See more at jetstream/object.go