Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor origin inventory builder #78

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,25 +22,16 @@
import com.google.common.eventbus.EventBus;
import com.google.common.eventbus.Subscribe;
import com.hotels.styx.api.Announcer;
import com.hotels.styx.api.HttpClient;
import com.hotels.styx.api.Id;
import com.hotels.styx.api.client.ActiveOrigins;
import com.hotels.styx.api.client.Connection;
import com.hotels.styx.api.client.ConnectionPool;
import com.hotels.styx.api.client.Origin;
import com.hotels.styx.api.client.OriginsInventorySnapshot;
import com.hotels.styx.api.client.OriginsInventoryStateChangeListener;
import com.hotels.styx.api.metrics.MetricRegistry;
import com.hotels.styx.api.metrics.codahale.CodaHaleMetricRegistry;
import com.hotels.styx.client.applications.BackendService;
import com.hotels.styx.client.connectionpool.CloseAfterUseConnectionDestination;
import com.hotels.styx.client.connectionpool.ConnectionPoolFactory;
import com.hotels.styx.client.healthcheck.OriginHealthCheckFunction;
import com.hotels.styx.client.healthcheck.OriginHealthStatusMonitor;
import com.hotels.styx.client.healthcheck.OriginHealthStatusMonitorFactory;
import com.hotels.styx.client.healthcheck.UrlRequestHealthCheck;
import com.hotels.styx.client.healthcheck.monitors.NoOriginHealthStatusMonitor;
import com.hotels.styx.client.netty.connectionpool.NettyConnectionFactory;
import com.hotels.styx.client.origincommands.DisableOrigin;
import com.hotels.styx.client.origincommands.EnableOrigin;
import com.hotels.styx.client.origincommands.GetOriginsInventorySnapshot;
Expand All @@ -54,17 +45,16 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;

import static com.google.common.base.Preconditions.checkArgument;
import static com.hotels.styx.client.HttpConfig.newHttpConfigBuilder;
import static com.hotels.styx.client.OriginsInventory.OriginState.ACTIVE;
import static com.hotels.styx.client.OriginsInventory.OriginState.DISABLED;
import static com.hotels.styx.client.OriginsInventory.OriginState.INACTIVE;
import static com.hotels.styx.common.StyxFutures.await;
import static java.util.Collections.emptyMap;
import static java.util.Collections.emptySet;
import static java.util.Collections.singleton;
import static java.util.Objects.isNull;
import static java.util.Objects.nonNull;
Expand Down Expand Up @@ -476,149 +466,59 @@ private OriginState state() {
}
}

