A Java client for the NATS messaging system.
- Java API Docs - the latest Java API docs.
- NATS by Example is an evolving collection of runnable, cross-client reference examples for NATS.
- The examples directory covers basic api use.
- The Java Nats Examples GitHub repo, a collection of simple use case examples.
- Java Orbit is a set of independent utilities or extensions for this client.
- Simplification
- Service Framework
- Subject Validation
- Recent Version Notes
- Installation
- Basic Usage
- Connection Options
- JetStream
- Connection Security
- Manual Pull Subscriptions
- Version Notes
- Benchmarking
- Building From Source
- Examples
- License
There is a new simplified api that makes working with streams and consumers well, simpler! Simplification is released as of 2.16.14.
Check out the examples:
- ContextExample
- FetchBytesExample
- FetchMessagesExample
- IterableConsumerExample
- MessageConsumerExample
- NextExample
The service API allows you to easily build NATS services. The Service Framework is released as of 2.16.14
The Services Framework introduces a higher-level API for implementing services with NATS. NATS has always been a strong technology on which to build services, as they are straightforward to write, are location and DNS independent, and can be scaled up or down by simply adding or removing instances of the service.
The Services Framework further streamlines their development by providing observability and standardization. The Service Framework allows your services to be discovered and queried for status without additional work.
Check out the ServiceExample
Subject validation is changed as of 2.25.1. Up until now, subject validation has been strict. This means that when subjects were presented for instance in publish or subscribe calls, strict validation was applied. While this is great for some use cases, for instance when giving the ability of an application user to set a subject, it's generally not necessary. And in that case, you may want to validate the subject as you would to prevent a sql injection. If you tightly control your subjects, you may prefer no validation at all.
Options now specifies lenient validation by default and gives you the ability to set the validation to none or strict if that makes sense for your application. This can be accomplished with Options.Builder methods:
public Builder noSubjectValidation();
public Builder strictSubjectValidation();
of via properties
noSubjectValidation=true
strictSubjectValidation=true
Behavior and Benchmark over 37 million passes...
| Type | Millis Total | Nanos Per | Behavior |
|---|---|---|---|
| Lenient | 7393 |
199.81 |
Space, Tab, CR, LF are invalid |
| Strict | 15272 |
413.76 |
Lenient plus check for improper formation of segments or wildcard use |
| None | 2155 |
58.24 |
Only checks for string not being null or empty when the subject is required |
Starting with 2.21.2 snapshots, the project has been migrated to Sonatype's Maven Central Repository.
Releases will still propagate to Maven Central as usual, but releases that are just published and not yet propagated,
and -SNAPSHOTS are available at a different urls.
See Using Gradle or Using Maven for more information
2.18.0 attempts to start us on the road to proper Semantic Version (semver). In the last few patch releases, there were technically things that should cause a minor version bump, but were numbered as a patch.
Even if just one api is newly added, semver requires that we bump the minor version. The forceReconnect api
is an example of one api being added to the Connection interface. It should have resulted in a minor version bump.
Going forward, when a release contains only bug fixes, it's appropriate to simply bump the patch. But if an api is added, even one, then the minor version will be bumped.
There is are new Connection interface apis:
void forceReconnect() throws IOException, InterruptedException;void forceReconnect(ForceReconnectOptions options) throws IOException, InterruptedException;
If you call forceReconnect, your connection will be immediately closed and the reconnect logic will be executed.
This version supports connecting to a reverse proxy securely while the proxy connects to the server insecurely. See the Reverse Proxy Section for more details.
This release was full of core improvements which improve the use of more asynchronous behaviors including
- removing use of
synchronizedin favor ofReentrantLock - The ability to have a dispatcher use an executor to dispatch listener event messages instead of the dispatcher thread blocking to deliver the event.
The client, unless overridden, uses a java.net.Socket for connections. This java.net.Socket implementation does not support a write timeout, so writing data to the socket is a blocking call.
Under some conditions it will block indefinitely, freezing that connection on the client. One way this could happen is if the server was too busy to read what was being sent. Or, it could be a device, network, or connection issue. Whatever it is, it blocks the jvm Socket write implementation which used to block us. It's rare, but it does happen.
To address this, we now monitor socket writes to ensure they complete within a timeout.
The timeout is configurable in Options via the builder and socketWriteTimeout(duration|milliseconds).
The default is 1 minute if you don't set it.
You can turn the watching off by setting a null duration or 0 milliseconds.
When the watcher is turned on, a background task watches the write operations and makes sure they complete within the timeout. If a write operation fails to complete, the task tells the connection to close the socket, which triggers the retry logic. There may still be messages in the output queue, and messages that were in transit are in an unknown state. Handling disconnections and output queue is left for another discussion.
See Version Notes
The java-nats client is provided in a single jar file, with a single external dependency for the encryption in NKey support. See Building From Source for details on building the library.
You can download the latest jar at jnats-2.25.1.jar.
The examples are available at jnats-2.25.1-examples.jar.
The NATS client is available in the Maven central repository, and can be imported as a standard dependency in your build.gradle file:
dependencies {
implementation 'io.nats:jnats:2.25.1'
}If you need the latest and greatest before Maven central updates, you can use:
repositories {
mavenCentral()
maven {
url "https://repo1.maven.org/maven2/"
}
}If you need a snapshot version, you must add the url for the snapshots and change your dependency.
repositories {
mavenCentral()
maven {
url "https://central.sonatype.com/repository/maven-snapshots"
}
}
dependencies {
implementation 'io.nats:jnats:2.25.2-SNAPSHOT'
}The NATS client is available on the Maven Central Repository and can be imported as a normal dependency in your pom.xml file:
<dependency>
<groupId>io.nats</groupId>
<artifactId>jnats</artifactId>
<version>2.25.1</version>
</dependency>If you need the absolute latest, before it propagates to maven central, you can use the repository:
<repositories>
<repository>
<id>sonatype releases</id>
<url>https://repo1.maven.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>If you need a snapshot version, you must enable snapshots and change your dependency.
<repositories>
<repository>
<id>sonatype snapshots</id>
<url>https://central.sonatype.com/repository/maven-snapshots</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
</repositories>
<dependency>
<groupId>io.nats</groupId>
<artifactId>jnats</artifactId>
<version>2.25.2-SNAPSHOT</version>
</dependency>To include this library with a GraalVM project, you must use configure these initialize-at-run-time classes.
--initialize-at-run-time=java.security.SecureRandom.--initialize-at-run-time=io.nats.client.support.RandomUtils--initialize-at-run-time=io.nats.client.NUID
These will instruct GraalVM to initialize specified classes at runtime so that these instances don't have fixed seeds. GraalVM won't compile without these parameters.
For a much more thorough discussion of the subject, please visit the nats-graalvm-example repository made by one of our contributors. There is a detailed demonstration for creating an efficient NATS client with GraalVM. This example leverages the client to connect to a server.
Sending and receiving with NATS is as simple as connecting to the nats-server and publishing or subscribing for messages.
Please see the examples in this project. The Examples Readme is a good place to start.
There are also examples in the java-nats-examples repo.
There are five different ways to connect using the Java library, each with a parallel method that will allow doing reconnect logic if the initial connection fails. The ability to reconnect on the initial connection failure is NOT an Options setting.
-
Connect to a local server on the default url. From the Options class:
DEFAULT_URL = "nats://localhost:4222";// default options Connection nc = Nats.connect(); // default options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect();
-
Connect to one or more servers using a URL:
// single URL, all other default options Connection nc = Nats.connect("nats://myhost:4222"); // comma-separated list of URLs, all other default options Connection nc = Nats.connect("nats://myhost:4222,nats://myhost:4223"); // single URL, all other default options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect("nats://myhost:4222"); // comma-separated list of URLs, all other default options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect("nats://myhost:4222,nats://myhost:4223");
-
Connect to one or more servers with a custom configuration:
Options o = new Options.Builder().server("nats://serverone:4222").server("nats://servertwo:4222").maxReconnects(-1).build(); // custom options Connection nc = Nats.connect(o); // custom options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect(o);
-
Connect asynchronously, this requires a callback to tell the application when the client is connected:
Options options = new Options.Builder().server(Options.DEFAULT_URL).connectionListener(handler).build(); Nats.connectAsynchronously(options, true);
-
Connect with an authentication handler:
AuthHandler authHandler = Nats.credentials(System.getenv("NATS_CREDS")); // single URL, all other default options Connection nc = Nats.connect("nats://myhost:4222", authHandler); // comma-separated list of URLs, all other default options Connection nc = Nats.connect("nats://myhost:4222,nats://myhost:4223", authHandler); // single URL, all other default options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect("nats://myhost:4222", authHandler); // comma-separated list of URLs, all other default options, reconnect on connect Connection nc = Nats.connectReconnectOnConnect("nats://myhost:4222,nats://myhost:4223", authHandler);
The Java client will automatically reconnect if it loses its connection to the nats-server. If given a single server, the client will keep trying that one. If given a list of servers, the client will rotate between them. When the NATS servers are in a cluster, they will tell the client about the other servers. In the simplest case a client could connect to one server, learn about the cluster, and reconnect to another server if its initial one goes down.
To tell the connection about multiple servers for the initial connection, use the servers() method on the Options builder, or call server() multiple times.
String[] serverUrls = {"nats://serverOne:4222", "nats://serverTwo:4222"};
Options o = new Options.Builder().servers(serverUrls).build();Reconnection behavior is controlled via a few options. See the Javadoc for the Options.Builder class for specifics on reconnection limits, delays, and buffers.
Connection options are configured using the Options class. There is a Builder that uses a fluent interface.
It can accept Properties object or a path to a Properties file.
The io.nats.client. prefix is not required in the properties file anymore. These are now equivalent:
io.nats.client.servers=nats://localhost:4222servers=nats://localhost:4222The Options builder allows you to use both properties and code. When it comes to the builder, the last one called wins. This applies to each property.
props.setProperty(Options.PROP_MAX_MESSAGES_IN_OUTGOING_QUEUE, "7000");
o = new Options.Builder()
.properties(props)
.maxMessagesInOutgoingQueue(6000)
.build();
assertEquals(6000, o.getMaxMessagesInOutgoingQueue());
o = new Options.Builder()
.maxMessagesInOutgoingQueue(6000)
.properties(props)
.build();
assertEquals(7000, o.getMaxMessagesInOutgoingQueue());
o = new Options.Builder()
.maxMessagesInOutgoingQueue(6000)
.maxMessagesInOutgoingQueue(8000)
.build();
assertEquals(8000, o.getMaxMessagesInOutgoingQueue());