All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.nats.service.Discovery Maven / Gradle / Ivy

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