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

zipkin2.reporter.HttpEndpointSuppliers Maven / Gradle / Ivy

The newest version!
/*
 * Copyright The OpenZipkin Authors
 * SPDX-License-Identifier: Apache-2.0
 */
package zipkin2.reporter;

import java.io.IOException;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import zipkin2.reporter.HttpEndpointSupplier.Factory;

import static java.lang.String.format;

/**
 * Built-in {@link HttpEndpointSupplier} implementations.
 *
 * @since 3.3
 */
public final class HttpEndpointSuppliers {
  enum ConstantFactory implements Factory {
    INSTANCE;

    @Override public HttpEndpointSupplier create(String endpoint) {
      return newConstant(endpoint);
    }

    @Override public String toString() {
      return "ConstantFactory{}";
    }
  }

  /**
   * Returns a {@linkplain Factory} which calls {@link #newConstant(String)} for each input.
   *
   * @since 3.3
   */
  public static Factory constantFactory() {
    return ConstantFactory.INSTANCE;
  }

  /**
   * {@link BaseHttpSender sender} implementations look for a {@linkplain Constant} to avoid the
   * overhead of dynamic lookups on each call to {@link BaseHttpSender#postSpans(Object, Object)}.
   *
   * @since 3.3
   */
  public static HttpEndpointSupplier.Constant newConstant(String endpoint) {
    if (endpoint == null) throw new NullPointerException("endpoint == null");
    return new Constant(endpoint);
  }

  static final class Constant implements HttpEndpointSupplier.Constant {

    final String endpoint;

    Constant(String endpoint) {
      this.endpoint = endpoint;
    }

    @Override public String get() {
      return endpoint;
    }

    @Override public void close() {
    }

    @Override public boolean equals(Object o) {
      if (o == this) return true;
      if (!(o instanceof HttpEndpointSupplier.Constant)) return false;
      HttpEndpointSupplier.Constant that = (HttpEndpointSupplier.Constant) o;
      return endpoint.equals(that.get());
    }

    @Override public int hashCode() {
      return endpoint.hashCode();
    }

    @Override public String toString() {
      return endpoint;
    }
  }

  /**
   * Returns a {@linkplain Factory} which calls {@link #newRateLimited(HttpEndpointSupplier, int)}
   * for each input.
   *
   * @param intervalSeconds amount of time to wait *after* calls to get before trying again.
   * @return a factory that returns rate-limited suppliers unless the input was a
   * {@linkplain HttpEndpointSupplier.Constant}.
   * @throws IllegalArgumentException if intervalSeconds is less than one.
   * @since 3.3
   */
  public static Factory newRateLimitedFactory(Factory input, int intervalSeconds) {
    if (input == null) throw new NullPointerException("input == null");
    if (intervalSeconds < 1) throw new IllegalArgumentException("intervalSeconds < 1");
    if (input == ConstantFactory.INSTANCE) return input;
    return new RateLimited.Factory(input, intervalSeconds);
  }

  /**
   * Dynamic {@linkplain HttpEndpointSupplier}s can guard against abuse by rate-limiting them.
   *
   * 

Calling this will invoke {@link HttpEndpointSupplier#get()} on the input. This ensures at * least one successful call or an error. * *

Note: errors that occur during get will be logged and ignored. This allows the sender to * attempt a prior endpoint vs erring on repeat. * * @param input what to rate-limit. * @param intervalSeconds amount of time to wait *after* calls to get before trying again. * @return a rate-limited supplier unless the input was a {@linkplain HttpEndpointSupplier.Constant}. * @throws IllegalArgumentException if intervalSeconds is less than one. * @since 3.3 */ public static HttpEndpointSupplier newRateLimited(HttpEndpointSupplier input, int intervalSeconds) { if (input == null) throw new NullPointerException("input == null"); if (intervalSeconds < 1) throw new IllegalArgumentException("intervalSeconds < 1"); if (input instanceof HttpEndpointSupplier.Constant) return input; return new RateLimited(RateLimited.LOGGER, input, intervalSeconds); } static class RateLimited implements HttpEndpointSupplier { // not final for testing static final Logger LOGGER = Logger.getLogger(RateLimited.class.getName()); static final class Factory implements HttpEndpointSupplier.Factory { final HttpEndpointSupplier.Factory delegate; final int intervalSeconds; Factory(HttpEndpointSupplier.Factory delegate, int intervalSeconds) { this.delegate = delegate; this.intervalSeconds = intervalSeconds; } @Override public HttpEndpointSupplier create(String endpoint) { return newRateLimited(delegate.create(endpoint), intervalSeconds); } @Override public String toString() { return "RateLimited{delegate=" + delegate + ", intervalSeconds=" + intervalSeconds + "}"; } } final Logger logger; final HttpEndpointSupplier delegate; final int intervalSeconds; String endpoint; long nanoTimeout; RateLimited(Logger logger, HttpEndpointSupplier delegate, int intervalSeconds) { this.logger = logger; this.delegate = delegate; this.intervalSeconds = intervalSeconds; // Make sure at least one value can be obtained. endpoint = delegate.get(); updateNextGet(); } // overridable for tests long nanoTime() { return System.nanoTime(); } /** * This is used on a sender thread, so it doesn't need to be synchronized technically. * However, this is to guard against misuse. */ @Override public final synchronized String get() { long now = nanoTime(), nanoTimeout = this.nanoTimeout; // Is it time to call get again? long nanosUntilGet = -(now - nanoTimeout); // because nanoTime can be negative if (nanosUntilGet <= 0) { // See if we won the race, and if so, update the endpoint. try { endpoint = delegate.get(); } catch (Throwable t) { Call.propagateIfFatal(t); String message = format("error from httpEndpointSupplier.get() will retry in %ds: %s", intervalSeconds, t.getMessage()); logger.warning(message); // log the exception at fine/debug level logger.log(Level.FINE, "exception from: " + delegate, t); } finally { updateNextGet(); } } return endpoint; } private void updateNextGet() { // Above may take more than a second, so update the interval again. nanoTimeout = nanoTime() + TimeUnit.SECONDS.toNanos(intervalSeconds); } @Override public final void close() throws IOException { delegate.close(); } @Override public String toString() { return "RateLimited{delegate=" + delegate + ", intervalSeconds=" + intervalSeconds + "}"; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy