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

io.reactivex.netty.protocol.http.client.CompositeHttpClient Maven / Gradle / Ivy

There is a newer version: 0.3.18
Show newest version
/*
 * Copyright 2014 Netflix, Inc.
 *
 * 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.reactivex.netty.protocol.http.client;

import io.netty.bootstrap.Bootstrap;
import io.reactivex.netty.channel.ObservableConnection;
import io.reactivex.netty.client.ClientChannelFactory;
import io.reactivex.netty.client.ClientConnectionFactory;
import io.reactivex.netty.client.ClientMetricsEvent;
import io.reactivex.netty.client.ConnectionPoolBuilder;
import io.reactivex.netty.metrics.MetricEventsListener;
import io.reactivex.netty.metrics.MetricEventsSubject;
import io.reactivex.netty.pipeline.PipelineConfigurator;
import rx.Observable;
import rx.Subscription;

import java.util.concurrent.ConcurrentHashMap;

/**
 * An implementation of {@link HttpClient} that can execute requests over multiple hosts.
 * Internally this implementation uses one {@link HttpClientImpl} per unique
 * {@link io.reactivex.netty.client.RxClient.ServerInfo}.
 * The only way to create this client is via the {@link CompositeHttpClientBuilder}
 *
 * @author Nitesh Kant
 */
public class CompositeHttpClient extends HttpClientImpl {

    private final ConcurrentHashMap> httpClients;

    /**
     * This will be the unmodified configurator that can be used for all clients.
     */
    private final PipelineConfigurator, HttpClientRequest> pipelineConfigurator;
    private final ConnectionPoolBuilder, HttpClientRequest> poolBuilder;

    public CompositeHttpClient(String name, ServerInfo defaultServer, Bootstrap clientBootstrap,
                               PipelineConfigurator, HttpClientRequest> pipelineConfigurator,
                               ClientConfig clientConfig,
                               ClientChannelFactory, HttpClientRequest> channelFactory,
                               ClientConnectionFactory, HttpClientRequest,
                                       ? extends ObservableConnection, HttpClientRequest>> connectionFactory,
                               MetricEventsSubject> eventsSubject) {
        super(name, defaultServer, clientBootstrap, pipelineConfigurator, clientConfig, channelFactory, connectionFactory,
              eventsSubject);
        httpClients = new ConcurrentHashMap>();
        this.pipelineConfigurator = pipelineConfigurator;
        poolBuilder = null;
        httpClients.put(defaultServer, this); // So that submit() with default serverInfo also goes to the same client as no serverinfo.
    }

    CompositeHttpClient(String name, ServerInfo defaultServer, Bootstrap clientBootstrap,
                        PipelineConfigurator, HttpClientRequest> pipelineConfigurator,
                        ClientConfig clientConfig,
                        ConnectionPoolBuilder, HttpClientRequest> poolBuilder,
                        MetricEventsSubject> eventsSubject) {
        super(name, defaultServer, clientBootstrap, pipelineConfigurator, clientConfig, poolBuilder, eventsSubject);
        httpClients = new ConcurrentHashMap>();
        this.pipelineConfigurator = pipelineConfigurator;
        this.poolBuilder = poolBuilder;
        httpClients.put(defaultServer, this); // So that submit() with default serverInfo also goes to the same client as no serverinfo.
    }

    public Observable> submit(ServerInfo serverInfo, HttpClientRequest request) {
        HttpClient client = getClient(serverInfo);
        return client.submit(request);
    }

    public Observable> submit(ServerInfo serverInfo, HttpClientRequest request,
                                                    HttpClientConfig config) {
        HttpClient client = getClient(serverInfo);
        return client.submit(request, config);
    }

    private HttpClient getClient(ServerInfo serverInfo) {
        HttpClient client = httpClients.get(serverInfo);
        if (null == client) {
            client = newClient(serverInfo);
            HttpClient existing = httpClients.putIfAbsent(serverInfo, client);
            if (null != existing) {
                client.shutdown();
                client = existing;
            }
        }
        return client;
    }

    @Override
    public void shutdown() {
        for (HttpClient client : httpClients.values()) { // This map also contains the default client, so we don't need to shut the default explicitly.
            client.shutdown();
        }
    }

    public Subscription subscribe(ServerInfo server, MetricEventsListener> listener) {
        HttpClient client = httpClients.get(server);
        if (null == client) {
            throw new IllegalArgumentException("Invalid server: " + server.getHost() + ':' + server.getPort());
        }
        return client.subscribe(listener);
    }

    public ServerInfo getDefaultServer() {
        return serverInfo;
    }

    private HttpClientImpl newClient(ServerInfo serverInfo) {
        if (null != poolBuilder) {
            return new HttpClientImpl(name, serverInfo, clientBootstrap.clone(), pipelineConfigurator, clientConfig,
                                            clonePoolBuilder(serverInfo, poolBuilder), eventsSubject);
        } else {
            return new HttpClientImpl(name, serverInfo, clientBootstrap.clone(), pipelineConfigurator, clientConfig,
                                            channelFactory, connectionFactory, eventsSubject);
        }
    }

    private ConnectionPoolBuilder, HttpClientRequest> clonePoolBuilder(ServerInfo serverInfo,
                                                                                                ConnectionPoolBuilder, HttpClientRequest> poolBuilder) {
        ConnectionPoolBuilder, HttpClientRequest> toReturn = poolBuilder.copy(serverInfo);
        toReturn.withConnectionPoolLimitStrategy(
                ((CompositeHttpClientBuilder.CloneablePoolLimitDeterminationStrategy) poolBuilder
                        .getLimitDeterminationStrategy()).copy());
        return toReturn;
    }
}