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

io.servicetalk.loadbalancer.RoundRobinLoadBalancerFactory Maven / Gradle / Ivy

There is a newer version: 0.42.52
Show newest version
/*
 * Copyright © 2021-2022 Apple Inc. and the ServiceTalk project 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.servicetalk.loadbalancer;

import io.servicetalk.client.api.ConnectionFactory;
import io.servicetalk.client.api.LoadBalancedConnection;
import io.servicetalk.client.api.LoadBalancer;
import io.servicetalk.client.api.LoadBalancerFactory;
import io.servicetalk.client.api.ServiceDiscovererEvent;
import io.servicetalk.concurrent.api.DefaultThreadFactory;
import io.servicetalk.concurrent.api.Executor;
import io.servicetalk.concurrent.api.Executors;
import io.servicetalk.concurrent.api.Publisher;
import io.servicetalk.loadbalancer.RoundRobinLoadBalancer.HealthCheckConfig;
import io.servicetalk.transport.api.ExecutionStrategy;

import java.time.Duration;
import java.util.Collection;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import javax.annotation.Nullable;

import static io.servicetalk.utils.internal.DurationUtils.ensureNonNegative;
import static io.servicetalk.utils.internal.DurationUtils.ensurePositive;
import static io.servicetalk.utils.internal.DurationUtils.isPositive;
import static java.time.Duration.ofSeconds;
import static java.util.concurrent.TimeUnit.SECONDS;

/**
 * {@link LoadBalancerFactory} that creates {@link LoadBalancer} instances which use a round-robin strategy
 * for selecting connections from a pool of addresses.
 * 

* For more information, see javadoc for {@link RoundRobinLoadBalancerBuilder}. * * @param The resolved address type. * @param The type of connection. * @deprecated this class will be made package-private in the future, use {@link RoundRobinLoadBalancers} to create * {@link RoundRobinLoadBalancerBuilder}. */ @Deprecated // FIXME: 0.43 - make package private public final class RoundRobinLoadBalancerFactory implements LoadBalancerFactory { private static final Duration DEFAULT_HEALTH_CHECK_INTERVAL = ofSeconds(5); private static final Duration DEFAULT_HEALTH_CHECK_JITTER = ofSeconds(3); static final Duration DEFAULT_HEALTH_CHECK_RESUBSCRIBE_INTERVAL = ofSeconds(10); static final int DEFAULT_HEALTH_CHECK_FAILED_CONNECTIONS_THRESHOLD = 5; // higher than default for AutoRetryStrategy private final String id; private final int linearSearchSpace; @Nullable private final HealthCheckConfig healthCheckConfig; private RoundRobinLoadBalancerFactory(final String id, final int linearSearchSpace, @Nullable final HealthCheckConfig healthCheckConfig) { this.id = id; this.linearSearchSpace = linearSearchSpace; this.healthCheckConfig = healthCheckConfig; } @Deprecated @Override public LoadBalancer newLoadBalancer( final String targetResource, final Publisher>> eventPublisher, final ConnectionFactory connectionFactory) { return new RoundRobinLoadBalancer<>(id, targetResource, eventPublisher, connectionFactory, linearSearchSpace, healthCheckConfig); } @Override public LoadBalancer newLoadBalancer( final Publisher>> eventPublisher, final ConnectionFactory connectionFactory, final String targetResource) { return new RoundRobinLoadBalancer<>(id, targetResource, eventPublisher, connectionFactory, linearSearchSpace, healthCheckConfig); } @Override public ExecutionStrategy requiredOffloads() { // We do not block return ExecutionStrategy.offloadNone(); } /** * Builder for {@link RoundRobinLoadBalancerFactory}. * * @param The resolved address type. * @param The type of connection. * @deprecated this class will be made package-private in the future, rely on the * {@link RoundRobinLoadBalancerBuilder} instead. */ @Deprecated // FIXME: 0.43 - make package private public static final class Builder implements RoundRobinLoadBalancerBuilder { private final String id; private int linearSearchSpace = 16; @Nullable private Executor backgroundExecutor; private Duration healthCheckInterval = DEFAULT_HEALTH_CHECK_INTERVAL; private Duration healthCheckJitter = DEFAULT_HEALTH_CHECK_JITTER; private int healthCheckFailedConnectionsThreshold = DEFAULT_HEALTH_CHECK_FAILED_CONNECTIONS_THRESHOLD; private long healthCheckResubscribeLowerBound = DEFAULT_HEALTH_CHECK_RESUBSCRIBE_INTERVAL.minus(DEFAULT_HEALTH_CHECK_JITTER).toNanos(); private long healthCheckResubscribeUpperBound = DEFAULT_HEALTH_CHECK_RESUBSCRIBE_INTERVAL.plus(DEFAULT_HEALTH_CHECK_JITTER).toNanos();; /** * Creates a new instance with default settings. * * @deprecated use {@link RoundRobinLoadBalancers#builder(String)} instead. */ @Deprecated // FIXME: 0.43 - remove deprecated constructor public Builder() { this("undefined"); } Builder(final String id) { if (id.isEmpty()) { throw new IllegalArgumentException("ID can not be empty"); } this.id = id; } @Override public RoundRobinLoadBalancerFactory.Builder linearSearchSpace(int linearSearchSpace) { if (linearSearchSpace < 0) { throw new IllegalArgumentException("linearSearchSpace: " + linearSearchSpace + " (expected >=0)"); } this.linearSearchSpace = linearSearchSpace; return this; } @Override public RoundRobinLoadBalancerFactory.Builder backgroundExecutor( Executor backgroundExecutor) { this.backgroundExecutor = new NormalizedTimeSourceExecutor(backgroundExecutor); return this; } /** * Configure an interval for health checking a host that failed to open connections. If no interval is provided * using this method, a default value will be used. *