public static Builder newOriginsInventoryBuilder(BackendService backendService) {
return new Builder(backendService);
public static Builder newOriginsInventoryBuilder(Id appId) {
return new Builder(appId);
}

/**
* A builder for {@link com.hotels.styx.client.OriginsInventory}.
*/
public static class Builder {
private final BackendService backendService;
private final HttpConfig.Builder httpConfigBuilder = newHttpConfigBuilder();
private OriginHealthStatusMonitor healthStatusMonitor;
private OriginHealthStatusMonitor.Factory originHealthStatusMonitorFactory;
private final Id appId;
private OriginHealthStatusMonitor originHealthMonitor = new NoOriginHealthStatusMonitor();
private MetricRegistry metricsRegistry = new CodaHaleMetricRegistry();
private String version = "";
private ConnectionPool.Factory connectionPoolFactory;
private Connection.Factory connectionFactory;
private EventBus eventBus = new EventBus();
private int clientWorkerThreadsCount = 1;
private OriginStatsFactory originStatsFactory;


public Builder healthStatusMonitor(OriginHealthStatusMonitor healthStatusMonitor) {
this.healthStatusMonitor = healthStatusMonitor;
return this;
}

public Builder originHealthStatusMonitorFactory(OriginHealthStatusMonitor.Factory originHealthStatusMonitorFactory) {
this.originHealthStatusMonitorFactory = originHealthStatusMonitorFactory;
return this;
}
private ConnectionPool.Factory connectionPoolFactory;
private Set<Origin> initialOrigins = emptySet();

public Builder metricsRegistry(MetricRegistry metricsRegistry) {
this.metricsRegistry = metricsRegistry;
return this;
}

public Builder version(String version) {
this.version = version;
return this;
}

public Builder connectionPoolFactory(ConnectionPool.Factory connectionPoolFactory) {
this.connectionPoolFactory = connectionPoolFactory;
this.connectionPoolFactory = requireNonNull(connectionPoolFactory);
return this;
}

public Builder connectionFactory(Connection.Factory connectionFactory) {
this.connectionFactory = connectionFactory;
public Builder originHealthMonitor(OriginHealthStatusMonitor originHealthMonitor) {
this.originHealthMonitor = requireNonNull(originHealthMonitor);
return this;
}

public Builder eventBus(EventBus eventBus) {
this.eventBus = eventBus;
this.eventBus = requireNonNull(eventBus);
return this;
}

public Builder clientWorkerThreadsCount(int clientWorkerThreadsCount) {
this.clientWorkerThreadsCount = clientWorkerThreadsCount;
// TODO: Mikko: seems bit pointless?
public Builder initialOrigins(Set<Origin> origins) {
this.initialOrigins = ImmutableSet.copyOf(origins);
return this;
}

public Builder(BackendService backendService) {
this.backendService = backendService;
}

public Builder originStatsFactory(OriginStatsFactory originStatsFactory) {
this.originStatsFactory = originStatsFactory;
return this;
public Builder(Id appId) {
this.appId = requireNonNull(appId);
}

public OriginsInventory build() {
if (metricsRegistry == null) {
metricsRegistry = new CodaHaleMetricRegistry();
}

await(originHealthMonitor.start());

healthStatusMonitor = Optional.ofNullable(originHealthStatusMonitorFactory)
.orElseGet(OriginHealthStatusMonitorFactory::new)
.create(backendService.id(), backendService.healthCheckConfig(), () -> originHealthCheckFunction(metricsRegistry));

return originsInventory(healthStatusMonitor, httpConfigBuilder.build(), metricsRegistry, originStatsFactory);
}

private OriginsInventory originsInventory(OriginHealthStatusMonitor originHealthStatusMonitor, HttpConfig httpConfig,
MetricRegistry metricsRegistry, OriginStatsFactory originStatsFactory) {
await(originHealthStatusMonitor.start());

ConnectionPool.Factory hostConnectionPoolFactory = connectionPoolFactory(backendService.connectionPoolConfig(), httpConfig, metricsRegistry, originStatsFactory);
OriginsInventory originsInventory = new OriginsInventory(eventBus, backendService.id(), originHealthStatusMonitor, hostConnectionPoolFactory, metricsRegistry);
originsInventory.setOrigins(backendService.origins());
OriginsInventory originsInventory = new OriginsInventory(eventBus, appId, originHealthMonitor, connectionPoolFactory, metricsRegistry);
originsInventory.setOrigins(initialOrigins);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the setOrigins(Set<Origin>) method can be private, and setOrigins(Origin...) can be package-private.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Kyle. The setOrigins(Set<Origin>) is nowadays public. This is because the inventory by design will manage the state for the origins, and the setOrigins provides a way to push more origins into the inventory.


return originsInventory;
}

private OriginHealthCheckFunction originHealthCheckFunction(MetricRegistry metricRegistry) {
NettyConnectionFactory connectionFactory = new NettyConnectionFactory.Builder()
.name("Health-Check-Monitor-" + backendService.id())
.tlsSettings(backendService.tlsSettings().orElse(null))
.build();

ConnectionSettings connectionSettings = new ConnectionSettings(
backendService.connectionPoolConfig().connectTimeoutMillis(),
backendService.healthCheckConfig().timeoutMillis());

HttpClient client = new SimpleNettyHttpClient.Builder()
.userAgent("Styx/" + version)
.connectionDestinationFactory(
new CloseAfterUseConnectionDestination.Factory()
.connectionSettings(connectionSettings)
.connectionFactory(connectionFactory))
.build();

String healthCheckUri = backendService.healthCheckConfig()
.uri()
.orElseThrow(() -> new IllegalArgumentException("Health check URI missing for " + backendService.id()));

return new UrlRequestHealthCheck(healthCheckUri, client, metricRegistry);
}

private ConnectionPool.Factory connectionPoolFactory(ConnectionPool.Settings connectionPoolSettings, HttpConfig httpConfig,
MetricRegistry metricsRegistry, OriginStatsFactory originStatsFactory) {
return connectionPoolFactory != null ? connectionPoolFactory : newConnectionPoolFactory(connectionPoolSettings, httpConfig, metricsRegistry, originStatsFactory);
}

private ConnectionPoolFactory newConnectionPoolFactory(ConnectionPool.Settings connectionPoolSettings,
HttpConfig httpConfig, MetricRegistry metricsRegistry, OriginStatsFactory originStatsFactory) {
Connection.Factory cf = connectionFactory != null
? connectionFactory
: new NettyConnectionFactory.Builder()
.clientWorkerThreadsCount(clientWorkerThreadsCount)
.httpConfig(httpConfig)
.tlsSettings(backendService.tlsSettings().orElse(null))
.responseTimeoutMillis(backendService.responseTimeoutMillis())
.originStatsFactory(originStatsFactory)
.build();

return new ConnectionPoolFactory.Builder()
.connectionFactory(cf)
.connectionPoolSettings(connectionPoolSettings)
.metricRegistry(metricsRegistry)
.build();
}

}

enum OriginState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
import static com.hotels.styx.api.HttpHeaderNames.CONTENT_LENGTH;
import static com.hotels.styx.api.HttpHeaderNames.TRANSFER_ENCODING;
import static com.hotels.styx.client.OriginsInventory.newOriginsInventoryBuilder;
import static com.hotels.styx.client.connectionpool.ConnectionPools.simplePoolFactory;
import static com.hotels.styx.client.stickysession.StickySessionCookie.newStickySessionCookie;
import static io.netty.handler.codec.http.HttpMethod.HEAD;
import static java.util.Collections.emptyList;
Expand Down Expand Up @@ -347,7 +348,10 @@ public StyxHttpClient build() {
}

if (originsInventory == null) {
originsInventory = newOriginsInventoryBuilder(backendService).build();
originsInventory = newOriginsInventoryBuilder(backendService.id())
.connectionPoolFactory(simplePoolFactory(backendService, metricsRegistry))
.initialOrigins(backendService.origins())
.build();
}

if (loadBalancingStrategy == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
import static com.google.common.base.Preconditions.checkNotNull;
import static com.hotels.styx.api.Id.GENERIC_APP;
import static com.hotels.styx.api.client.Origin.checkThatOriginsAreDistinct;
import static com.hotels.styx.client.connectionpool.ConnectionPoolSettings.defaultSettableConnectionPoolSettings;
import static com.hotels.styx.client.connectionpool.ConnectionPoolSettings.defaultConnectionPoolSettings;
import static com.hotels.styx.client.healthcheck.HealthCheckConfig.noHealthCheck;
import static com.hotels.styx.client.stickysession.StickySessionConfig.stickySessionDisabled;
import static java.lang.String.format;
Expand Down Expand Up @@ -224,7 +224,7 @@ public static final class Builder {
private Id id = GENERIC_APP;
private String path = "/";
private Set<Origin> origins = emptySet();
private ConnectionPool.Settings connectionPoolSettings = defaultSettableConnectionPoolSettings();
private ConnectionPool.Settings connectionPoolSettings = defaultConnectionPoolSettings();
private StickySessionConfig stickySessionConfig = stickySessionDisabled();
private HealthCheckConfig healthCheckConfig = noHealthCheck();
private List<RewriteConfig> rewrites = emptyList();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ private ConnectionPoolSettings(Builder builder) {
*
* @return a new instance
*/
public static ConnectionPoolSettings defaultSettableConnectionPoolSettings() {
public static ConnectionPoolSettings defaultConnectionPoolSettings() {
return new ConnectionPoolSettings(new Builder());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/**
* Copyright (C) 2013-2018 Expedia 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 com.hotels.styx.client.connectionpool;

import com.hotels.styx.api.client.ConnectionPool;
import com.hotels.styx.api.client.Origin;
import com.hotels.styx.api.metrics.MetricRegistry;
import com.hotels.styx.client.OriginStatsFactory;
import com.hotels.styx.client.applications.BackendService;
import com.hotels.styx.client.netty.connectionpool.NettyConnectionFactory;

import static com.hotels.styx.client.connectionpool.ConnectionPoolSettings.defaultConnectionPoolSettings;

/**
* Helper routines for building connection pools with default settings.
*/
public final class ConnectionPools {
private ConnectionPools() {
}

public static ConnectionPool poolForOrigin(Origin origin) {
return new SimpleConnectionPool(origin, defaultConnectionPoolSettings(), new NettyConnectionFactory.Builder().build());
}

public static ConnectionPool.Factory simplePoolFactory() {
return ConnectionPools::poolForOrigin;
}

public static ConnectionPool poolForOrigin(Origin origin, MetricRegistry metricRegistry) {
return new StatsReportingConnectionPool(
new SimpleConnectionPool(
origin,
defaultConnectionPoolSettings(),
new NettyConnectionFactory.Builder()
.originStatsFactory(new OriginStatsFactory(metricRegistry))
.build()),
metricRegistry);
}

private static ConnectionPool poolForOrigin(Origin origin, MetricRegistry metricRegistry, NettyConnectionFactory connectionFactory) {
return new StatsReportingConnectionPool(
new SimpleConnectionPool(
origin,
defaultConnectionPoolSettings(),
connectionFactory),
metricRegistry
);
}

public static ConnectionPool.Factory simplePoolFactory(MetricRegistry metricRegistry) {
return origin -> poolForOrigin(origin, metricRegistry);
}

public static ConnectionPool.Factory simplePoolFactory(BackendService backendService, MetricRegistry metricRegistry) {
NettyConnectionFactory connectionFactory = new NettyConnectionFactory.Builder()
.originStatsFactory(new OriginStatsFactory(metricRegistry))
.responseTimeoutMillis(backendService.responseTimeoutMillis())
.build();

return origin -> poolForOrigin(origin, metricRegistry, connectionFactory);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,16 @@ package com.hotels.styx.client

import com.github.tomakehurst.wiremock.client.WireMock
import com.github.tomakehurst.wiremock.client.WireMock._
import com.hotels.styx.support.api.BlockingObservables.waitForResponse
import com.hotels.styx.api.HttpRequest.Builder
import com.hotels.styx.api.client.Origin
import com.hotels.styx.api.messages.HttpResponseStatus.OK
import com.hotels.styx.api.metrics.MetricRegistry
import com.hotels.styx.api.metrics.codahale.CodaHaleMetricRegistry
import com.hotels.styx.client.OriginsInventory.newOriginsInventoryBuilder
import com.hotels.styx.client.StyxHttpClient.newHttpClientBuilder
import com.hotels.styx.client.applications.BackendService
import com.hotels.styx.api.messages.HttpResponseStatus.OK
import com.hotels.styx.client.connectionpool.ConnectionPools.simplePoolFactory
import com.hotels.styx.support.api.BlockingObservables.waitForResponse
import org.scalatest._
import org.scalatest.concurrent.Eventually

Expand All @@ -46,8 +47,10 @@ class ClientConnectionPoolSpec extends FunSuite with BeforeAndAfterAll with Even

val backendService = new BackendService.Builder().origins(originOne).build()

val originsInventory = newOriginsInventoryBuilder(backendService)
val originsInventory = newOriginsInventoryBuilder(backendService.id())
.metricsRegistry(metricRegistry)
.connectionPoolFactory(simplePoolFactory(metricRegistry))
.initialOrigins(backendService.origins)
.build()

client = newHttpClientBuilder(backendService)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.hotels.styx.client.StyxHeaderConfig.ORIGIN_ID_DEFAULT
import com.hotels.styx.client.StyxHttpClient.newHttpClientBuilder
import com.hotels.styx.client.applications.BackendService
import com.hotels.styx.client.connectionpool.ConnectionPoolSettings
import com.hotels.styx.client.connectionpool.ConnectionPools.simplePoolFactory
import com.hotels.styx.client.retry.RetryNTimes
import com.hotels.styx.client.stickysession.StickySessionConfig
import com.hotels.styx.support.api.BlockingObservables.waitForResponse
Expand Down Expand Up @@ -184,7 +185,10 @@ class RetryHandlingSpec extends FunSuite with BeforeAndAfterAll with Matchers wi
.stickySessionConfig(StickySessionEnabled)
.build()

val originsInventory = newOriginsInventoryBuilder(backendService).build()
val originsInventory = newOriginsInventoryBuilder(backendService.id())
.connectionPoolFactory(simplePoolFactory())
.initialOrigins(backendService.origins)
.build()

val client: StyxHttpClient = newHttpClientBuilder(backendService)
.originsInventory(originsInventory)
Expand Down
Loading