package netflix.ocelli.rxnetty.protocol.http;
import io.reactivex.netty.client.ConnectionProvider;
import io.reactivex.netty.protocol.http.client.HttpClient;
import io.reactivex.netty.protocol.http.client.events.HttpClientEventsListener;
import netflix.ocelli.Instance;
import netflix.ocelli.LoadBalancerStrategy;
import netflix.ocelli.loadbalancer.ChoiceOfTwoLoadBalancer;
import netflix.ocelli.loadbalancer.RandomWeightedLoadBalancer;
import netflix.ocelli.loadbalancer.RoundRobinLoadBalancer;
import netflix.ocelli.loadbalancer.weighting.LinearWeightingStrategy;
import netflix.ocelli.rxnetty.FailureListener;
import netflix.ocelli.rxnetty.internal.AbstractLoadBalancer;
import netflix.ocelli.rxnetty.internal.HostConnectionProvider;
import netflix.ocelli.rxnetty.protocol.WeightComparator;
import rx.Observable;
import rx.functions.Func1;
import java.net.SocketAddress;
/**
* An HTTP load balancer to be used with {@link HttpClient}.
*
* <h2>Failure detection</h2>
*
* For every host that this load balancer connects, it provides a way to register a {@link HttpClientEventsListener}
* instance that can detect failures based on the various events received. Upon detecting the failure, an appropriate
* action can be taken for the host, using the provided {@link FailureListener}.
*
* <h2>Use with {@link HttpClient}</h2>
*
* In order to use this load balancer with RxNetty clients, one has to convert it to an instance of
* {@link ConnectionProvider} by calling {@link #toConnectionProvider()}
*
* @param <W> Type of Objects written on the connections created by this load balancer.
* @param <R> Type of Objects read from the connections created by this load balancer.
*/
public class HttpLoadBalancer<W, R> extends AbstractLoadBalancer<W, R> {
private static final Func1<FailureListener, HttpClientEventsListener> NO_LISTENER_FACTORY =
new Func1<FailureListener, HttpClientEventsListener>() {
@Override
public HttpClientEventsListener call(FailureListener failureListener) {
return null;
}
};
private HttpLoadBalancer(Observable<Instance<SocketAddress>> hosts,
LoadBalancerStrategy<HostConnectionProvider<W, R>> loadBalancer) {
this(hosts, loadBalancer, NO_LISTENER_FACTORY);
}
/**
* Typically, static methods in this class would be used to create new instances of the load balancer with known
* load balancing strategies. However, for any custom load balancing schemes, one can use this constructor directly.
*
* @param hosts Stream of hosts to use for load balancing.
* @param loadBalancer The load balancing strategy.
* @param eventListenerFactory A factory for creating new {@link HttpClientEventsListener} per host.
*/
public HttpLoadBalancer(Observable<Instance<SocketAddress>> hosts,
LoadBalancerStrategy<HostConnectionProvider<W, R>> loadBalancer,
Func1<FailureListener, ? extends HttpClientEventsListener> eventListenerFactory) {
super(hosts, eventListenerFactory, loadBalancer);
}
/**
* Creates a new load balancer using a round-robin load balancing strategy ({@link RoundRobinLoadBalancer}) over the
* passed stream of hosts. The hosts ({@link SocketAddress}) emitted by the passed stream are used till their
* lifecycle ends ({@code Observable} returned by {@link Instance#getLifecycle()} terminates).
*
* For using any failure detection schemes for the hosts, use {@link #roundRobin(Observable, Func1)} instead.
*
* @param hosts Stream of hosts to use for load balancing.
*
* @param <W> Type of Objects written on the connections created by this load balancer.
* @param <R> Type of Objects read from the connections created by this load balancer.
*
* @return New load balancer instance.
*/
public static <W, R> HttpLoadBalancer<W, R> roundRobin(Observable<Instance<SocketAddress>> hosts) {
return new HttpLoadBalancer<W, R>(hosts, new RoundRobinLoadBalancer<HostConnectionProvider<W, R>>());
}
/**
* Creates a new load balancer using a round-robin load balancing strategy ({@link RoundRobinLoadBalancer}) over the
* passed stream of hosts. The hosts ({@link SocketAddress}) emitted by the passed stream are used till their
* lifecycle ends ({@code Observable} returned by {@link Instance#getLifecycle()} terminates) or are explicitly
* removed by the passed failure detector.
*
* @param hosts Stream of hosts to use for load balancing.
* @param failureDetector A factory for creating a {@link HttpClientEventsListener} per host. The listeners based on
* any criterion can then remove the host from the load balancing pool.
*
* @param <W> Type of Objects written on the connections created by this load balancer.
* @param <R> Type of Objects read from the connections created by this load balancer.
*
* @return New load balancer instance.
*/
public static <W, R> HttpLoadBalancer<W, R> roundRobin(Observable<Instance<SocketAddress>> hosts,
Func1<FailureListener, HttpClientEventsListener> failureDetector) {
return new HttpLoadBalancer<>(hosts, new RoundRobinLoadBalancer<HostConnectionProvider<W, R>>(),
failureDetector);
}
/**
* Creates a new load balancer using a weighted random load balancing strategy ({@link RandomWeightedLoadBalancer})
* over the passed stream of hosts.
*
* @param hosts Stream of hosts to use for load balancing.
* @param listenerFactory A factory for creating {@link WeightedHttpClientListener} per active host.
*
* @param <W> Type of Objects written on the connections created by this load balancer.
* @param <R> Type of Objects read from the connections created by this load balancer.
*
* @return New load balancer instance.
*/
public static <W, R> HttpLoadBalancer<W, R> weigthedRandom(Observable<Instance<SocketAddress>> hosts,
Func1<FailureListener, WeightedHttpClientListener> listenerFactory) {
LinearWeightingStrategy<HostConnectionProvider<W, R>> ws = new LinearWeightingStrategy<>(
new Func1<HostConnectionProvider<W, R>, Integer>() {
@Override
public Integer call(HostConnectionProvider<W, R> cp) {
WeightedHttpClientListener el = (WeightedHttpClientListener) cp.getEventsListener();
return el.getWeight();
}
});
return new HttpLoadBalancer<W, R>(hosts, new RandomWeightedLoadBalancer<HostConnectionProvider<W, R>>(ws),
listenerFactory);
}
/**
* Creates a new load balancer using a power of two choices load balancing strategy ({@link ChoiceOfTwoLoadBalancer})
* over the passed stream of hosts.
*
* @param hosts Stream of hosts to use for load balancing.
* @param listenerFactory A factory for creating {@link WeightedHttpClientListener} per active host.
*
* @param <W> Type of Objects written on the connections created by this load balancer.
* @param <R> Type of Objects read from the connections created by this load balancer.
*
* @return New load balancer instance.
*/
public static <W, R> HttpLoadBalancer<W, R> choiceOfTwo(Observable<Instance<SocketAddress>> hosts,
Func1<FailureListener, WeightedHttpClientListener> listenerFactory) {
return new HttpLoadBalancer<W, R>(hosts, new ChoiceOfTwoLoadBalancer<>(new WeightComparator<W, R>()),
listenerFactory);
}
}