* {@link #healthCheckFailedConnectionsThreshold(int)} can be used to disable the health checking mechanism * and always consider all hosts for establishing new connections. * * @param interval interval at which a background health check will be scheduled. * @return {@code this}. * @see #healthCheckFailedConnectionsThreshold(int) * @deprecated Use {@link #healthCheckInterval(Duration, Duration)}. */ @Deprecated public RoundRobinLoadBalancerFactory.Builder healthCheckInterval(Duration interval) { return healthCheckInterval(interval, interval.compareTo(DEFAULT_HEALTH_CHECK_INTERVAL) < 0 ? interval.dividedBy(2) : DEFAULT_HEALTH_CHECK_JITTER); } @Override public RoundRobinLoadBalancerFactory.Builder healthCheckInterval(Duration interval, Duration jitter) { validate(interval, jitter); this.healthCheckInterval = interval; this.healthCheckJitter = jitter; return this; } @Override public RoundRobinLoadBalancerFactory.Builder healthCheckResubscribeInterval( Duration interval, Duration jitter) { validate(interval, jitter); this.healthCheckResubscribeLowerBound = interval.minus(jitter).toNanos(); this.healthCheckResubscribeUpperBound = interval.plus(jitter).toNanos(); return this; } private static void validate(Duration interval, Duration jitter) { ensurePositive(interval, "interval"); ensureNonNegative(jitter, "jitter"); final Duration lowerBound = interval.minus(jitter); if (!isPositive(lowerBound)) { throw new IllegalArgumentException("interval (" + interval + ") minus jitter (" + jitter + ") must be greater than 0, current=" + lowerBound); } final Duration upperBound = interval.plus(jitter); if (!isPositive(upperBound)) { throw new IllegalArgumentException("interval (" + interval + ") plus jitter (" + jitter + ") must not overflow, current=" + upperBound); } } @Override public RoundRobinLoadBalancerFactory.Builder healthCheckFailedConnectionsThreshold( int threshold) { if (threshold == 0) { throw new IllegalArgumentException("Health check failed connections threshold should not be 0"); } this.healthCheckFailedConnectionsThreshold = threshold; return this; } @Override public RoundRobinLoadBalancerFactory build() { if (this.healthCheckFailedConnectionsThreshold < 0) { return new RoundRobinLoadBalancerFactory<>(id, linearSearchSpace, null); } HealthCheckConfig healthCheckConfig = new HealthCheckConfig( this.backgroundExecutor == null ? SharedExecutor.getInstance() : this.backgroundExecutor, healthCheckInterval, healthCheckJitter, healthCheckFailedConnectionsThreshold, healthCheckResubscribeLowerBound, healthCheckResubscribeUpperBound); return new RoundRobinLoadBalancerFactory<>(id, linearSearchSpace, healthCheckConfig); } } static final class SharedExecutor { private static final Executor INSTANCE = new NormalizedTimeSourceExecutor(Executors.from( new ThreadPoolExecutor(1, 1, 60, SECONDS, new LinkedBlockingQueue<>(), new DefaultThreadFactory("round-robin-load-balancer-executor")))); private SharedExecutor() { } static Executor getInstance() { return INSTANCE; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy