![JAR search and dependency download from the Maven repository](/logo.png)
io.nats.service.Discovery Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnats Show documentation
Show all versions of jnats Show documentation
Client library for working with the NATS messaging system.
The newest version!
// Copyright 2023 The NATS Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package io.nats.service;
import io.nats.client.Connection;
import io.nats.client.Message;
import io.nats.client.Subscription;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static io.nats.service.Service.*;
/**
* Discovery is a utility class to help discover services by executing Ping, Info and Stats requests
* You are required to provide a connection.
* Optionally you can set 'maxTimeMillis' and 'maxResults'. When making a discovery request,
* the discovery will wait until the first one of those thresholds is reached before returning the results.
* 'maxTimeMillis' defaults to {@value DEFAULT_DISCOVERY_MAX_TIME_MILLIS}
* 'maxResults' defaults tp {@value DEFAULT_DISCOVERY_MAX_RESULTS}
*/
public class Discovery {
public static final long DEFAULT_DISCOVERY_MAX_TIME_MILLIS = 5000;
public static final int DEFAULT_DISCOVERY_MAX_RESULTS = 10;
private final Connection conn;
private final long maxTimeMillis;
private final int maxResults;
private Supplier inboxSupplier;
/**
* Construct a Discovery instance with a connection and default maxTimeMillis / maxResults
* @param conn the NATS Connection
*/
public Discovery(Connection conn) {
this(conn, 0, 0);
}
/**
* Construct a Discovery instance
* @param conn the NATS Connection
* @param maxTimeMillis the maximum time to wait for discovery requests to complete or any number less than 1 to use the default
* @param maxResults the maximum number of results to wait for or any number less than 1 to use the default
*/
public Discovery(Connection conn, long maxTimeMillis, int maxResults) {
this.conn = conn;
this.maxTimeMillis = maxTimeMillis < 1 ? DEFAULT_DISCOVERY_MAX_TIME_MILLIS : maxTimeMillis;
this.maxResults = maxResults < 1 ? DEFAULT_DISCOVERY_MAX_RESULTS : maxResults;
setInboxSupplier(null);
}
/**
* Override the normal inbox with a custom inbox to support you security model
* @param inboxSupplier the supplier
*/
public void setInboxSupplier(Supplier inboxSupplier) {
this.inboxSupplier = inboxSupplier == null ? conn::createInbox : inboxSupplier;
}
// ----------------------------------------------------------------------------------------------------
// ping
// ----------------------------------------------------------------------------------------------------
/**
* Make a ping request to all services running on the server.
* @return the list of {@link PingResponse}
*/
public List ping() {
return ping(null);
}
/**
* Make a ping request only to services having the matching service name
* @param serviceName the service name
* @return the list of {@link PingResponse}
*/
public List ping(String serviceName) {
List list = new ArrayList<>();
discoverMany(SRV_PING, serviceName, jsonBytes -> list.add(new PingResponse(jsonBytes)));
return list;
}
/**
* Make a ping request to a specific instance of a service having matching service name and id
* @param serviceName the service name
* @param serviceId the specific service id
* @return the list of {@link PingResponse}
*/
public PingResponse ping(String serviceName, String serviceId) {
byte[] jsonBytes = discoverOne(SRV_PING, serviceName, serviceId);
return jsonBytes == null ? null : new PingResponse(jsonBytes);
}
// ----------------------------------------------------------------------------------------------------
// info
// ----------------------------------------------------------------------------------------------------
/**
* Make an info request to all services running on the server.
* @return the list of {@link InfoResponse}
*/
public List info() {
return info(null);
}
/**
* Make an info request only to services having the matching service name
* @param serviceName the service name
* @return the list of {@link InfoResponse}
*/
public List info(String serviceName) {
List list = new ArrayList<>();
discoverMany(SRV_INFO, serviceName, jsonBytes -> list.add(new InfoResponse(jsonBytes)));
return list;
}
/**
* Make an info request to a specific instance of a service having matching service name and id
* @param serviceName the service name
* @param serviceId the specific service id
* @return the list of {@link InfoResponse}
*/
public InfoResponse info(String serviceName, String serviceId) {
byte[] jsonBytes = discoverOne(SRV_INFO, serviceName, serviceId);
return jsonBytes == null ? null : new InfoResponse(jsonBytes);
}
// ----------------------------------------------------------------------------------------------------
// stats
// ----------------------------------------------------------------------------------------------------
/**
* Make a stats request to all services running on the server.
* @return the list of {@link StatsResponse}
*/
public List stats() {
return stats(null);
}
/**
* Make a stats request only to services having the matching service name
* @param serviceName the service name
* @return the list of {@link StatsResponse}
*/
public List stats(String serviceName) {
List list = new ArrayList<>();
discoverMany(SRV_STATS, serviceName, jsonBytes -> list.add(new StatsResponse(jsonBytes)));
return list;
}
/**
* Make a stats request to a specific instance of a service having matching service name and id
* @param serviceName the service name
* @param serviceId the specific service id
* @return the list of {@link StatsResponse}
*/
public StatsResponse stats(String serviceName, String serviceId) {
byte[] jsonBytes = discoverOne(SRV_STATS, serviceName, serviceId);
return jsonBytes == null ? null : new StatsResponse(jsonBytes);
}
// ----------------------------------------------------------------------------------------------------
// workers
// ----------------------------------------------------------------------------------------------------
private byte[] discoverOne(String action, String serviceName, String serviceId) {
String subject = Service.toDiscoverySubject(action, serviceName, serviceId);
try {
Message m = conn.request(subject, null, Duration.ofMillis(maxTimeMillis));
if (m != null) {
return m.getData();
}
}
catch (InterruptedException e) {
// conn.request interrupted means data is not retrieved
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
return null;
}
private void discoverMany(String action, String serviceName, Consumer dataConsumer) {
Subscription sub = null;
try {
String replyTo = inboxSupplier.get();
sub = conn.subscribe(replyTo);
String subject = toDiscoverySubject(action, serviceName, null);
conn.publish(subject, replyTo, null);
int resultsLeft = maxResults;
long start = System.currentTimeMillis();
long timeLeft = maxTimeMillis;
while (resultsLeft > 0 && timeLeft > 0) {
Message msg = sub.nextMessage(timeLeft);
if (msg == null) {
return;
}
dataConsumer.accept(msg.getData());
resultsLeft--;
// try again while we have time
timeLeft = maxTimeMillis - (System.currentTimeMillis() - start);
}
}
catch (InterruptedException e) {
// sub.nextMessage was fetching one message
// and data is not completely read
// so it seems like this is an error condition
Thread.currentThread().interrupt();
throw new RuntimeException(e);
}
finally {
try {
//noinspection DataFlowIssue
sub.unsubscribe();
}
catch (Exception ignore) {}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy