From ab795ddcaccd26bac56d8eae9dfb928142ace9ab Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Wed, 22 Jan 2020 12:55:20 +0000 Subject: [PATCH 01/16] temp save --- .../FlowControllingHttpContentProducer.java | 5 +- .../java/com/hotels/styx/ExecutorFactory.java | 35 +++ .../hotels/styx/routing/config/Builtins.java | 32 +++ .../routing/config/RoutingObjectFactory.java | 12 +- .../styx/startup/StyxServerComponents.java | 29 +- .../com/hotels/styx/StyxObjectRecord.kt | 2 +- .../styx/executors/NettyExecutorFactory.kt | 43 +++ .../com/hotels/styx/servers/StyxHttpServer.kt | 33 ++- .../test/kotlin/com/hotels/styx/Support.kt | 14 +- .../hotels/styx/servers/StyxHttpServerTest.kt | 258 +++++++++++++++++- .../proxy/src/test/resources/logback.xml | 2 +- 11 files changed, 431 insertions(+), 34 deletions(-) create mode 100644 components/proxy/src/main/java/com/hotels/styx/ExecutorFactory.java create mode 100644 components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt diff --git a/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java b/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java index d36fccaced..200f167d9c 100644 --- a/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java +++ b/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java @@ -95,12 +95,13 @@ enum ProducerState { this.stateMachine = new StateMachine.Builder() .initialState(BUFFERING) + .transition(BUFFERING, ContentSubscribedEvent.class, this::contentSubscribedInBuffering) .transition(BUFFERING, RxBackpressureRequestEvent.class, this::rxBackpressureRequestInBuffering) + .transition(BUFFERING, ContentChunkEvent.class, this::contentChunkInBuffering) + .transition(BUFFERING, ContentEndEvent.class, this::contentEndEventWhileBuffering) .transition(BUFFERING, ChannelInactiveEvent.class, this::releaseAndTerminate) .transition(BUFFERING, ChannelExceptionEvent.class, this::releaseAndTerminate) - .transition(BUFFERING, ContentSubscribedEvent.class, this::contentSubscribedInBuffering) - .transition(BUFFERING, ContentEndEvent.class, this::contentEndEventWhileBuffering) .transition(BUFFERING_COMPLETED, RxBackpressureRequestEvent.class, this::rxBackpressureRequestInBufferingCompleted) .transition(BUFFERING_COMPLETED, ContentChunkEvent.class, this::spuriousContentChunkEvent) diff --git a/components/proxy/src/main/java/com/hotels/styx/ExecutorFactory.java b/components/proxy/src/main/java/com/hotels/styx/ExecutorFactory.java new file mode 100644 index 0000000000..1c84d569b4 --- /dev/null +++ b/components/proxy/src/main/java/com/hotels/styx/ExecutorFactory.java @@ -0,0 +1,35 @@ +/* + Copyright (C) 2013-2020 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; + +import com.fasterxml.jackson.databind.JsonNode; + +/** + * A generic factory that can be implemented to create executor objects whose type is not known + * until read from configuration. + * + */ +public interface ExecutorFactory { + /** + * Create an executor instance. + * + * @param name Executor name + * @param configuration Styx executor configuration + * + * @return Styx service instance + */ + NettyExecutor create(String name, JsonNode configuration); +} diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java index fdcd88dd18..db2f156955 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java @@ -16,11 +16,14 @@ package com.hotels.styx.routing.config; import com.google.common.collect.ImmutableMap; +import com.hotels.styx.ExecutorFactory; import com.hotels.styx.InetServer; +import com.hotels.styx.NettyExecutor; import com.hotels.styx.api.Eventual; import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.extension.service.spi.StyxService; import com.hotels.styx.config.schema.Schema; +import com.hotels.styx.executors.NettyExecutorFactory; import com.hotels.styx.routing.RoutingObject; import com.hotels.styx.routing.db.StyxObjectStore; import com.hotels.styx.routing.handlers.ConditionRouter; @@ -90,10 +93,19 @@ YAML_FILE_CONFIGURATION_SERVICE, new YamlFileConfigurationServiceFactory() "HttpServer", new StyxHttpServerFactory() ); + public static final ImmutableMap BUILTIN_SERVER_SCHEMAS = ImmutableMap.of( "HttpServer", StyxHttpServer.SCHEMA ); + public static final ImmutableMap BUILTIN_EXECUTOR_FACTORIES = ImmutableMap.of( + "NettyExecutor", new NettyExecutorFactory() + ); + + public static final ImmutableMap BUILTIN_EXECUTOR_SCHEMAS = ImmutableMap.of( + "NettyExecutor", NettyExecutorFactory.SCHEMA + ); + public static final RouteRefLookup DEFAULT_REFERENCE_LOOKUP = reference -> (request, ctx) -> Eventual.of(response(NOT_FOUND) .body(format("Handler not found for '%s'.", reference), UTF_8) @@ -220,4 +232,24 @@ public static InetServer buildServer( return constructor.create(name, context, serverDef.config(), serverDb); } + + /** + * Builds a Styx server. + * + * Styx server is a service that can accept incoming traffic from the client hosts. + * + * @param name Styx service name + * @param serverDef Styx service object configuration + * @param factories Service provider factories by name + * + * @return a Styx service + */ + public static NettyExecutor buildExecutor( + String name, + StyxObjectDefinition serverDef, + Map factories) { + ExecutorFactory constructor = factories.get(serverDef.type()); + checkArgument(constructor != null, format("Unknown executor type '%s' for '%s' provider", serverDef.type(), serverDef.name())); + return constructor.create(name, serverDef.config()); + } } diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java b/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java index 9a443eebbd..f53c7c0dc8 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -16,6 +16,9 @@ package com.hotels.styx.routing.config; import com.hotels.styx.Environment; +import com.hotels.styx.NettyExecutor; +import com.hotels.styx.StyxObjectRecord; +import com.hotels.styx.api.configuration.ObjectStore; import com.hotels.styx.proxy.plugin.NamedPlugin; import com.hotels.styx.routing.RoutingObject; import com.hotels.styx.routing.RoutingObjectRecord; @@ -61,6 +64,7 @@ class Context { private final Iterable plugins; private final Map interceptorFactories; private final boolean requestTracking; + private StyxObjectStore> executorObjectStore; public Context( RouteRefLookup refLookup, @@ -69,7 +73,8 @@ public Context( Map objectFactories, Iterable plugins, Map interceptorFactories, - boolean requestTracking) { + boolean requestTracking, + StyxObjectStore> executorObjectStore) { this.refLookup = refLookup; this.environment = requireNonNull(environment); this.routeDb = requireNonNull(routeDb); @@ -77,6 +82,7 @@ public Context( this.plugins = requireNonNull(plugins); this.interceptorFactories = requireNonNull(interceptorFactories); this.requestTracking = requestTracking; + this.executorObjectStore = executorObjectStore; } public Environment environment() { @@ -106,5 +112,7 @@ public boolean requestTracking() { public RouteRefLookup refLookup() { return refLookup; } + + public ObjectStore> executors() { return executorObjectStore; }; } } diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 964c22ee8e..2b36945c95 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -53,6 +53,7 @@ import static com.hotels.styx.StartupConfig.newStartupConfigBuilder; import static com.hotels.styx.Version.readVersionFrom; import static com.hotels.styx.infrastructure.logging.LOGBackConfigurer.initLogging; +import static com.hotels.styx.routing.config.Builtins.BUILTIN_EXECUTOR_FACTORIES; import static com.hotels.styx.routing.config.Builtins.BUILTIN_HANDLER_FACTORIES; import static com.hotels.styx.routing.config.Builtins.BUILTIN_SERVER_FACTORIES; import static com.hotels.styx.routing.config.Builtins.BUILTIN_SERVICE_PROVIDER_FACTORIES; @@ -76,6 +77,7 @@ public class StyxServerComponents { private final StyxObjectStore routeObjectStore = new StyxObjectStore<>(); private final StyxObjectStore> providerObjectStore = new StyxObjectStore<>(); private final StyxObjectStore> serverObjectStore = new StyxObjectStore<>(); + private final StyxObjectStore> executorObjectStore = new StyxObjectStore<>(); private final RoutingObjectFactory.Context routingObjectContext; private final StartupConfig startupConfig; @@ -102,6 +104,23 @@ private StyxServerComponents(Builder builder) { ? loadPlugins(environment) : loadPlugins(environment, builder.configuredPluginFactories); + this.services = mergeServices( + builder.servicesLoader.load(environment, routeObjectStore), + builder.additionalServices + ); + + this.plugins.forEach(plugin -> this.environment.plugins().add(plugin)); + + this.environment.configuration().get("executors", JsonNode.class) + .map(StyxServerComponents::readComponents) + .orElse(ImmutableMap.of()) + .forEach((name, definition) -> { + LOGGER.warn("Loading styx server: " + name + ": " + definition); + NettyExecutor provider = Builtins.buildExecutor(name, definition, BUILTIN_EXECUTOR_FACTORIES); + StyxObjectRecord record = new StyxObjectRecord<>(definition.type(), ImmutableSet.copyOf(definition.tags()), definition.config(), provider); + executorObjectStore.insert(name, record); + }); + this.routingObjectContext = new RoutingObjectFactory.Context( new RouteDbRefLookup(this.routeObjectStore), environment, @@ -109,14 +128,8 @@ private StyxServerComponents(Builder builder) { routingObjectFactories, plugins, INTERCEPTOR_FACTORIES, - false); - - this.services = mergeServices( - builder.servicesLoader.load(environment, routeObjectStore), - builder.additionalServices - ); - - this.plugins.forEach(plugin -> this.environment.plugins().add(plugin)); + false, + executorObjectStore); this.environment.configuration().get("routingObjects", JsonNode.class) .map(StyxServerComponents::readComponents) diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/StyxObjectRecord.kt b/components/proxy/src/main/kotlin/com/hotels/styx/StyxObjectRecord.kt index c1971a6731..3b26c14873 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/StyxObjectRecord.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/StyxObjectRecord.kt @@ -21,7 +21,7 @@ import com.hotels.styx.api.extension.service.spi.StyxService /** * A routing object and its associated configuration metadata. */ -data class StyxObjectRecord( +data class StyxObjectRecord( val type: String, val tags: Set, val config: JsonNode, diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt b/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt new file mode 100644 index 0000000000..79f61cd262 --- /dev/null +++ b/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt @@ -0,0 +1,43 @@ +/* + Copyright (C) 2013-2020 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.executors + +import com.fasterxml.jackson.databind.JsonNode +import com.hotels.styx.ExecutorFactory +import com.hotels.styx.NettyExecutor +import com.hotels.styx.config.schema.SchemaDsl +import com.hotels.styx.infrastructure.configuration.yaml.JsonNodeConfig + +class NettyExecutorFactory : ExecutorFactory { + private fun parseConfig(configuration: JsonNode) = JsonNodeConfig(configuration).`as`(NettyExecutorConfig::class.java) + + override fun create(name: String, configuration: JsonNode): NettyExecutor { + val config = parseConfig(configuration) + return NettyExecutor.create(config.namePattern, config.count) + } + + companion object { + @JvmField + val SCHEMA = SchemaDsl.`object`( + SchemaDsl.field("threads", SchemaDsl.integer()), + SchemaDsl.field("namePattern", SchemaDsl.string()) + ) + } +} + +internal data class NettyExecutorConfig( + val count: Int = 0, + val namePattern: String = "netty-executor") diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index d957d23dd7..e38030d32a 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -60,7 +60,10 @@ object StyxHttpServer { optional("maxConnectionsCount", integer()), optional("bossThreadsCount", integer()), - optional("workerThreadsCount", integer()) + optional("workerThreadsCount", integer()), + + optional("bossExecutor", string()), + optional("workerExecutor", string()) ) internal val LOGGER = LoggerFactory.getLogger(StyxHttpServer::class.java) @@ -92,7 +95,10 @@ private data class StyxHttpServerConfiguration( val maxConnectionsCount: Int = 512, val bossThreadsCount: Int = 0, - val workerThreadsCount: Int = 0 + val workerThreadsCount: Int = 0, + + val bossExecutor: String?, + val workerExecutor: String? ) internal class StyxHttpServerFactory : StyxServerFactory { @@ -102,6 +108,23 @@ internal class StyxHttpServerFactory : StyxServerFactory { val config = serverConfig(configuration) val environment = context.environment() + + val bossExecutor = if (config.bossExecutor != null) { + context.executors().get(config.bossExecutor) + .map { it.styxService } + .orElseThrow() + } else { + NettyExecutor.create("Http-Server(localhost-${config.port})-boss", config.bossThreadsCount) + } + + val workerExecutor = if (config.workerExecutor != null) { + context.executors().get(config.workerExecutor) + .map { it.styxService } + .orElseThrow() + } else { + NettyExecutor.create("Http-Server(localhost-${config.port})-worker", config.bossThreadsCount) + } + val proxyServerConfig = ProxyServerConfig.Builder() .setCompressResponses(config.compressResponses) .setMaxInitialLength(config.maxInitialLength) @@ -110,8 +133,6 @@ internal class StyxHttpServerFactory : StyxServerFactory { .setRequestTimeoutMillis(config.requestTimeoutMillis) .setKeepAliveTimeoutMillis(config.keepAliveTimeoutMillis) .setMaxConnectionsCount(config.maxConnectionsCount) - .setBossThreadsCount(config.bossThreadsCount) - .setWorkerThreadsCount(config.workerThreadsCount) .build() return NettyServerBuilder() @@ -142,8 +163,8 @@ internal class StyxHttpServerFactory : StyxServerFactory { .protocols(*config.tlsSettings.protocols.toTypedArray()) .build() })) - .workerExecutor(NettyExecutor.create("Http-Server(localhost-${config.port})", config.workerThreadsCount)) - .bossExecutor(NettyExecutor.create("Http-Server(localhost-${config.port})", config.bossThreadsCount)) + .workerExecutor(workerExecutor) + .bossExecutor(bossExecutor) .handler({ request, ctx -> context.refLookup() .apply(StyxObjectReference(config.handler)) diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt b/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt index 85f2b2db22..01d81c5be9 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt @@ -44,7 +44,6 @@ import io.mockk.every import io.mockk.mockk import org.slf4j.LoggerFactory import reactor.core.publisher.toMono -import java.lang.RuntimeException import java.nio.charset.StandardCharsets.UTF_8 import java.util.concurrent.CompletableFuture import java.util.concurrent.Executor @@ -60,8 +59,17 @@ internal data class RoutingObjectFactoryContext( val objectFactories: Map = BUILTIN_HANDLER_FACTORIES, val plugins: Iterable = listOf(), val interceptorFactories: Map = INTERCEPTOR_FACTORIES, - val requestTracking: Boolean = false) { - fun get() = RoutingObjectFactory.Context(routeRefLookup, environment, objectStore, objectFactories, plugins, INTERCEPTOR_FACTORIES, requestTracking) + val requestTracking: Boolean = false, + val executorObjectStore: StyxObjectStore> = StyxObjectStore()) { + fun get() = RoutingObjectFactory.Context( + routeRefLookup, + environment, + objectStore, + objectFactories, + plugins, + INTERCEPTOR_FACTORIES, + requestTracking, + executorObjectStore) } diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt index e3bad5386d..fa7b2b8d50 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt @@ -16,6 +16,7 @@ package com.hotels.styx.servers import com.hotels.styx.InetServer +import com.hotels.styx.NettyExecutor import com.hotels.styx.StyxObjectRecord import com.hotels.styx.StyxServers.toGuavaService import com.hotels.styx.api.ByteStream @@ -58,9 +59,11 @@ class StyxHttpServerTest : FeatureSpec({ val serverConfig = configBlock(""" port: 0 handler: aHandler + workerExecutor: worker + bossExecutor: boss """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) try { @@ -90,7 +93,7 @@ class StyxHttpServerTest : FeatureSpec({ sslProvider: JDK """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) try { @@ -158,7 +161,7 @@ class StyxHttpServerTest : FeatureSpec({ maxInitialLength: 100 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -195,7 +198,7 @@ class StyxHttpServerTest : FeatureSpec({ maxHeaderSize: 1024 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -235,7 +238,7 @@ class StyxHttpServerTest : FeatureSpec({ requestTimeoutMillis: 50 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) try { @@ -270,7 +273,7 @@ class StyxHttpServerTest : FeatureSpec({ keepAliveTimeoutMillis: 500 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -312,11 +315,9 @@ class StyxHttpServerTest : FeatureSpec({ port: 0 handler: aggregator maxConnectionsCount: 2 - bossThreadsCount: 1 - workerThreadsCount: 1 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -335,8 +336,243 @@ class StyxHttpServerTest : FeatureSpec({ guavaServer.stopAsync().awaitTerminated() } + feature("Uses named executor") { + val serverConfig = configBlock(""" + port: 0 + handler: aggregator + maxConnectionsCount: 2 + """.trimIndent()) + + val server = StyxHttpServerFactory().create("test-01", routingContext.copy(executorObjectStore = executors).get(), serverConfig, db) + val guavaServer = toGuavaService(server) + guavaServer.startAsync().awaitRunning() + + StyxHttpClient.Builder().build().send(get("/a/" + "b".repeat(95)) + .header(HOST, "localhost:${server.inetAddress().port}") + .build())!! + .wait()!! + .let { + it.status() shouldBe OK + } + + + println("Thread names: ${threadNames()}") + + guavaServer.stopAsync().awaitTerminated() + } + }) +/* +Thread names: [ +Http-Server(localhost-0)-boss-4-Thread, +Http-Server(localhost-0)-worker-14-Thread, +Http-Server(localhost-0)-boss-14-Thread, +Http-Server(localhost-0)-worker-4-Thread, +Http-Server(localhost-0)-boss-10-Thread, +main, +Http-Server(localhost-0)-worker-6-Thread, +Http-Server(localhost-0)-boss-4-Thread, +Http-Server(localhost-0)-worker-11-Thread, +Styx-Client-6-Thread, +Styx-Client-0-Thread, +Http-Server(localhost-0)-worker-9-Thread, +Http-Server(localhost-0)-worker-12-Thread, +Styx-Client-7-Thread, +Monitor +Ctrl-Break, +Http-Server(localhost-0)-boss-13-Thread, +Http-Server(localhost-0)-worker-8-Thread +Http-Server(localhost-0)-boss-1-Thread +Http-Server(localhost-0)-worker-0-Thread +Http-Server(localhost-0)-worker-2-Thread +Http-Server(localhost-0)-boss-5-Thread +Http-Server(localhost-0)-boss-9-Thread +Http-Server(localhost-0)-boss-9-Thread +Http-Server(localhost-0)-worker-0-Thread +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-worker-14-Thread +Http-Server(localhost-0)-boss-3-Thread +Http-Server(localhost-0)-boss-8-Thread +Http-Server(localhost-0)-worker-10-Thread +Http-Server(localhost-0)-worker-13-Thread +Http-Server(localhost-0)-boss-15-Thread +Http-Server(localhost-0)-boss-7-Thread +Http-Server(localhost-0)-boss-15-Thread +Http-Server(localhost-0)-worker-2-Thread +Http-Server(localhost-0)-worker-6-Thread +Http-Server(localhost-0)-worker-7-Thread +Http-Server(localhost-0)-boss-10-Thread +Styx-Client-1-Thread +Http-Server(localhost-0)-worker-11-Thread +Http-Server(localhost-0)-boss-9-Thread +Http-Server(localhost-0)-boss-5-Thread +Http-Server(localhost-0)-boss-11-Thread +Http-Server(localhost-0)-worker-2-Thread +Http-Server(localhost-0)-boss-12-Thread +Http-Server(localhost-0)-worker-13-Thread +Http-Server(localhost-0)-boss-3-Thread +Http-Server(localhost-0)-worker-7-Thread +Http-Server(localhost-0)-boss-6-Thread +Http-Server(localhost-0)-worker-7-Thread +Netty-Executor-3-Thread +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-worker-8-Thread +Http-Server(localhost-0)-boss-14-Thread +Http-Server(localhost-0)-worker-10-Thread +Http-Server(localhost-0)-boss-7-Thread +Http-Server(localhost-0)-worker-3-Thread +Http-Server(localhost-0)-boss-15-Thread +Http-Server(localhost-0)-boss-1-Thread +Http-Server(localhost-0)-boss-2-Thread +Common-Cleaner +Http-Server(localhost-0)-boss-8-Thread +Http-Server(localhost-0)-boss-12-Thread +Http-Server(localhost-0)-boss-4-Thread +Http-Server(localhost-0)-boss-6-Thread +Http-Server(localhost-0)-worker-6-Thread +Http-Server(localhost-0)-worker-4-Thread +Http-Server(localhost-0)-boss-14-Thread +Http-Server(localhost-0)-boss-11-Thread +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-worker-8-Thread +Http-Server(localhost-0)-worker-3-Thread +Http-Server(localhost-0)-worker-4-Thread +Http-Server(localhost-0)-worker-9-Thread +Http-Server(localhost-0)-worker-2-Thread +Reference Handler +Http-Server(localhost-0)-worker-13-Thread +Http-Server(localhost-0)-worker-2-Thread +Http-Server(localhost-0)-worker-1-Thread +Http-Server(localhost-0)-worker-7-Thread +Http-Server(localhost-0)-boss-7-Thread +Http-Server(localhost-0)-worker-6-Thread +Http-Server(localhost-0)-worker-3-Thread +Styx-Client-3-Thread +Http-Server(localhost-0)-boss-8-Thread +Http-Server(localhost-0)-worker-5-Thread +Finalizer +pool-1-thread-1 +Http-Server(localhost-0)-boss-4-Thread +Http-Server(localhost-0)-worker-3-Thread +Http-Server(localhost-0)-boss-13-Thread +Http-Server(localhost-0)-worker-9-Thread +Http-Server(localhost-0)-worker-5-Thread +Http-Server(localhost-0)-boss-1-Thread +Http-Server(localhost-0)-boss-2-Thread +Http-Server(localhost-0)-boss-6-Thread +Http-Server(localhost-0)-boss-7-Thread +Http-Server(localhost-0)-worker-8-Thread +Http-Server(localhost-0)-worker-1-Thread +Http-Server(localhost-0)-worker-6-Thread +Http-Server(localhost-0)-boss-14-Thread +Http-Server(localhost-0)-boss-5-Thread +Http-Server(localhost-0)-worker-15-Thread +Styx-Client-4-Thread +Http-Server(localhost-0)-boss-13-Thread +Http-Server(localhost-0)-worker-15-Thread +Http-Server(localhost-0)-boss-2-Thread +Http-Server(localhost-0)-boss-11-Thread +Http-Server(localhost-0)-boss-2-Thread +Http-Server(localhost-0)-worker-13-Thread +Http-Server(localhost-0)-boss-6-Thread +Http-Server(localhost-0)-boss-13-Thread +Http-Server(localhost-0)-boss-5-Thread +Http-Server(localhost-0)-boss-12-Thread +Http-Server(localhost-0)-boss-3-Thread +Http-Server(localhost-0)-worker-1-Thread +Http-Server(localhost-0)-worker-15-Thread +Http-Server(localhost-0)-boss-10-Thread +MY_TEST_CLIENT_BOSS-0-Thread +Http-Server(localhost-0)-boss-1-Thread +Http-Server(localhost-0)-worker-12-Thread +Http-Server(localhost-0)-worker-11-Thread +Http-Server(localhost-0)-boss-9-Thread +Http-Server(localhost-0)-worker-0-Thread +Http-Server(localhost-0)-worker-8-Thread +Http-Server(localhost-0)-worker-10-Thread +Http-Server(localhost-0)-boss-2-Thread +Http-Server(localhost-0)-boss-4-Thread +Http-Server(localhost-0)-boss-12-Thread +Http-Server(localhost-0)-worker-11-Thread +Http-Server(localhost-0)-boss-3-Thread +Http-Server(localhost-0)-worker-12-Thread +Netty-Executor-2-Thread +Http-Server(localhost-0)-boss-5-Thread +Http-Server(localhost-0)-boss-13-Thread +Http-Server(localhost-0)-worker-12-Thread +Http-Server(localhost-0)-worker-15-Thread +Http-Server(localhost-0)-boss-8-Thread +Http-Server(localhost-0)-boss-7-Thread +Http-Server(localhost-0)-worker-10-Thread +Http-Server(localhost-0)-worker-14-Thread +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-worker-9-Thread +Http-Server(localhost-0)-boss-12-Thread +Http-Server(localhost-0)-worker-14-Thread +Styx-Client-5-Thread +pool-3-thread-1 +kotlintest-engine-0 +Styx-Client-2-Thread +Http-Server(localhost-0)-boss-11-Thread +Http-Server(localhost-0)-boss-8-Thread +Http-Server(localhost-0)-boss-6-Thread +Http-Server(localhost-0)-worker-3-Thread +Http-Server(localhost-0)-worker-4-Thread +Http-Server(localhost-0)-boss-9-Thread +Http-Server(localhost-0)-worker-9-Thread +Http-Server(localhost-0)-boss-10-Thread +Attach Listener +Http-Server(localhost-0)-worker-4-Thread +Http-Server(localhost-0)-boss-15-Thread +Netty-Executor-0-Thread +Http-Server(localhost-0)-worker-15-Thread +Http-Server(localhost-0)-boss-3-Thread +globalEventExecutor-1-3 +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-boss-15-Thread +Http-Server(localhost-0)-boss-1-Thread +Http-Server(localhost-0)-worker-1-Thread +Http-Server(localhost-0)-worker-13-Thread +ForkJoinPool.commonPool-worker-3 +Http-Server(localhost-0)-worker-5-Thread +Http-Server(localhost-0)-boss-0-Thread +Http-Server(localhost-0)-worker-7-Thread +Http-Server(localhost-0)-worker-5-Thread +Http-Server(localhost-0)-boss-14-Thread +Http-Server(localhost-0)-boss-11-Thread +Signal Dispatcher +Http-Server(localhost-0)-worker-5-Thread +Http-Server(localhost-0)-worker-1-Thread +pool-2-thread-1 @coroutine#33 +MY_TEST_CLIENT_WORKER-0-Thread +Http-Server(localhost-0)-boss-10-Thread +Http-Server(localhost-0)-worker-12-Thread +Http-Server(localhost-0)-worker-11-Thread +Http-Server(localhost-0)-worker-14-Thread +Netty-Executor-1-Thread +Http-Server(localhost-0)-worker-10-Thread] + + */ + +fun createExecutors(): StyxObjectStore> = StyxObjectStore>() + .let { + it.insert("worker", StyxObjectRecord("NettyExecutor", setOf(), configBlock("a: b"), NettyExecutor.create("MY_TEST_SERVER_WORKER", 1))) + it.insert("boss", StyxObjectRecord("NettyExecutor", setOf(), configBlock("a: b"), NettyExecutor.create("MY_TEST_SERVER_BOSS", 1))) + it + } + +fun threadCount(namePattern: String) = Thread.getAllStackTraces().keys + .map { it.name } + .filter { it.contains(namePattern) } + .count() + +fun threadNames() = Thread.getAllStackTraces().keys + .map { it.name } + + +val executors = createExecutors() + private fun createConnection(port: Int) = NettyConnectionFactory.Builder() .build() .createConnection(newOriginBuilder("localhost", port).build(), ConnectionSettings(250)) @@ -369,8 +605,8 @@ private val routingContext = RoutingObjectFactoryContext( .aggregate(1024) .flatMap { Eventual.of(response.stream()) } }) - }) - .get() + }, + executorObjectStore = executors) private fun ungzip(content: ByteArray, charset: Charset): String = GZIPInputStream(content.inputStream()).bufferedReader(charset).use { it.readText() } diff --git a/components/proxy/src/test/resources/logback.xml b/components/proxy/src/test/resources/logback.xml index c1fd57d080..b0e9ff26a2 100644 --- a/components/proxy/src/test/resources/logback.xml +++ b/components/proxy/src/test/resources/logback.xml @@ -7,7 +7,7 @@ - + From 674b4a446c167c5a420b051a685941d7e8b0b0af Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Tue, 11 Feb 2020 11:44:46 +0000 Subject: [PATCH 02/16] Tidy up. --- .../FlowControllingHttpContentProducer.java | 7 +- .../hotels/styx/routing/config/Builtins.java | 1 - .../styx/startup/StyxServerComponents.java | 26 +-- .../hotels/styx/servers/StyxHttpServerTest.kt | 194 +----------------- 4 files changed, 17 insertions(+), 211 deletions(-) diff --git a/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java b/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java index 200f167d9c..a9ad4e1c09 100644 --- a/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java +++ b/components/client/src/main/java/com/hotels/styx/client/netty/connectionpool/FlowControllingHttpContentProducer.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -95,13 +95,12 @@ enum ProducerState { this.stateMachine = new StateMachine.Builder() .initialState(BUFFERING) - .transition(BUFFERING, ContentSubscribedEvent.class, this::contentSubscribedInBuffering) .transition(BUFFERING, RxBackpressureRequestEvent.class, this::rxBackpressureRequestInBuffering) - .transition(BUFFERING, ContentChunkEvent.class, this::contentChunkInBuffering) - .transition(BUFFERING, ContentEndEvent.class, this::contentEndEventWhileBuffering) .transition(BUFFERING, ChannelInactiveEvent.class, this::releaseAndTerminate) .transition(BUFFERING, ChannelExceptionEvent.class, this::releaseAndTerminate) + .transition(BUFFERING, ContentSubscribedEvent.class, this::contentSubscribedInBuffering) + .transition(BUFFERING, ContentEndEvent.class, this::contentEndEventWhileBuffering) .transition(BUFFERING_COMPLETED, RxBackpressureRequestEvent.class, this::rxBackpressureRequestInBufferingCompleted) .transition(BUFFERING_COMPLETED, ContentChunkEvent.class, this::spuriousContentChunkEvent) diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java index db2f156955..4d206364b5 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java @@ -93,7 +93,6 @@ YAML_FILE_CONFIGURATION_SERVICE, new YamlFileConfigurationServiceFactory() "HttpServer", new StyxHttpServerFactory() ); - public static final ImmutableMap BUILTIN_SERVER_SCHEMAS = ImmutableMap.of( "HttpServer", StyxHttpServer.SCHEMA ); diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 2b36945c95..a25d44aa02 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -98,19 +98,6 @@ private StyxServerComponents(Builder builder) { this.executor = NettyExecutor.create("Styx-Client-Worker", environment.configuration().proxyServerConfig().clientWorkerThreadsCount()); - // TODO In further refactoring, we will probably want this loading to happen outside of this constructor call, - // so that it doesn't delay the admin server from starting up - this.plugins = builder.configuredPluginFactories.isEmpty() - ? loadPlugins(environment) - : loadPlugins(environment, builder.configuredPluginFactories); - - this.services = mergeServices( - builder.servicesLoader.load(environment, routeObjectStore), - builder.additionalServices - ); - - this.plugins.forEach(plugin -> this.environment.plugins().add(plugin)); - this.environment.configuration().get("executors", JsonNode.class) .map(StyxServerComponents::readComponents) .orElse(ImmutableMap.of()) @@ -121,6 +108,19 @@ private StyxServerComponents(Builder builder) { executorObjectStore.insert(name, record); }); + this.services = mergeServices( + builder.servicesLoader.load(environment, routeObjectStore), + builder.additionalServices + ); + + // TODO In further refactoring, we will probably want this loading to happen outside of this constructor call, + // so that it doesn't delay the admin server from starting up + this.plugins = builder.configuredPluginFactories.isEmpty() + ? loadPlugins(environment) + : loadPlugins(environment, builder.configuredPluginFactories); + + this.plugins.forEach(plugin -> this.environment.plugins().add(plugin)); + this.routingObjectContext = new RoutingObjectFactory.Context( new RouteDbRefLookup(this.routeObjectStore), environment, diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt index fa7b2b8d50..3036724f20 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt @@ -122,7 +122,7 @@ class StyxHttpServerTest : FeatureSpec({ compressResponses: true """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext, serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -363,198 +363,6 @@ class StyxHttpServerTest : FeatureSpec({ }) -/* -Thread names: [ -Http-Server(localhost-0)-boss-4-Thread, -Http-Server(localhost-0)-worker-14-Thread, -Http-Server(localhost-0)-boss-14-Thread, -Http-Server(localhost-0)-worker-4-Thread, -Http-Server(localhost-0)-boss-10-Thread, -main, -Http-Server(localhost-0)-worker-6-Thread, -Http-Server(localhost-0)-boss-4-Thread, -Http-Server(localhost-0)-worker-11-Thread, -Styx-Client-6-Thread, -Styx-Client-0-Thread, -Http-Server(localhost-0)-worker-9-Thread, -Http-Server(localhost-0)-worker-12-Thread, -Styx-Client-7-Thread, -Monitor -Ctrl-Break, -Http-Server(localhost-0)-boss-13-Thread, -Http-Server(localhost-0)-worker-8-Thread -Http-Server(localhost-0)-boss-1-Thread -Http-Server(localhost-0)-worker-0-Thread -Http-Server(localhost-0)-worker-2-Thread -Http-Server(localhost-0)-boss-5-Thread -Http-Server(localhost-0)-boss-9-Thread -Http-Server(localhost-0)-boss-9-Thread -Http-Server(localhost-0)-worker-0-Thread -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-worker-14-Thread -Http-Server(localhost-0)-boss-3-Thread -Http-Server(localhost-0)-boss-8-Thread -Http-Server(localhost-0)-worker-10-Thread -Http-Server(localhost-0)-worker-13-Thread -Http-Server(localhost-0)-boss-15-Thread -Http-Server(localhost-0)-boss-7-Thread -Http-Server(localhost-0)-boss-15-Thread -Http-Server(localhost-0)-worker-2-Thread -Http-Server(localhost-0)-worker-6-Thread -Http-Server(localhost-0)-worker-7-Thread -Http-Server(localhost-0)-boss-10-Thread -Styx-Client-1-Thread -Http-Server(localhost-0)-worker-11-Thread -Http-Server(localhost-0)-boss-9-Thread -Http-Server(localhost-0)-boss-5-Thread -Http-Server(localhost-0)-boss-11-Thread -Http-Server(localhost-0)-worker-2-Thread -Http-Server(localhost-0)-boss-12-Thread -Http-Server(localhost-0)-worker-13-Thread -Http-Server(localhost-0)-boss-3-Thread -Http-Server(localhost-0)-worker-7-Thread -Http-Server(localhost-0)-boss-6-Thread -Http-Server(localhost-0)-worker-7-Thread -Netty-Executor-3-Thread -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-worker-8-Thread -Http-Server(localhost-0)-boss-14-Thread -Http-Server(localhost-0)-worker-10-Thread -Http-Server(localhost-0)-boss-7-Thread -Http-Server(localhost-0)-worker-3-Thread -Http-Server(localhost-0)-boss-15-Thread -Http-Server(localhost-0)-boss-1-Thread -Http-Server(localhost-0)-boss-2-Thread -Common-Cleaner -Http-Server(localhost-0)-boss-8-Thread -Http-Server(localhost-0)-boss-12-Thread -Http-Server(localhost-0)-boss-4-Thread -Http-Server(localhost-0)-boss-6-Thread -Http-Server(localhost-0)-worker-6-Thread -Http-Server(localhost-0)-worker-4-Thread -Http-Server(localhost-0)-boss-14-Thread -Http-Server(localhost-0)-boss-11-Thread -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-worker-8-Thread -Http-Server(localhost-0)-worker-3-Thread -Http-Server(localhost-0)-worker-4-Thread -Http-Server(localhost-0)-worker-9-Thread -Http-Server(localhost-0)-worker-2-Thread -Reference Handler -Http-Server(localhost-0)-worker-13-Thread -Http-Server(localhost-0)-worker-2-Thread -Http-Server(localhost-0)-worker-1-Thread -Http-Server(localhost-0)-worker-7-Thread -Http-Server(localhost-0)-boss-7-Thread -Http-Server(localhost-0)-worker-6-Thread -Http-Server(localhost-0)-worker-3-Thread -Styx-Client-3-Thread -Http-Server(localhost-0)-boss-8-Thread -Http-Server(localhost-0)-worker-5-Thread -Finalizer -pool-1-thread-1 -Http-Server(localhost-0)-boss-4-Thread -Http-Server(localhost-0)-worker-3-Thread -Http-Server(localhost-0)-boss-13-Thread -Http-Server(localhost-0)-worker-9-Thread -Http-Server(localhost-0)-worker-5-Thread -Http-Server(localhost-0)-boss-1-Thread -Http-Server(localhost-0)-boss-2-Thread -Http-Server(localhost-0)-boss-6-Thread -Http-Server(localhost-0)-boss-7-Thread -Http-Server(localhost-0)-worker-8-Thread -Http-Server(localhost-0)-worker-1-Thread -Http-Server(localhost-0)-worker-6-Thread -Http-Server(localhost-0)-boss-14-Thread -Http-Server(localhost-0)-boss-5-Thread -Http-Server(localhost-0)-worker-15-Thread -Styx-Client-4-Thread -Http-Server(localhost-0)-boss-13-Thread -Http-Server(localhost-0)-worker-15-Thread -Http-Server(localhost-0)-boss-2-Thread -Http-Server(localhost-0)-boss-11-Thread -Http-Server(localhost-0)-boss-2-Thread -Http-Server(localhost-0)-worker-13-Thread -Http-Server(localhost-0)-boss-6-Thread -Http-Server(localhost-0)-boss-13-Thread -Http-Server(localhost-0)-boss-5-Thread -Http-Server(localhost-0)-boss-12-Thread -Http-Server(localhost-0)-boss-3-Thread -Http-Server(localhost-0)-worker-1-Thread -Http-Server(localhost-0)-worker-15-Thread -Http-Server(localhost-0)-boss-10-Thread -MY_TEST_CLIENT_BOSS-0-Thread -Http-Server(localhost-0)-boss-1-Thread -Http-Server(localhost-0)-worker-12-Thread -Http-Server(localhost-0)-worker-11-Thread -Http-Server(localhost-0)-boss-9-Thread -Http-Server(localhost-0)-worker-0-Thread -Http-Server(localhost-0)-worker-8-Thread -Http-Server(localhost-0)-worker-10-Thread -Http-Server(localhost-0)-boss-2-Thread -Http-Server(localhost-0)-boss-4-Thread -Http-Server(localhost-0)-boss-12-Thread -Http-Server(localhost-0)-worker-11-Thread -Http-Server(localhost-0)-boss-3-Thread -Http-Server(localhost-0)-worker-12-Thread -Netty-Executor-2-Thread -Http-Server(localhost-0)-boss-5-Thread -Http-Server(localhost-0)-boss-13-Thread -Http-Server(localhost-0)-worker-12-Thread -Http-Server(localhost-0)-worker-15-Thread -Http-Server(localhost-0)-boss-8-Thread -Http-Server(localhost-0)-boss-7-Thread -Http-Server(localhost-0)-worker-10-Thread -Http-Server(localhost-0)-worker-14-Thread -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-worker-9-Thread -Http-Server(localhost-0)-boss-12-Thread -Http-Server(localhost-0)-worker-14-Thread -Styx-Client-5-Thread -pool-3-thread-1 -kotlintest-engine-0 -Styx-Client-2-Thread -Http-Server(localhost-0)-boss-11-Thread -Http-Server(localhost-0)-boss-8-Thread -Http-Server(localhost-0)-boss-6-Thread -Http-Server(localhost-0)-worker-3-Thread -Http-Server(localhost-0)-worker-4-Thread -Http-Server(localhost-0)-boss-9-Thread -Http-Server(localhost-0)-worker-9-Thread -Http-Server(localhost-0)-boss-10-Thread -Attach Listener -Http-Server(localhost-0)-worker-4-Thread -Http-Server(localhost-0)-boss-15-Thread -Netty-Executor-0-Thread -Http-Server(localhost-0)-worker-15-Thread -Http-Server(localhost-0)-boss-3-Thread -globalEventExecutor-1-3 -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-boss-15-Thread -Http-Server(localhost-0)-boss-1-Thread -Http-Server(localhost-0)-worker-1-Thread -Http-Server(localhost-0)-worker-13-Thread -ForkJoinPool.commonPool-worker-3 -Http-Server(localhost-0)-worker-5-Thread -Http-Server(localhost-0)-boss-0-Thread -Http-Server(localhost-0)-worker-7-Thread -Http-Server(localhost-0)-worker-5-Thread -Http-Server(localhost-0)-boss-14-Thread -Http-Server(localhost-0)-boss-11-Thread -Signal Dispatcher -Http-Server(localhost-0)-worker-5-Thread -Http-Server(localhost-0)-worker-1-Thread -pool-2-thread-1 @coroutine#33 -MY_TEST_CLIENT_WORKER-0-Thread -Http-Server(localhost-0)-boss-10-Thread -Http-Server(localhost-0)-worker-12-Thread -Http-Server(localhost-0)-worker-11-Thread -Http-Server(localhost-0)-worker-14-Thread -Netty-Executor-1-Thread -Http-Server(localhost-0)-worker-10-Thread] - - */ - fun createExecutors(): StyxObjectStore> = StyxObjectStore>() .let { it.insert("worker", StyxObjectRecord("NettyExecutor", setOf(), configBlock("a: b"), NettyExecutor.create("MY_TEST_SERVER_WORKER", 1))) From 33807ed6ae16f732faa321a6f48071242aca372d Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Tue, 11 Feb 2020 14:52:26 +0000 Subject: [PATCH 03/16] Tests pass. --- .../main/java/com/hotels/styx/StyxServer.java | 25 ++++++--- .../hotels/styx/admin/AdminServerBuilder.java | 14 +++-- .../styx/startup/StyxServerComponents.java | 21 ++++++++ .../com/hotels/styx/servers/StyxHttpServer.kt | 20 ++++--- .../com/hotels/styx/services/HealthChecks.kt | 5 +- .../java/com/hotels/styx/StyxServerTest.java | 13 +++-- .../RequestEnrichingInterceptorTest.java | 1 + .../startup/StyxServerComponentsTest.java | 8 ++- .../HealthCheckMonitoringServiceTest.kt | 10 +++- .../hotels/styx/server/netty/NettyServer.java | 38 +++---------- .../styx/server/netty/NettyServerBuilder.java | 53 +++++++++++-------- .../com/hotels/styx/testapi/StyxServer.java | 4 +- .../com/hotels/styx/StyxServerSupport.scala | 5 +- .../support/configuration/StyxConfig.scala | 9 +++- 14 files changed, 140 insertions(+), 86 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java index f7bc796c37..15c22d5944 100644 --- a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java +++ b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java @@ -84,6 +84,8 @@ public final class StyxServer extends AbstractService { private final ServiceManager phase2Services; private final Stopwatch stopwatch; private final StyxServerComponents components; + private NettyExecutor proxyBossExecutor; + private NettyExecutor proxyWorkerExecutor; public static void main(String[] args) { try { @@ -109,8 +111,10 @@ private static StyxServer createStyxServer(String[] args) { LOG.info("Styx configFileLocation={}", startupConfig.configFileLocation()); LOG.info("Styx logConfigLocation={}", startupConfig.logConfigLocation()); + StyxConfig styxConfig = parseConfiguration(startupConfig); + StyxServerComponents components = new StyxServerComponents.Builder() - .styxConfig(parseConfiguration(startupConfig)) + .styxConfig(styxConfig) .startupConfig(startupConfig) .loggingSetUp(environment -> activateLogbackConfigurer(startupConfig)) .build(); @@ -192,14 +196,18 @@ public StyxServer(StyxServerComponents components, Stopwatch stopwatch) { // Phase 2: start HTTP services; StyxConfig styxConfig = components.environment().configuration(); + + proxyBossExecutor = NettyExecutor.create("Proxy-Boss", styxConfig.proxyServerConfig().bossThreadsCount()); + proxyWorkerExecutor = NettyExecutor.create("Proxy-Worker", styxConfig.proxyServerConfig().workerThreadsCount()); + httpServer = styxConfig.proxyServerConfig() .httpConnectorConfig() - .map(it -> httpServer(components.environment(), it, handlerForOldProxyServer)) + .map(it -> httpServer(components, it, handlerForOldProxyServer)) .orElse(null); httpsServer = styxConfig.proxyServerConfig() .httpsConnectorConfig() - .map(it -> httpServer(components.environment(), it, handlerForOldProxyServer)) + .map(it -> httpServer(components, it, handlerForOldProxyServer)) .orElse(null); ArrayList services2 = new ArrayList<>(); @@ -235,7 +243,8 @@ public InetSocketAddress adminHttpAddress() { return adminServer.inetAddress(); } - private static InetServer httpServer(Environment environment, ConnectorConfig connectorConfig, HttpHandler styxDataPlane) { + private InetServer httpServer(StyxServerComponents components, ConnectorConfig connectorConfig, HttpHandler styxDataPlane) { + Environment environment = components.environment(); CharSequence styxInfoHeaderName = environment.configuration().styxHeaderConfig().styxInfoHeaderName(); ResponseInfoFormat responseInfoFormat = new ResponseInfoFormat(environment); @@ -251,8 +260,8 @@ private static InetServer httpServer(Environment environment, ConnectorConfig co return NettyServerBuilder.newBuilder() .setMetricsRegistry(environment.metricRegistry()) - .bossExecutor(NettyExecutor.create("Proxy-Boss", environment.configuration().proxyServerConfig().bossThreadsCount())) - .workerExecutor(NettyExecutor.create("Proxy-Worker", environment.configuration().proxyServerConfig().workerThreadsCount())) + .bossExecutor(proxyBossExecutor) + .workerExecutor(proxyWorkerExecutor) .setProtocolConnector(proxyConnector) .handler(styxDataPlane) .build(); @@ -304,6 +313,10 @@ protected void doStart() { @Override protected void doStop() { this.phase2Services.stopAsync().awaitStopped(); + + proxyBossExecutor.shut(); + proxyWorkerExecutor.shut(); + this.phase1Services.stopAsync().awaitStopped(); shutdownLogging(true); } diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java index 9dcdef883b..e0dda55a2d 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/AdminServerBuilder.java @@ -122,12 +122,18 @@ public InetServer build() { StyxConfig styxConfig = environment.configuration(); AdminServerConfig adminServerConfig = styxConfig.adminServerConfig(); - NettyExecutor executor = NettyExecutor.create("Admin-Boss", adminServerConfig.bossThreadsCount()); + NettyExecutor bossExecutor = NettyExecutor.create("Admin-Boss", adminServerConfig.bossThreadsCount()); + NettyExecutor workerExecutor = NettyExecutor.create("Admin-Worker", adminServerConfig.workerThreadsCount()); + NettyServerBuilder builder = NettyServerBuilder.newBuilder() .setMetricsRegistry(environment.metricRegistry()) - .bossExecutor(executor) - .workerExecutor(NettyExecutor.create("Admin-Worker", adminServerConfig.workerThreadsCount())) - .handler(adminEndpoints(styxConfig, startupConfig)); + .bossExecutor(bossExecutor) + .workerExecutor(workerExecutor) + .handler(adminEndpoints(styxConfig, startupConfig)) + .shutdownAction(() -> { + bossExecutor.shut(); + workerExecutor.shut(); + }); // Currently admin server cannot be started over TLS protocol. // This appears to be an existing issue that needs rectifying. diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index a25d44aa02..19397717d1 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -80,6 +80,8 @@ public class StyxServerComponents { private final StyxObjectStore> executorObjectStore = new StyxObjectStore<>(); private final RoutingObjectFactory.Context routingObjectContext; private final StartupConfig startupConfig; + private final NettyExecutor bossExecutor; + private final NettyExecutor workerExecutor; private static final Logger LOGGER = getLogger(StyxServerComponents.class); private final NettyExecutor executor; @@ -97,6 +99,8 @@ private StyxServerComponents(Builder builder) { builder.loggingSetUp.setUp(environment); this.executor = NettyExecutor.create("Styx-Client-Worker", environment.configuration().proxyServerConfig().clientWorkerThreadsCount()); + this.bossExecutor = builder.bossExecutor; + this.workerExecutor = builder.workerExecutor; this.environment.configuration().get("executors", JsonNode.class) .map(StyxServerComponents::readComponents) @@ -247,6 +251,14 @@ private static Map mergeServices(Map c .build(); } + public NettyExecutor bossExecutor() { + return this.bossExecutor; + } + + public NettyExecutor workerExecutor() { + return this.workerExecutor; + } + /** * CoreConfig builder. */ @@ -260,6 +272,8 @@ public static final class Builder { private final Map additionalRoutingObjectFactories = new HashMap<>(); private final Map additionalServices = new HashMap<>(); + private NettyExecutor bossExecutor; + private NettyExecutor workerExecutor; public Builder styxConfig(StyxConfig styxConfig) { this.styxConfig = requireNonNull(styxConfig); @@ -328,6 +342,13 @@ public Builder additionalRoutingObjects(Map additi return this; } + public Builder serverExecutors(NettyExecutor bossExecutor, NettyExecutor workerExecutor) { + this.bossExecutor = bossExecutor; + this.workerExecutor = workerExecutor; + + return this; + } + public StyxServerComponents build() { return new StyxServerComponents(this); } diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index e38030d32a..c9705665fb 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -125,21 +125,19 @@ internal class StyxHttpServerFactory : StyxServerFactory { NettyExecutor.create("Http-Server(localhost-${config.port})-worker", config.bossThreadsCount) } - val proxyServerConfig = ProxyServerConfig.Builder() - .setCompressResponses(config.compressResponses) - .setMaxInitialLength(config.maxInitialLength) - .setMaxHeaderSize(config.maxHeaderSize) - .setMaxChunkSize(config.maxChunkSize) - .setRequestTimeoutMillis(config.requestTimeoutMillis) - .setKeepAliveTimeoutMillis(config.keepAliveTimeoutMillis) - .setMaxConnectionsCount(config.maxConnectionsCount) - .build() - return NettyServerBuilder() .setMetricsRegistry(environment.metricRegistry()) .setProtocolConnector( ProxyConnectorFactory( - proxyServerConfig, + ProxyServerConfig.Builder() + .setCompressResponses(config.compressResponses) + .setMaxInitialLength(config.maxInitialLength) + .setMaxHeaderSize(config.maxHeaderSize) + .setMaxChunkSize(config.maxChunkSize) + .setRequestTimeoutMillis(config.requestTimeoutMillis) + .setKeepAliveTimeoutMillis(config.keepAliveTimeoutMillis) + .setMaxConnectionsCount(config.maxConnectionsCount) + .build(), environment.metricRegistry(), environment.errorListener(), environment.configuration().get(ENCODE_UNWISECHARS).orElse(""), diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/services/HealthChecks.kt b/components/proxy/src/main/kotlin/com/hotels/styx/services/HealthChecks.kt index 4bba64d2b8..e91d34d3d0 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/services/HealthChecks.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/services/HealthChecks.kt @@ -19,6 +19,7 @@ import com.hotels.styx.* import com.hotels.styx.api.HttpInterceptor import com.hotels.styx.api.HttpRequest import com.hotels.styx.routing.RoutingObject +import com.hotels.styx.server.HttpInterceptorContext import org.reactivestreams.Publisher import reactor.core.publisher.Mono import reactor.core.publisher.toMono @@ -52,10 +53,10 @@ data class ObjectOther(val state: String) : ObjectHealth() { typealias Probe = (RoutingObject) -> Publisher typealias CheckState = (currentState: ObjectHealth, reachable: Boolean) -> ObjectHealth -fun urlProbe(probe: HttpRequest, timeout: Duration, context: HttpInterceptor.Context): Probe = +fun urlProbe(probe: HttpRequest, timeout: Duration, requestContext: HttpInterceptor.Context): Probe = { routingObject -> routingObject - .handle(probe.stream(), context) + .handle(probe.stream(), requestContext) .map { it.consume() it.status().code() < 400 diff --git a/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java b/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java index a72118bf3b..5bd4c41eea 100644 --- a/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -74,6 +74,8 @@ public class StyxServerTest { private LoggingTestSupport log; private LoggingTestSupport pssLog; + private NettyExecutor bossExecutor = NettyExecutor.create("StyxServerTest-boss", 1); + private NettyExecutor workerExecutor = NettyExecutor.create("StyxServerTest-worker", 1); @BeforeEach public void setUp() { @@ -85,6 +87,8 @@ public void setUp() { public void removeAppender() { log.stop(); pssLog.stop(); + bossExecutor.shut(); + workerExecutor.shut(); } @BeforeAll @@ -162,6 +166,7 @@ public void disablesResourceLeakDetectionByDefault() { StyxServerComponents config = new StyxServerComponents.Builder() .configuration(EMPTY_CONFIGURATION) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(new MemoryBackedRegistry<>()))) + .serverExecutors(bossExecutor, workerExecutor) .build(); new StyxServer(config); @@ -257,20 +262,22 @@ protected CompletableFuture startService() { }; } - private static StyxServer styxServerWithPlugins(Map plugins) { + private StyxServer styxServerWithPlugins(Map plugins) { StyxServerComponents config = new StyxServerComponents.Builder() .configuration(styxConfig()) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(new MemoryBackedRegistry<>()))) .plugins(plugins) + .serverExecutors(bossExecutor, workerExecutor) .build(); return new StyxServer(config); } - private static StyxServer styxServerWithBackendServiceRegistry(StyxService backendServiceRegistry) { + private StyxServer styxServerWithBackendServiceRegistry(StyxService backendServiceRegistry) { StyxServerComponents config = new StyxServerComponents.Builder() .configuration(styxConfig()) .additionalServices(ImmutableMap.of("backendServiceRegistry", backendServiceRegistry)) + .serverExecutors(bossExecutor, workerExecutor) .build(); return new StyxServer(config); diff --git a/components/proxy/src/test/java/com/hotels/styx/proxy/interceptors/RequestEnrichingInterceptorTest.java b/components/proxy/src/test/java/com/hotels/styx/proxy/interceptors/RequestEnrichingInterceptorTest.java index b3faaeb82e..b48153701d 100644 --- a/components/proxy/src/test/java/com/hotels/styx/proxy/interceptors/RequestEnrichingInterceptorTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/proxy/interceptors/RequestEnrichingInterceptorTest.java @@ -99,6 +99,7 @@ public void retainsXForwardedProtoWhenPresentInHttpsMessage() { private static class TestChain implements Chain { private final boolean secure; + TestChain(boolean secure) { this.secure = secure; } diff --git a/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java b/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java index 94c1a7f9b6..2169eeb971 100644 --- a/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,6 +18,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.hotels.styx.Environment; +import com.hotels.styx.NettyExecutor; import com.hotels.styx.StyxConfig; import com.hotels.styx.api.Eventual; import com.hotels.styx.api.configuration.Configuration; @@ -52,6 +53,7 @@ public void setsUpLoggingOnBuild() { new StyxServerComponents.Builder() .styxConfig(new StyxConfig()) .loggingSetUp(loggingSetUp) + .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); verify(loggingSetUp).setUp(any(Environment.class)); @@ -65,6 +67,7 @@ public void loadsPlugins() { StyxServerComponents components = new StyxServerComponents.Builder() .styxConfig(new StyxConfig()) .pluginFactories(ImmutableList.of(f1, f2)) + .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); List plugins = components.plugins(); @@ -80,6 +83,7 @@ public void loadsServices() { .services((env, routeDb) -> ImmutableMap.of( "service1", mock(StyxService.class), "service2", mock(StyxService.class))) + .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Map services = components.services(); @@ -94,6 +98,7 @@ public void exposesAdditionalServices() { .additionalServices(ImmutableMap.of( "service1", mock(StyxService.class), "service2", mock(StyxService.class))) + .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Map services = components.services(); @@ -109,6 +114,7 @@ public void createsEnvironment() { StyxServerComponents components = new StyxServerComponents.Builder() .styxConfig(new StyxConfig(config)) + .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Environment environment = components.environment(); diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/services/HealthCheckMonitoringServiceTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/services/HealthCheckMonitoringServiceTest.kt index afc68630be..7274ff2821 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/services/HealthCheckMonitoringServiceTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/services/HealthCheckMonitoringServiceTest.kt @@ -15,6 +15,7 @@ */ package com.hotels.styx.services +import com.hotels.styx.NettyExecutor import com.hotels.styx.STATE_INACTIVE import com.hotels.styx.api.LiveHttpRequest import com.hotels.styx.lbGroupTag @@ -43,6 +44,8 @@ class HealthCheckMonitoringServiceTest : FeatureSpec({ fun createdTag(tag: String) = tag.matches("created:.*".toRegex()) + val workerExecutor = NettyExecutor.create("monitoringServiceTest", 1) + feature("Lifecycle management") { val scheduledFuture = mockk>(relaxed = true) @@ -69,7 +72,8 @@ class HealthCheckMonitoringServiceTest : FeatureSpec({ period = 100.milliseconds, activeThreshold = 2, inactiveThreshold = 2, - executor = executor + executor = executor, + workerExecutor = workerExecutor ) scenario("Starts scheduled executor") { @@ -138,7 +142,7 @@ class HealthCheckMonitoringServiceTest : FeatureSpec({ record("aaa-02", "x", setOf(lbGroupTag("aaa")), mockk(), handler02) } - val monitor = HealthCheckMonitoringService(objectStore, "aaa", "/healthCheck.txt", 100.milliseconds, 3, 3, executor) + val monitor = HealthCheckMonitoringService(objectStore, "aaa", "/healthCheck.txt", 100.milliseconds, 3, 3, executor, workerExecutor) scenario("Probes discovered objects at specified URL") { monitor.runChecks("aaa", objectStore) @@ -264,4 +268,6 @@ class HealthCheckMonitoringServiceTest : FeatureSpec({ } } } + + workerExecutor.shut() }) diff --git a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServer.java b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServer.java index 8e8cf6f86e..da02706f94 100644 --- a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServer.java +++ b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServer.java @@ -27,14 +27,12 @@ import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelInitializer; import io.netty.channel.group.ChannelGroup; -import io.netty.util.concurrent.Future; import org.slf4j.Logger; import java.net.BindException; import java.net.InetSocketAddress; import java.util.Map; import java.util.Optional; -import java.util.concurrent.Callable; import java.util.concurrent.CompletableFuture; import static com.google.common.base.Throwables.propagate; @@ -48,7 +46,6 @@ import static java.lang.String.format; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Objects.requireNonNull; -import static java.util.concurrent.TimeUnit.MILLISECONDS; import static org.slf4j.LoggerFactory.getLogger; /** @@ -64,8 +61,8 @@ final class NettyServer extends AbstractStyxService implements InetServer { private final String host; private final NettyExecutor bossExecutor; private final NettyExecutor workerExecutor; + private final Runnable shutdownAction; - private volatile Callable stopper; private volatile InetSocketAddress address; NettyServer(NettyServerBuilder nettyServerBuilder) { @@ -76,6 +73,7 @@ final class NettyServer extends AbstractStyxService implements InetServer { this.serverConnector = nettyServerBuilder.protocolConnector(); this.bossExecutor = nettyServerBuilder.bossExecutor(); this.workerExecutor = nettyServerBuilder.workerExecutor(); + this.shutdownAction = nettyServerBuilder.shutdownAction(); } @Override @@ -136,7 +134,6 @@ protected void initChannel(Channel ch) throws Exception { channelGroup.add(channel); address = (InetSocketAddress) channel.localAddress(); LOGGER.info("server connector {} bound successfully on port {} socket port {}", new Object[]{serverConnector.getClass(), port, address}); - stopper = new Stopper(bossExecutor, workerExecutor); serviceFuture.complete(null); } else { LOGGER.warn("Failed to start service={} cause={}", this, future.cause()); @@ -151,10 +148,11 @@ protected void initChannel(Channel ch) throws Exception { protected CompletableFuture stopService() { return CompletableFuture.runAsync(() -> { try { - if (stopper != null) { - stopper.call(); - address = null; + channelGroup.close().awaitUninterruptibly(); + if (this.shutdownAction != null) { + shutdownAction.run(); } + address = null; } catch (Exception e) { throw propagate(e); } @@ -167,28 +165,4 @@ private Throwable mapToBetterException(Throwable cause, int port) { } return cause; } - - private class Stopper implements Callable { - private final NettyExecutor bossGroup; - private final NettyExecutor workerGroup; - - public Stopper(NettyExecutor bossGroup, NettyExecutor workerGroup) { - this.bossGroup = bossGroup; - this.workerGroup = workerGroup; - } - - @Override - public Void call() { - channelGroup.close().awaitUninterruptibly(); - // Note: The return values from the shutdown methods is ignored. - // Not sure why. - shutdownEventExecutorGroup(bossGroup); - shutdownEventExecutorGroup(workerGroup); - return null; - } - - private Future shutdownEventExecutorGroup(NettyExecutor eventExecutorGroup) { - return eventExecutorGroup.eventLoopGroup().shutdownGracefully(10, 1000, MILLISECONDS); - } - } } diff --git a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java index 823802ff44..975ef6f0a4 100644 --- a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java +++ b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java @@ -42,6 +42,7 @@ public final class NettyServerBuilder { private HttpHandler handler = (request, context) -> Eventual.of(response(NOT_FOUND).build()); private NettyExecutor bossExecutor; private NettyExecutor workerExecutor; + private Runnable shutdownAction = () -> { }; public static NettyServerBuilder newBuilder() { return new NettyServerBuilder(); @@ -51,6 +52,34 @@ String host() { return firstNonNull(host, "localhost"); } + MetricRegistry metricsRegistry() { + return this.metricRegistry; + } + + NettyExecutor bossExecutor() { + return this.bossExecutor; + } + + NettyExecutor workerExecutor() { + return this.workerExecutor; + } + + ChannelGroup channelGroup() { + return this.channelGroup; + } + + public Runnable shutdownAction() { + return this.shutdownAction; + } + + HttpHandler handler() { + return this.handler; + } + + ServerConnector protocolConnector() { + return httpConnector; + } + public NettyServerBuilder host(String host) { this.host = host; return this; @@ -61,9 +90,6 @@ public NettyServerBuilder setMetricsRegistry(MetricRegistry metricRegistry) { return this; } - MetricRegistry metricsRegistry() { - return this.metricRegistry; - } public NettyServerBuilder bossExecutor(NettyExecutor executor) { this.bossExecutor = checkNotNull(executor, "boss executor"); @@ -75,34 +101,19 @@ public NettyServerBuilder workerExecutor(NettyExecutor executor) { return this; } - public NettyExecutor bossExecutor() { - return this.bossExecutor; - } - - public NettyExecutor workerExecutor() { - return this.workerExecutor; - } - - public ChannelGroup channelGroup() { - return this.channelGroup; - } - public NettyServerBuilder handler(HttpHandler handler) { this.handler = handler; return this; } - HttpHandler handler() { - return this.handler; - } - public NettyServerBuilder setProtocolConnector(ServerConnector connector) { this.httpConnector = connector; return this; } - ServerConnector protocolConnector() { - return httpConnector; + public NettyServerBuilder shutdownAction(Runnable shutdownAction) { + this.shutdownAction = shutdownAction; + return this; } public InetServer build() { diff --git a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java index b9eb779ff7..ae162b42e1 100644 --- a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java +++ b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; +import com.hotels.styx.NettyExecutor; import com.hotels.styx.StyxConfig; import com.hotels.styx.admin.AdminServerConfig; import com.hotels.styx.api.MetricRegistry; @@ -65,6 +66,7 @@ private StyxServer(Builder builder) { .styxConfig(styxConfig(builder)) .pluginFactories(builder.pluginFactories) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(backendServicesRegistry))) + .serverExecutors(NettyExecutor.create("StyxServer-boss", 1), NettyExecutor.create("StyxServer-worker", 1)) .build(); metricRegistry = config.environment().metricRegistry(); diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala index 738aacfbea..97bf754758 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -34,6 +34,7 @@ import com.hotels.styx.server.netty.NettyServerConfig.Connectors import com.hotels.styx.server.{HttpConnectorConfig, HttpsConnectorConfig} import com.hotels.styx.startup.StyxServerComponents import com.hotels.styx.support.CodaHaleMetricsFacade +import com.hotels.styx.support.configuration.StyxBaseConfig import scala.collection.JavaConverters._ @@ -86,6 +87,7 @@ object StyxServerSupport { val builder = new StyxServerComponents.Builder() .styxConfig(styxConfig) .additionalServices(ImmutableMap.of("backendServiceRegistry", styxService)) + .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) if (plugins.nonEmpty) { builder.plugins(plugins1) @@ -99,6 +101,7 @@ object StyxServerSupport { val builder = new StyxServerComponents.Builder() .styxConfig(styxConfig) + .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) if (plugins.nonEmpty) { builder.plugins(plugins1) diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala index 5c11d567e6..192025eda3 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -18,7 +18,7 @@ package com.hotels.styx.support.configuration import java.nio.file.Path import java.util -import com.hotels.styx.StyxServer +import com.hotels.styx.{NettyExecutor, StyxServer} import com.hotels.styx.StyxServerSupport._ import com.hotels.styx.api.extension.service.spi.StyxService import com.hotels.styx.api.plugins.spi.Plugin @@ -58,6 +58,9 @@ sealed trait StyxBaseConfig { object StyxBaseConfig { val defaultLogbackXml = ResourcePaths.fixturesHome(this.getClass, "/logback.xml") + val globalBossExecutor = NettyExecutor.create("StyxServer-Boss", 1) + val globalWorkerExecutor = NettyExecutor.create("StyxServer-Worker", 1) + } case class StyxConfig(proxyConfig: ProxyConfig = ProxyConfig(), @@ -150,6 +153,7 @@ case class StyxYamlConfig(yamlConfig: String, .styxConfig(styxConfig) .additionalServices(services(backendsRegistry).asJava) .loggingSetUp(logbackXmlLocation.toString) + .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) .build()) styxServer.startAsync().awaitRunning() @@ -163,6 +167,7 @@ case class StyxYamlConfig(yamlConfig: String, val styxServer = new StyxServer(new StyxServerComponents.Builder() .styxConfig(styxConfig) .loggingSetUp(logbackXmlLocation.toString) + .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) .build()) styxServer.startAsync().awaitRunning() From f8fbd50f77ea46840a40c5828d40d271379bb91d Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Tue, 11 Feb 2020 15:07:27 +0000 Subject: [PATCH 04/16] Remove serverExecutors from StyxServerComponents. --- .../styx/startup/StyxServerComponents.java | 21 ------------------- .../java/com/hotels/styx/StyxServerTest.java | 7 ------- .../startup/StyxServerComponentsTest.java | 6 ------ .../com/hotels/styx/testapi/StyxServer.java | 1 - .../com/hotels/styx/StyxServerSupport.scala | 2 -- .../support/configuration/StyxConfig.scala | 2 -- 6 files changed, 39 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 19397717d1..a25d44aa02 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -80,8 +80,6 @@ public class StyxServerComponents { private final StyxObjectStore> executorObjectStore = new StyxObjectStore<>(); private final RoutingObjectFactory.Context routingObjectContext; private final StartupConfig startupConfig; - private final NettyExecutor bossExecutor; - private final NettyExecutor workerExecutor; private static final Logger LOGGER = getLogger(StyxServerComponents.class); private final NettyExecutor executor; @@ -99,8 +97,6 @@ private StyxServerComponents(Builder builder) { builder.loggingSetUp.setUp(environment); this.executor = NettyExecutor.create("Styx-Client-Worker", environment.configuration().proxyServerConfig().clientWorkerThreadsCount()); - this.bossExecutor = builder.bossExecutor; - this.workerExecutor = builder.workerExecutor; this.environment.configuration().get("executors", JsonNode.class) .map(StyxServerComponents::readComponents) @@ -251,14 +247,6 @@ private static Map mergeServices(Map c .build(); } - public NettyExecutor bossExecutor() { - return this.bossExecutor; - } - - public NettyExecutor workerExecutor() { - return this.workerExecutor; - } - /** * CoreConfig builder. */ @@ -272,8 +260,6 @@ public static final class Builder { private final Map additionalRoutingObjectFactories = new HashMap<>(); private final Map additionalServices = new HashMap<>(); - private NettyExecutor bossExecutor; - private NettyExecutor workerExecutor; public Builder styxConfig(StyxConfig styxConfig) { this.styxConfig = requireNonNull(styxConfig); @@ -342,13 +328,6 @@ public Builder additionalRoutingObjects(Map additi return this; } - public Builder serverExecutors(NettyExecutor bossExecutor, NettyExecutor workerExecutor) { - this.bossExecutor = bossExecutor; - this.workerExecutor = workerExecutor; - - return this; - } - public StyxServerComponents build() { return new StyxServerComponents(this); } diff --git a/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java b/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java index 5bd4c41eea..d5e6888218 100644 --- a/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/StyxServerTest.java @@ -74,8 +74,6 @@ public class StyxServerTest { private LoggingTestSupport log; private LoggingTestSupport pssLog; - private NettyExecutor bossExecutor = NettyExecutor.create("StyxServerTest-boss", 1); - private NettyExecutor workerExecutor = NettyExecutor.create("StyxServerTest-worker", 1); @BeforeEach public void setUp() { @@ -87,8 +85,6 @@ public void setUp() { public void removeAppender() { log.stop(); pssLog.stop(); - bossExecutor.shut(); - workerExecutor.shut(); } @BeforeAll @@ -166,7 +162,6 @@ public void disablesResourceLeakDetectionByDefault() { StyxServerComponents config = new StyxServerComponents.Builder() .configuration(EMPTY_CONFIGURATION) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(new MemoryBackedRegistry<>()))) - .serverExecutors(bossExecutor, workerExecutor) .build(); new StyxServer(config); @@ -267,7 +262,6 @@ private StyxServer styxServerWithPlugins(Map plugins) { .configuration(styxConfig()) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(new MemoryBackedRegistry<>()))) .plugins(plugins) - .serverExecutors(bossExecutor, workerExecutor) .build(); return new StyxServer(config); @@ -277,7 +271,6 @@ private StyxServer styxServerWithBackendServiceRegistry(StyxService backendServi StyxServerComponents config = new StyxServerComponents.Builder() .configuration(styxConfig()) .additionalServices(ImmutableMap.of("backendServiceRegistry", backendServiceRegistry)) - .serverExecutors(bossExecutor, workerExecutor) .build(); return new StyxServer(config); diff --git a/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java b/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java index 2169eeb971..18b5e86f09 100644 --- a/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java +++ b/components/proxy/src/test/java/com/hotels/styx/startup/StyxServerComponentsTest.java @@ -18,7 +18,6 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.hotels.styx.Environment; -import com.hotels.styx.NettyExecutor; import com.hotels.styx.StyxConfig; import com.hotels.styx.api.Eventual; import com.hotels.styx.api.configuration.Configuration; @@ -53,7 +52,6 @@ public void setsUpLoggingOnBuild() { new StyxServerComponents.Builder() .styxConfig(new StyxConfig()) .loggingSetUp(loggingSetUp) - .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); verify(loggingSetUp).setUp(any(Environment.class)); @@ -67,7 +65,6 @@ public void loadsPlugins() { StyxServerComponents components = new StyxServerComponents.Builder() .styxConfig(new StyxConfig()) .pluginFactories(ImmutableList.of(f1, f2)) - .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); List plugins = components.plugins(); @@ -83,7 +80,6 @@ public void loadsServices() { .services((env, routeDb) -> ImmutableMap.of( "service1", mock(StyxService.class), "service2", mock(StyxService.class))) - .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Map services = components.services(); @@ -98,7 +94,6 @@ public void exposesAdditionalServices() { .additionalServices(ImmutableMap.of( "service1", mock(StyxService.class), "service2", mock(StyxService.class))) - .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Map services = components.services(); @@ -114,7 +109,6 @@ public void createsEnvironment() { StyxServerComponents components = new StyxServerComponents.Builder() .styxConfig(new StyxConfig(config)) - .serverExecutors(mock(NettyExecutor.class), mock(NettyExecutor.class)) .build(); Environment environment = components.environment(); diff --git a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java index ae162b42e1..cfbe3b677b 100644 --- a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java +++ b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java @@ -66,7 +66,6 @@ private StyxServer(Builder builder) { .styxConfig(styxConfig(builder)) .pluginFactories(builder.pluginFactories) .additionalServices(ImmutableMap.of("backendServiceRegistry", new RegistryServiceAdapter(backendServicesRegistry))) - .serverExecutors(NettyExecutor.create("StyxServer-boss", 1), NettyExecutor.create("StyxServer-worker", 1)) .build(); metricRegistry = config.environment().metricRegistry(); diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala index 97bf754758..b093f0ff77 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/StyxServerSupport.scala @@ -87,7 +87,6 @@ object StyxServerSupport { val builder = new StyxServerComponents.Builder() .styxConfig(styxConfig) .additionalServices(ImmutableMap.of("backendServiceRegistry", styxService)) - .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) if (plugins.nonEmpty) { builder.plugins(plugins1) @@ -101,7 +100,6 @@ object StyxServerSupport { val builder = new StyxServerComponents.Builder() .styxConfig(styxConfig) - .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) if (plugins.nonEmpty) { builder.plugins(plugins1) diff --git a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala index 192025eda3..d5bff6466a 100644 --- a/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala +++ b/system-tests/e2e-suite/src/test/scala/com/hotels/styx/support/configuration/StyxConfig.scala @@ -153,7 +153,6 @@ case class StyxYamlConfig(yamlConfig: String, .styxConfig(styxConfig) .additionalServices(services(backendsRegistry).asJava) .loggingSetUp(logbackXmlLocation.toString) - .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) .build()) styxServer.startAsync().awaitRunning() @@ -167,7 +166,6 @@ case class StyxYamlConfig(yamlConfig: String, val styxServer = new StyxServer(new StyxServerComponents.Builder() .styxConfig(styxConfig) .loggingSetUp(logbackXmlLocation.toString) - .serverExecutors(StyxBaseConfig.globalBossExecutor, StyxBaseConfig.globalWorkerExecutor) .build()) styxServer.startAsync().awaitRunning() From ac7a829c25682b04718c6d8cb792931309997d8e Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Tue, 11 Feb 2020 16:02:37 +0000 Subject: [PATCH 05/16] Inject executors to StyxHttpServer. --- .../styx/startup/StyxServerComponents.java | 20 +++++++++- .../styx/executors/NettyExecutorFactory.kt | 17 +++++++-- .../com/hotels/styx/servers/StyxHttpServer.kt | 36 ++++-------------- .../hotels/styx/servers/StyxHttpServerTest.kt | 38 ++++++++++++++----- 4 files changed, 68 insertions(+), 43 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index a25d44aa02..03e2793dcf 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -34,6 +34,7 @@ import com.hotels.styx.api.plugins.spi.Plugin; import com.hotels.styx.common.format.SanitisedHttpHeaderFormatter; import com.hotels.styx.common.format.SanitisedHttpMessageFormatter; +import com.hotels.styx.executors.NettyExecutorConfig; import com.hotels.styx.infrastructure.configuration.yaml.JsonNodeConfig; import com.hotels.styx.proxy.plugin.NamedPlugin; import com.hotels.styx.routing.RoutingObjectRecord; @@ -103,11 +104,26 @@ private StyxServerComponents(Builder builder) { .orElse(ImmutableMap.of()) .forEach((name, definition) -> { LOGGER.warn("Loading styx server: " + name + ": " + definition); - NettyExecutor provider = Builtins.buildExecutor(name, definition, BUILTIN_EXECUTOR_FACTORIES); - StyxObjectRecord record = new StyxObjectRecord<>(definition.type(), ImmutableSet.copyOf(definition.tags()), definition.config(), provider); + NettyExecutor executor = Builtins.buildExecutor(name, definition, BUILTIN_EXECUTOR_FACTORIES); + StyxObjectRecord record = new StyxObjectRecord<>(definition.type(), ImmutableSet.copyOf(definition.tags()), definition.config(), executor); executorObjectStore.insert(name, record); }); + // Overwrite any existing or user-supplied values: + executorObjectStore.insert("StyxHttpServer-Global-Boss", new StyxObjectRecord<>( + "NettyExecutor", + ImmutableSet.of("StyxInternal"), + new NettyExecutorConfig(0, "StyxHttpServer-Global-Boss").asJsonNode(), + NettyExecutor.create("StyxHttpServer-Global-Boss", 0))); + + // Overwrite any existing or user-supplied values: + executorObjectStore.insert("StyxHttpServer-Global-Worker", + new StyxObjectRecord<>( + "NettyExecutor", + ImmutableSet.of("StyxInternal"), + new NettyExecutorConfig(0, "StyxHttpServer-Global-Worker").asJsonNode(), + NettyExecutor.create("StyxHttpServer-Global-Worker", 0))); + this.services = mergeServices( builder.servicesLoader.load(environment, routeObjectStore), builder.additionalServices diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt b/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt index 79f61cd262..8b0feb9ccf 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/executors/NettyExecutorFactory.kt @@ -15,10 +15,15 @@ */ package com.hotels.styx.executors +import com.fasterxml.jackson.core.JsonParser +import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.databind.JsonNode +import com.fasterxml.jackson.databind.ObjectMapper +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory import com.hotels.styx.ExecutorFactory import com.hotels.styx.NettyExecutor import com.hotels.styx.config.schema.SchemaDsl +import com.hotels.styx.infrastructure.configuration.json.ObjectMappers import com.hotels.styx.infrastructure.configuration.yaml.JsonNodeConfig class NettyExecutorFactory : ExecutorFactory { @@ -26,7 +31,7 @@ class NettyExecutorFactory : ExecutorFactory { override fun create(name: String, configuration: JsonNode): NettyExecutor { val config = parseConfig(configuration) - return NettyExecutor.create(config.namePattern, config.count) + return NettyExecutor.create(config.namePattern, config.threads) } companion object { @@ -38,6 +43,12 @@ class NettyExecutorFactory : ExecutorFactory { } } +private val mapper = ObjectMappers.addStyxMixins(ObjectMapper(YAMLFactory())) + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES) + .configure(JsonParser.Feature.AUTO_CLOSE_SOURCE, true) + internal data class NettyExecutorConfig( - val count: Int = 0, - val namePattern: String = "netty-executor") + val threads: Int = 0, + val namePattern: String = "netty-executor") { + fun asJsonNode(): JsonNode = mapper.readTree(mapper.writeValueAsString(this)) +} diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index c9705665fb..caa70ab109 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -17,7 +17,6 @@ package com.hotels.styx.servers import com.fasterxml.jackson.databind.JsonNode import com.hotels.styx.InetServer -import com.hotels.styx.NettyExecutor import com.hotels.styx.ProxyConnectorFactory import com.hotels.styx.ResponseInfoFormat import com.hotels.styx.StyxObjectRecord @@ -59,9 +58,6 @@ object StyxHttpServer { optional("keepAliveTimeoutMillis", integer()), optional("maxConnectionsCount", integer()), - optional("bossThreadsCount", integer()), - optional("workerThreadsCount", integer()), - optional("bossExecutor", string()), optional("workerExecutor", string()) ) @@ -94,11 +90,8 @@ private data class StyxHttpServerConfiguration( val keepAliveTimeoutMillis: Int = 120000, val maxConnectionsCount: Int = 512, - val bossThreadsCount: Int = 0, - val workerThreadsCount: Int = 0, - - val bossExecutor: String?, - val workerExecutor: String? + val bossExecutor: String = "StyxHttpServer-Global-Boss", + val workerExecutor: String = "StyxHttpServer-Global-Boss" ) internal class StyxHttpServerFactory : StyxServerFactory { @@ -106,25 +99,8 @@ internal class StyxHttpServerFactory : StyxServerFactory { override fun create(name: String, context: RoutingObjectFactory.Context, configuration: JsonNode, serverDb: StyxObjectStore>): InetServer { val config = serverConfig(configuration) - val environment = context.environment() - val bossExecutor = if (config.bossExecutor != null) { - context.executors().get(config.bossExecutor) - .map { it.styxService } - .orElseThrow() - } else { - NettyExecutor.create("Http-Server(localhost-${config.port})-boss", config.bossThreadsCount) - } - - val workerExecutor = if (config.workerExecutor != null) { - context.executors().get(config.workerExecutor) - .map { it.styxService } - .orElseThrow() - } else { - NettyExecutor.create("Http-Server(localhost-${config.port})-worker", config.bossThreadsCount) - } - return NettyServerBuilder() .setMetricsRegistry(environment.metricRegistry()) .setProtocolConnector( @@ -161,8 +137,12 @@ internal class StyxHttpServerFactory : StyxServerFactory { .protocols(*config.tlsSettings.protocols.toTypedArray()) .build() })) - .workerExecutor(workerExecutor) - .bossExecutor(bossExecutor) + .bossExecutor(context.executors().get(config.bossExecutor) + .map { it.styxService } + .orElseThrow()) + .workerExecutor(context.executors().get(config.workerExecutor) + .map { it.styxService } + .orElseThrow()) .handler({ request, ctx -> context.refLookup() .apply(StyxObjectReference(config.handler)) diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt index 3036724f20..3b831629e6 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt @@ -37,6 +37,7 @@ import com.hotels.styx.client.netty.connectionpool.NettyConnectionFactory import com.hotels.styx.routing.RoutingObject import com.hotels.styx.RoutingObjectFactoryContext import com.hotels.styx.configBlock +import com.hotels.styx.executors.NettyExecutorConfig import com.hotels.styx.routing.db.StyxObjectStore import com.hotels.styx.ref import com.hotels.styx.routeLookup @@ -59,8 +60,6 @@ class StyxHttpServerTest : FeatureSpec({ val serverConfig = configBlock(""" port: 0 handler: aHandler - workerExecutor: worker - bossExecutor: boss """.trimIndent()) val server = StyxHttpServerFactory().create("test-01", routingContext.get(), serverConfig, db) @@ -298,13 +297,13 @@ class StyxHttpServerTest : FeatureSpec({ Thread.sleep(100) - connection.isConnected shouldBe(true) + connection.isConnected shouldBe (true) } scenario("Should close the connection after keepAlive time") { Thread.sleep(500) - connection.isConnected shouldBe(false) + connection.isConnected shouldBe (false) } guavaServer.stopAsync().awaitTerminated() @@ -363,12 +362,31 @@ class StyxHttpServerTest : FeatureSpec({ }) + +private val globalBossExecutor = NettyExecutor.create("StyxHttpServer-Global-Boss", 1) +private val globalWorkerExecutor = NettyExecutor.create("StyxHttpServer-Global-Worker", 1) + fun createExecutors(): StyxObjectStore> = StyxObjectStore>() - .let { - it.insert("worker", StyxObjectRecord("NettyExecutor", setOf(), configBlock("a: b"), NettyExecutor.create("MY_TEST_SERVER_WORKER", 1))) - it.insert("boss", StyxObjectRecord("NettyExecutor", setOf(), configBlock("a: b"), NettyExecutor.create("MY_TEST_SERVER_BOSS", 1))) - it - } + .let { + + // TODO: These need to be kept in-sync with the ones in StyxServerComponents: + // Overwrite any existing or user-supplied values: + it.insert("StyxHttpServer-Global-Boss", StyxObjectRecord( + "NettyExecutor", + setOf("StyxInternal"), + NettyExecutorConfig(0, "StyxHttpServer-Global-Boss").asJsonNode(), + globalBossExecutor)); + + // Overwrite any existing or user-supplied values: + it.insert("StyxHttpServer-Global-Worker", + StyxObjectRecord( + "NettyExecutor", + setOf("StyxInternal"), + NettyExecutorConfig(0, "StyxHttpServer-Global-Worker").asJsonNode(), + globalWorkerExecutor)); + + it + } fun threadCount(namePattern: String) = Thread.getAllStackTraces().keys .map { it.name } @@ -402,7 +420,7 @@ private val compressedResponse = response(OK) private val routingContext = RoutingObjectFactoryContext( routeRefLookup = routeLookup { ref("aHandler" to RoutingObject { request, _ -> - when(request.url().toString()) { + when (request.url().toString()) { "/compressed" -> Eventual.of(compressedResponse.stream()) else -> Eventual.of(response.stream()) } From f25db3f33ff0083126ee419afd82a9e61a507273 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 13 Feb 2020 15:08:17 +0000 Subject: [PATCH 06/16] Add ExecutorSettingsSpec, a Styx functional test. Add default `Styx-Client-Global-Worker` executor. HostProxy: Add `executor` configuration attribute. OriginsConfigConverter: Use `Styx-Client-Global-Worker` by default. --- .../java/com/hotels/styx/NettyExecutor.java | 8 +- .../com/hotels/styx/ServerConfigSchema.java | 3 + .../main/java/com/hotels/styx/StyxServer.java | 4 + .../routing/config/RoutingObjectFactory.java | 6 +- .../styx/routing/handlers/HostProxy.java | 27 ++- .../styx/startup/StyxServerComponents.java | 14 ++ .../com/hotels/styx/servers/StyxHttpServer.kt | 5 + .../styx/services/OriginsConfigConverter.kt | 4 +- .../test/kotlin/com/hotels/styx/Support.kt | 37 +++- .../hotels/styx/servers/StyxHttpServerTest.kt | 35 +--- .../YamlFileConfigurationServiceTest.kt | 10 +- .../styx/server/netty/NettyServerBuilder.java | 4 +- .../com/hotels/styx/testapi/StyxServer.java | 1 - .../styx/config/ExecutorSettingsSpec.kt | 185 ++++++++++++++++++ .../hotels/styx/support/StyxServerProvider.kt | 13 +- 15 files changed, 307 insertions(+), 49 deletions(-) create mode 100644 system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt diff --git a/components/common/src/main/java/com/hotels/styx/NettyExecutor.java b/components/common/src/main/java/com/hotels/styx/NettyExecutor.java index 160a54cec3..86d8c0435b 100644 --- a/components/common/src/main/java/com/hotels/styx/NettyExecutor.java +++ b/components/common/src/main/java/com/hotels/styx/NettyExecutor.java @@ -26,6 +26,8 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.util.concurrent.TimeUnit; + import static com.hotels.styx.EventLoopGroups.epollEventLoopGroup; import static com.hotels.styx.EventLoopGroups.nioEventLoopGroup; @@ -70,7 +72,11 @@ private NettyExecutor(EventLoopGroup eventLoopGroup, } public void shut() { - eventLoopGroup.shutdownGracefully(); + try { + eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS).await(5000); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } } public Class serverEventLoopClass() { diff --git a/components/proxy/src/main/java/com/hotels/styx/ServerConfigSchema.java b/components/proxy/src/main/java/com/hotels/styx/ServerConfigSchema.java index 7c6df690af..13d298697e 100644 --- a/components/proxy/src/main/java/com/hotels/styx/ServerConfigSchema.java +++ b/components/proxy/src/main/java/com/hotels/styx/ServerConfigSchema.java @@ -35,6 +35,7 @@ import static com.hotels.styx.config.schema.SchemaDsl.string; import static com.hotels.styx.config.schema.SchemaDsl.union; import static com.hotels.styx.config.validator.DocumentFormat.newDocument; +import static com.hotels.styx.routing.config.Builtins.BUILTIN_EXECUTOR_SCHEMAS; import static com.hotels.styx.routing.config.Builtins.BUILTIN_HANDLER_SCHEMAS; import static com.hotels.styx.routing.config.Builtins.BUILTIN_SERVER_SCHEMAS; import static com.hotels.styx.routing.config.Builtins.BUILTIN_SERVICE_PROVIDER_SCHEMAS; @@ -111,6 +112,7 @@ final class ServerConfigSchema { field("expirationMillis", integer()) )) )), + optional("executors", map(routingObject())), optional("services", object( field("factories", map(object(opaque()))) )), @@ -166,6 +168,7 @@ final class ServerConfigSchema { BUILTIN_SERVICE_PROVIDER_SCHEMAS.forEach(STYX_SERVER_CONFIGURATION_SCHEMA_BUILDER::typeExtension); BUILTIN_SERVER_SCHEMAS.forEach(STYX_SERVER_CONFIGURATION_SCHEMA_BUILDER::typeExtension); INTERCEPTOR_SCHEMAS.forEach(STYX_SERVER_CONFIGURATION_SCHEMA_BUILDER::typeExtension); + BUILTIN_EXECUTOR_SCHEMAS.forEach(STYX_SERVER_CONFIGURATION_SCHEMA_BUILDER::typeExtension); } diff --git a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java index 15c22d5944..b87de72220 100644 --- a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java +++ b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java @@ -317,6 +317,10 @@ protected void doStop() { proxyBossExecutor.shut(); proxyWorkerExecutor.shut(); + this.components.executors() + .entrySet() + .forEach(entry -> entry.getValue().component4().shut()); + this.phase1Services.stopAsync().awaitStopped(); shutdownLogging(true); } diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java b/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java index f53c7c0dc8..e097972fee 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/config/RoutingObjectFactory.java @@ -66,6 +66,7 @@ class Context { private final boolean requestTracking; private StyxObjectStore> executorObjectStore; + // CHECKSTYLE:OFF public Context( RouteRefLookup refLookup, Environment environment, @@ -84,6 +85,7 @@ public Context( this.requestTracking = requestTracking; this.executorObjectStore = executorObjectStore; } + // CHECKSTYLE:ON public Environment environment() { return environment; @@ -113,6 +115,8 @@ public RouteRefLookup refLookup() { return refLookup; } - public ObjectStore> executors() { return executorObjectStore; }; + public ObjectStore> executors() { + return executorObjectStore; + }; } } diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java b/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java index 234cd4ec8a..6b156c8ed7 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java @@ -18,6 +18,7 @@ import com.fasterxml.jackson.annotation.JsonProperty; import com.google.common.annotations.VisibleForTesting; import com.google.common.net.HostAndPort; +import com.hotels.styx.NettyExecutor; import com.hotels.styx.api.Eventual; import com.hotels.styx.api.HttpInterceptor; import com.hotels.styx.api.LiveHttpRequest; @@ -74,6 +75,8 @@ public class HostProxy implements RoutingObject { optional("tlsSettings", object( optional("trustAllCerts", bool()), optional("sslProvider", string()), + + // We could pull them out to a separate configuration block: optional("trustStorePath", string()), optional("trustStorePassword", string()), optional("protocols", list(string())), @@ -105,7 +108,8 @@ public class HostProxy implements RoutingObject { )), optional("responseTimeoutMillis", integer()), optional("maxHeaderSize", integer()), - optional("metricPrefix", string()) + optional("metricPrefix", string()), + optional("executor", string()) ); private final String errorMessage; @@ -155,6 +159,7 @@ public static class HostProxyConfiguration { private final int responseTimeoutMillis; private final int maxHeaderSize; private final String metricPrefix; + private final String executor; public HostProxyConfiguration( String host, @@ -162,13 +167,15 @@ public HostProxyConfiguration( TlsSettings tlsSettings, int responseTimeoutMillis, int maxHeaderSize, - String metricPrefix) { + String metricPrefix, + String executor) { this.host = host; this.connectionPool = connectionPool; this.tlsSettings = tlsSettings; this.responseTimeoutMillis = responseTimeoutMillis; this.maxHeaderSize = maxHeaderSize; this.metricPrefix = metricPrefix; + this.executor = executor; } @JsonProperty("host") @@ -201,6 +208,11 @@ public String metricPrefix() { return metricPrefix; } + @JsonProperty("executor") + public String executor() { + return executor; + } + } /** @@ -230,6 +242,12 @@ public RoutingObject build(List fullName, Context context, StyxObjectDef String metricPrefix = config.get("metricPrefix", String.class) .orElse("routing.objects"); + String executorName = config.get("executor", String.class) + .orElse("Styx-Client-Global-Worker"); + + // TODO: unknown executor name: + NettyExecutor executor = context.executors().get(executorName).get().component4(); + HostAndPort hostAndPort = config.get("host") .map(HostAndPort::fromString) .map(it -> addDefaultPort(it, tlsSettings)) @@ -238,6 +256,7 @@ public RoutingObject build(List fullName, Context context, StyxObjectDef String objectName = fullName.get(fullName.size() - 1); return createHostProxyHandler( + executor, context.environment().metricRegistry(), hostAndPort, poolSettings, @@ -262,6 +281,7 @@ private static HostAndPort addDefaultPort(HostAndPort hostAndPort, TlsSettings t @NotNull public static HostProxy createHostProxyHandler( + NettyExecutor executor, MetricRegistry metricRegistry, HostAndPort hostAndPort, ConnectionPoolSettings poolSettings, @@ -284,6 +304,7 @@ public static HostProxy createHostProxyHandler( ConnectionPool.Factory connectionPoolFactory = new SimpleConnectionPoolFactory.Builder() .connectionFactory( connectionFactory( + executor, tlsSettings, responseTimeoutMillis, maxHeaderSize, @@ -297,6 +318,7 @@ public static HostProxy createHostProxyHandler( } private static Connection.Factory connectionFactory( + NettyExecutor executor, TlsSettings tlsSettings, int responseTimeoutMillis, int maxHeaderSize, @@ -312,6 +334,7 @@ private static Connection.Factory connectionFactory( .responseTimeoutMillis(responseTimeoutMillis) .build() ) + .executor(executor) .tlsSettings(tlsSettings) .httpConfig(newHttpConfigBuilder().setMaxHeadersSize(maxHeaderSize).build()) .build(); diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 03e2793dcf..a9a3f1cf61 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -85,6 +85,7 @@ public class StyxServerComponents { private static final Logger LOGGER = getLogger(StyxServerComponents.class); private final NettyExecutor executor; + // CHECKSTYLE:OFF private StyxServerComponents(Builder builder) { StyxConfig styxConfig = requireNonNull(builder.styxConfig); @@ -124,6 +125,14 @@ private StyxServerComponents(Builder builder) { new NettyExecutorConfig(0, "StyxHttpServer-Global-Worker").asJsonNode(), NettyExecutor.create("StyxHttpServer-Global-Worker", 0))); + // Overwrite any existing or user-supplied values: + executorObjectStore.insert("Styx-Client-Global-Worker", + new StyxObjectRecord<>( + "NettyExecutor", + ImmutableSet.of("StyxInternal"), + new NettyExecutorConfig(0, "Styx-Client-Global-Worker").asJsonNode(), + NettyExecutor.create("Styx-Client-Global-Worker", 0))); + this.services = mergeServices( builder.servicesLoader.load(environment, routeObjectStore), builder.additionalServices @@ -179,6 +188,7 @@ private StyxServerComponents(Builder builder) { serverObjectStore.insert(name, record); }); } + // CHECKSTYLE:ON private static Map readComponents(JsonNode root) { Map handlers = new HashMap<>(); @@ -214,6 +224,10 @@ public StyxObjectStore> servicesDatabase() { return this.providerObjectStore; } + public StyxObjectStore> executors() { + return this.executorObjectStore; + } + public StyxObjectStore> serversDatabase() { return this.serverObjectStore; } diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index caa70ab109..60e8be183a 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -137,12 +137,17 @@ internal class StyxHttpServerFactory : StyxServerFactory { .protocols(*config.tlsSettings.protocols.toTypedArray()) .build() })) + + // TODO: Unknown executor name .bossExecutor(context.executors().get(config.bossExecutor) .map { it.styxService } .orElseThrow()) + + // TODO: Unknown executor name .workerExecutor(context.executors().get(config.workerExecutor) .map { it.styxService } .orElseThrow()) + .handler({ request, ctx -> context.refLookup() .apply(StyxObjectReference(config.handler)) diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt b/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt index 463305d461..4d57573352 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt @@ -222,6 +222,7 @@ internal class OriginsConfigConverter( .parse(configSource(yamlConfig)).`as`(StyxObjectDefinition::class.java) } + // TODO: Executor: Must add executor configuration settings: private fun hostProxyConfig(poolSettings: ConnectionPoolSettings, tlsSettings: TlsSettings?, responseTimeout: Int, @@ -234,6 +235,7 @@ internal class OriginsConfigConverter( tlsSettings, responseTimeout, maxHeaderSize, - metricsPrefix)) + metricsPrefix, + "Styx-Client-Global-Worker")) } } diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt b/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt index 01d81c5be9..6b4000d0a2 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/Support.kt @@ -16,6 +16,7 @@ package com.hotels.styx import com.fasterxml.jackson.databind.JsonNode +import com.google.common.collect.ImmutableSet import com.hotels.styx.api.Eventual import com.hotels.styx.api.HttpHandler import com.hotels.styx.api.HttpRequest @@ -25,6 +26,7 @@ import com.hotels.styx.api.HttpResponseStatus.OK import com.hotels.styx.api.LiveHttpRequest import com.hotels.styx.api.LiveHttpResponse import com.hotels.styx.api.WebServiceHandler +import com.hotels.styx.executors.NettyExecutorConfig import com.hotels.styx.infrastructure.configuration.yaml.YamlConfig import com.hotels.styx.proxy.plugin.NamedPlugin import com.hotels.styx.routing.RoutingObject @@ -60,7 +62,7 @@ internal data class RoutingObjectFactoryContext( val plugins: Iterable = listOf(), val interceptorFactories: Map = INTERCEPTOR_FACTORIES, val requestTracking: Boolean = false, - val executorObjectStore: StyxObjectStore> = StyxObjectStore()) { + val executorObjectStore: StyxObjectStore> = executorObjects()) { fun get() = RoutingObjectFactory.Context( routeRefLookup, environment, @@ -70,9 +72,38 @@ internal data class RoutingObjectFactoryContext( INTERCEPTOR_FACTORIES, requestTracking, executorObjectStore) - } +fun executorObjects(): StyxObjectStore> = StyxObjectStore>() + .let { objectStore -> + + "Test-HttpServer-Global-Boss".let { name -> + objectStore.insert("StyxHttpServer-Global-Boss", StyxObjectRecord( + "NettyExecutor", + setOf("StyxInternal"), + NettyExecutorConfig(0, name).asJsonNode(), + NettyExecutor.create(name, 1))); + } + + "Test-HttpServer-Global-Worker".let { name -> + objectStore.insert("StyxHttpServer-Global-Worker", StyxObjectRecord( + "NettyExecutor", + setOf("StyxInternal"), + NettyExecutorConfig(0, name).asJsonNode(), + NettyExecutor.create(name, 1))); + } + + "Test-Client-Global-Worker".let { name -> + objectStore.insert("Styx-Client-Global-Worker", StyxObjectRecord( + "NettyExecutor", + ImmutableSet.of("StyxInternal"), + NettyExecutorConfig(0, name).asJsonNode(), + NettyExecutor.create(name, 0))) + } + + objectStore + } + fun WebServiceHandler.handle(request: HttpRequest) = this.handle(request, requestContext()) fun HttpHandler.handle(request: HttpRequest, count: Int = 10000) = this.handle(request.stream(), requestContext()) @@ -161,7 +192,7 @@ fun CompletableFuture.wait(debug: Boolean = false) = this.toMo } .block() -fun Eventual.wait(maxBytes: Int = 100*1024, debug: Boolean = false) = this.toMono() +fun Eventual.wait(maxBytes: Int = 100 * 1024, debug: Boolean = false) = this.toMono() .flatMap { it.aggregate(maxBytes).toMono() } .doOnNext { if (debug) { diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt index 3b831629e6..487ced322e 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/servers/StyxHttpServerTest.kt @@ -16,7 +16,6 @@ package com.hotels.styx.servers import com.hotels.styx.InetServer -import com.hotels.styx.NettyExecutor import com.hotels.styx.StyxObjectRecord import com.hotels.styx.StyxServers.toGuavaService import com.hotels.styx.api.ByteStream @@ -37,7 +36,7 @@ import com.hotels.styx.client.netty.connectionpool.NettyConnectionFactory import com.hotels.styx.routing.RoutingObject import com.hotels.styx.RoutingObjectFactoryContext import com.hotels.styx.configBlock -import com.hotels.styx.executors.NettyExecutorConfig +import com.hotels.styx.executorObjects import com.hotels.styx.routing.db.StyxObjectStore import com.hotels.styx.ref import com.hotels.styx.routeLookup @@ -342,7 +341,7 @@ class StyxHttpServerTest : FeatureSpec({ maxConnectionsCount: 2 """.trimIndent()) - val server = StyxHttpServerFactory().create("test-01", routingContext.copy(executorObjectStore = executors).get(), serverConfig, db) + val server = StyxHttpServerFactory().create("test-01", routingContext.copy(executorObjectStore = executorObjects()).get(), serverConfig, db) val guavaServer = toGuavaService(server) guavaServer.startAsync().awaitRunning() @@ -363,31 +362,6 @@ class StyxHttpServerTest : FeatureSpec({ }) -private val globalBossExecutor = NettyExecutor.create("StyxHttpServer-Global-Boss", 1) -private val globalWorkerExecutor = NettyExecutor.create("StyxHttpServer-Global-Worker", 1) - -fun createExecutors(): StyxObjectStore> = StyxObjectStore>() - .let { - - // TODO: These need to be kept in-sync with the ones in StyxServerComponents: - // Overwrite any existing or user-supplied values: - it.insert("StyxHttpServer-Global-Boss", StyxObjectRecord( - "NettyExecutor", - setOf("StyxInternal"), - NettyExecutorConfig(0, "StyxHttpServer-Global-Boss").asJsonNode(), - globalBossExecutor)); - - // Overwrite any existing or user-supplied values: - it.insert("StyxHttpServer-Global-Worker", - StyxObjectRecord( - "NettyExecutor", - setOf("StyxInternal"), - NettyExecutorConfig(0, "StyxHttpServer-Global-Worker").asJsonNode(), - globalWorkerExecutor)); - - it - } - fun threadCount(namePattern: String) = Thread.getAllStackTraces().keys .map { it.name } .filter { it.contains(namePattern) } @@ -396,9 +370,6 @@ fun threadCount(namePattern: String) = Thread.getAllStackTraces().keys fun threadNames() = Thread.getAllStackTraces().keys .map { it.name } - -val executors = createExecutors() - private fun createConnection(port: Int) = NettyConnectionFactory.Builder() .build() .createConnection(newOriginBuilder("localhost", port).build(), ConnectionSettings(250)) @@ -432,7 +403,7 @@ private val routingContext = RoutingObjectFactoryContext( .flatMap { Eventual.of(response.stream()) } }) }, - executorObjectStore = executors) + executorObjectStore = executorObjects()) private fun ungzip(content: ByteArray, charset: Charset): String = GZIPInputStream(content.inputStream()).bufferedReader(charset).use { it.readText() } diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt index b66a8f3421..43a067bc24 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt @@ -43,6 +43,7 @@ import org.slf4j.LoggerFactory import java.io.File import java.time.Duration import java.util.Optional +import java.util.concurrent.TimeUnit.SECONDS private val LOGGER = LoggerFactory.getLogger(YamlFileConfigurationServiceTest::class.java) @@ -65,7 +66,7 @@ class YamlFileConfigurationServiceTest : FunSpec() { try { action(service) } finally { - service.stop().join() + service.stop().orTimeout(2, SECONDS).join() } } @@ -90,8 +91,7 @@ class YamlFileConfigurationServiceTest : FunSpec() { OriginsConfigConverter(serviceDb, RoutingObjectFactoryContext(objectStore = routeDb).get(), "origins-cookie"), YamlFileConfigurationServiceConfig(originsConfig.absolutePath, pollInterval = pollInterval), serviceDb)) { - it.start().join() - + it.start().orTimeout(2, SECONDS).join() eventually(2.seconds, AssertionError::class.java) { routeDb.entrySet().size shouldBe 4 } @@ -786,7 +786,7 @@ internal data class CreatedService(val config: YamlFileConfigurationServiceTest. val startFuture = this.service.start() if (wait) { - startFuture.join() + startFuture.orTimeout(2, SECONDS).join() } return this @@ -801,7 +801,7 @@ internal data class CreatedService(val config: YamlFileConfigurationServiceTest. } fun stop() { - service.stop().join() + service.stop().orTimeout(2, SECONDS).join() } } diff --git a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java index 975ef6f0a4..cec43fefb1 100644 --- a/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java +++ b/components/server/src/main/java/com/hotels/styx/server/netty/NettyServerBuilder.java @@ -36,6 +36,8 @@ public final class NettyServerBuilder { private final ChannelGroup channelGroup = new DefaultChannelGroup(ImmediateEventExecutor.INSTANCE); + private static final NettyExecutor DEFAULT_SERVER_BOSS_EXECUTOR = NettyExecutor.create("Server-Boss", 1); + private String host; private MetricRegistry metricRegistry; private ServerConnector httpConnector; @@ -121,7 +123,7 @@ public InetServer build() { checkArgument(workerExecutor != null, "Must configure a worker executor"); if (bossExecutor == null) { - bossExecutor = NettyExecutor.create("Server-Boss", 1); + bossExecutor = DEFAULT_SERVER_BOSS_EXECUTOR; } return new NettyServer(this); } diff --git a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java index cfbe3b677b..e91b7ceda3 100644 --- a/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java +++ b/support/test-api/src/main/java/com/hotels/styx/testapi/StyxServer.java @@ -17,7 +17,6 @@ import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; -import com.hotels.styx.NettyExecutor; import com.hotels.styx.StyxConfig; import com.hotels.styx.admin.AdminServerConfig; import com.hotels.styx.api.MetricRegistry; diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt new file mode 100644 index 0000000000..af887e27e5 --- /dev/null +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt @@ -0,0 +1,185 @@ +/* + Copyright (C) 2013-2020 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.config + +import com.github.tomakehurst.wiremock.client.WireMock +import com.hotels.styx.api.HttpHeaderNames.HOST +import com.hotels.styx.api.HttpRequest.get +import com.hotels.styx.api.HttpResponseStatus.OK +import com.hotels.styx.client.StyxHttpClient +import com.hotels.styx.server.HttpConnectorConfig +import com.hotels.styx.servers.MockOriginServer +import com.hotels.styx.support.StyxServerProvider +import com.hotels.styx.support.proxyHttpHostHeader +import com.hotels.styx.support.serverPort +import com.hotels.styx.support.threadCount +import com.hotels.styx.support.threadNames +import com.hotels.styx.support.wait +import io.kotlintest.Spec +import io.kotlintest.shouldBe +import io.kotlintest.specs.FeatureSpec +import java.nio.charset.StandardCharsets.UTF_8 + +class ExecutorSettingsSpec : FeatureSpec() { + + val mockServer = MockOriginServer.create("", "", 0, HttpConnectorConfig(0)) + .start() + .stub(WireMock.get(WireMock.urlMatching("/.*")), WireMock.aResponse() + .withStatus(200) + .withBody("mock-server-01")) + + init { + feature("Executors configuration") { + + scenario("Applies client executor to HostProxy objects") { + styxServer.stop() + styxServer.restart(configuration = """ + executors: + forHostProxy: + type: NettyExecutor + config: + threads: 1 + namePattern: host-proxy + + httpPipeline: + type: HostProxy + config: + host: "localhost:${mockServer.port()}" + executor: forHostProxy + + proxy: + connectors: + http: + port: 0 + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + client.send(get("/a/") + .header(HOST, styxServer().proxyHttpHostHeader()) + .build()) + .wait()!! + .let { + it.status() shouldBe OK + it.bodyAs(UTF_8) shouldBe "mock-server-01" + } + + threadCount("host-proxy") shouldBe 1 + } + + scenario("!HostProxy uses default configuration when `executors` are not specified") { + // It is impossible to verify, externally, on what thread HostProxy runs. + // The best we can do is to make sure it still works. + styxServer.restart(configuration = """ + httpPipeline: + type: HostProxy + config: + host: "localhost:${mockServer.port()}" + + proxy: + connectors: + http: + port: 0 + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + client.send(get("/a/") + .header(HOST, styxServer().proxyHttpHostHeader()) + .build()) + .wait()!! + .let { + it.status() shouldBe OK + it.bodyAs(UTF_8) shouldBe "mock-server-01" + } + } + + scenario("Applies server executor to StyxHttpServer objects") { + styxServer.restart(configuration = """ + executors: + boss-executor: + type: NettyExecutor + config: + threads: 1 + namePattern: http-boss-executor + worker-executor: + type: NettyExecutor + config: + threads: 1 + namePattern: http-worker-executor + + routingObjects: + static-response: + type: StaticResponseHandler + config: + status: 200 + content: "Hello, from styx server!" + + servers: + http: + type: HttpServer + config: + port: 0 + handler: static-response + bossExecutor: boss-executor + workerExecutor: worker-executor + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + val httpPort = styxServer().serverPort("http") + + client.send(get("/b/") + .header(HOST, "localhost:$httpPort") + .build()) + .wait()!! + .let { + it.status() shouldBe OK + it.bodyAs(UTF_8) shouldBe "Hello, from styx server!" + } + + println("Test 3: thread names: " + threadNames().joinToString(separator = "\n") { it }) + + threadCount("http-boss-executor") shouldBe 1 + threadCount("http-worker-executor") shouldBe 1 + } + + scenario("Terminates executor threads when server shuts down") { + styxServer.stop() + threadCount("http-boss-executor") shouldBe 0 + threadCount("http-worker-executor") shouldBe 0 + } + } + } + + val client: StyxHttpClient = StyxHttpClient.Builder().build() + + val styxServer = StyxServerProvider() + + override fun afterSpec(spec: Spec) { + styxServer.stop() + mockServer.stop() + } +} diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt index 0500a67b17..eae2243923 100644 --- a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt @@ -1,5 +1,5 @@ /* - Copyright (C) 2013-2019 Expedia Inc. + Copyright (C) 2013-2020 Expedia Inc. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -227,7 +227,16 @@ fun StyxServer.routingObjects(debug: Boolean = false): Optional = StyxHt } } -fun threadCount(namePattern: String) = Thread.getAllStackTraces().keys +fun StyxServer.serverPort(name: String, debug: Boolean = false) = testClient + .send(HttpRequest.get("/admin/servers/$name/port") + .header(HOST, this.adminHostHeader()).build()) + .wait() + .bodyAs(UTF_8) + .toInt() + +fun threadNames() = Thread.getAllStackTraces().keys .map { it.name } + +fun threadCount(namePattern: String) = threadNames() .filter { it.contains(namePattern) } .count() From 7cab45b9ddae58b519e30d744baf6ee07c0a525a Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 13 Feb 2020 16:25:52 +0000 Subject: [PATCH 07/16] Allow users to override default global executor settings. --- .../styx/startup/StyxServerComponents.java | 20 +++---- .../com/hotels/styx/servers/StyxHttpServer.kt | 2 +- .../styx/config/ExecutorSettingsSpec.kt | 52 +++++++++++++++++-- 3 files changed, 60 insertions(+), 14 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index a9a3f1cf61..86174d86c5 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -100,16 +100,6 @@ private StyxServerComponents(Builder builder) { this.executor = NettyExecutor.create("Styx-Client-Worker", environment.configuration().proxyServerConfig().clientWorkerThreadsCount()); - this.environment.configuration().get("executors", JsonNode.class) - .map(StyxServerComponents::readComponents) - .orElse(ImmutableMap.of()) - .forEach((name, definition) -> { - LOGGER.warn("Loading styx server: " + name + ": " + definition); - NettyExecutor executor = Builtins.buildExecutor(name, definition, BUILTIN_EXECUTOR_FACTORIES); - StyxObjectRecord record = new StyxObjectRecord<>(definition.type(), ImmutableSet.copyOf(definition.tags()), definition.config(), executor); - executorObjectStore.insert(name, record); - }); - // Overwrite any existing or user-supplied values: executorObjectStore.insert("StyxHttpServer-Global-Boss", new StyxObjectRecord<>( "NettyExecutor", @@ -133,6 +123,16 @@ private StyxServerComponents(Builder builder) { new NettyExecutorConfig(0, "Styx-Client-Global-Worker").asJsonNode(), NettyExecutor.create("Styx-Client-Global-Worker", 0))); + this.environment.configuration().get("executors", JsonNode.class) + .map(StyxServerComponents::readComponents) + .orElse(ImmutableMap.of()) + .forEach((name, definition) -> { + LOGGER.warn("Loading styx server: " + name + ": " + definition); + NettyExecutor executor = Builtins.buildExecutor(name, definition, BUILTIN_EXECUTOR_FACTORIES); + StyxObjectRecord record = new StyxObjectRecord<>(definition.type(), ImmutableSet.copyOf(definition.tags()), definition.config(), executor); + executorObjectStore.insert(name, record); + }); + this.services = mergeServices( builder.servicesLoader.load(environment, routeObjectStore), builder.additionalServices diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index 60e8be183a..9367333acf 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -91,7 +91,7 @@ private data class StyxHttpServerConfiguration( val maxConnectionsCount: Int = 512, val bossExecutor: String = "StyxHttpServer-Global-Boss", - val workerExecutor: String = "StyxHttpServer-Global-Boss" + val workerExecutor: String = "StyxHttpServer-Global-Worker" ) internal class StyxHttpServerFactory : StyxServerFactory { diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt index af887e27e5..6d4f8dec28 100644 --- a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt @@ -26,7 +26,6 @@ import com.hotels.styx.support.StyxServerProvider import com.hotels.styx.support.proxyHttpHostHeader import com.hotels.styx.support.serverPort import com.hotels.styx.support.threadCount -import com.hotels.styx.support.threadNames import com.hotels.styx.support.wait import io.kotlintest.Spec import io.kotlintest.shouldBe @@ -160,8 +159,6 @@ class ExecutorSettingsSpec : FeatureSpec() { it.bodyAs(UTF_8) shouldBe "Hello, from styx server!" } - println("Test 3: thread names: " + threadNames().joinToString(separator = "\n") { it }) - threadCount("http-boss-executor") shouldBe 1 threadCount("http-worker-executor") shouldBe 1 } @@ -171,6 +168,55 @@ class ExecutorSettingsSpec : FeatureSpec() { threadCount("http-boss-executor") shouldBe 0 threadCount("http-worker-executor") shouldBe 0 } + + scenario("Overrides default global executors") { + styxServer.restart(configuration = """ + executors: + Styx-Client-Global-Worker: + type: NettyExecutor + config: + threads: 2 + namePattern: new-styx-client-global + + StyxHttpServer-Global-Worker: + type: NettyExecutor + config: + threads: 2 + namePattern: new-styx-server-worker + + routingObjects: + proxyToOrigin: + type: HostProxy + config: + host: "localhost:${mockServer.port()}" + + servers: + http: + type: HttpServer + config: + port: 0 + handler: proxyToOrigin + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + val httpPort = styxServer().serverPort("http") + + (1..10).map { client.send(get("/").header(HOST, "localhost:$httpPort").build()) } + .forEach { + it.wait()!! + .let { response -> + response.status() shouldBe OK + response.bodyAs(UTF_8) shouldBe "mock-server-01" + } + } + + threadCount("new-styx-client-global") shouldBe 2 + threadCount("new-styx-server-worker") shouldBe 2 + } } } From 80caa815c272e413f6c5ee2fc96df62742f2508d Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 13 Feb 2020 17:01:37 +0000 Subject: [PATCH 08/16] Throw IllegalStateException when started with undeclared executor name. --- .../styx/routing/handlers/HostProxy.java | 13 ++++++--- .../com/hotels/styx/servers/StyxHttpServer.kt | 29 +++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java b/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java index 6b156c8ed7..e348bb1f7d 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/handlers/HostProxy.java @@ -245,16 +245,21 @@ public RoutingObject build(List fullName, Context context, StyxObjectDef String executorName = config.get("executor", String.class) .orElse("Styx-Client-Global-Worker"); - // TODO: unknown executor name: - NettyExecutor executor = context.executors().get(executorName).get().component4(); + String objectName = fullName.get(fullName.size() - 1); + + NettyExecutor executor = context.executors().get(executorName) + .orElseThrow(() -> + new IllegalArgumentException( + format("HostProxy(%s) configuration error: executor='%s' not declared.", + objectName, + executorName))) + .component4(); HostAndPort hostAndPort = config.get("host") .map(HostAndPort::fromString) .map(it -> addDefaultPort(it, tlsSettings)) .orElseThrow(() -> missingAttributeError(configBlock, join(".", fullName), "host")); - String objectName = fullName.get(fullName.size() - 1); - return createHostProxyHandler( executor, context.environment().metricRegistry(), diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt index 9367333acf..1d7002777a 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/servers/StyxHttpServer.kt @@ -20,7 +20,13 @@ import com.hotels.styx.InetServer import com.hotels.styx.ProxyConnectorFactory import com.hotels.styx.ResponseInfoFormat import com.hotels.styx.StyxObjectRecord -import com.hotels.styx.config.schema.SchemaDsl.* +import com.hotels.styx.config.schema.SchemaDsl.`object` +import com.hotels.styx.config.schema.SchemaDsl.bool +import com.hotels.styx.config.schema.SchemaDsl.field +import com.hotels.styx.config.schema.SchemaDsl.integer +import com.hotels.styx.config.schema.SchemaDsl.list +import com.hotels.styx.config.schema.SchemaDsl.optional +import com.hotels.styx.config.schema.SchemaDsl.string import com.hotels.styx.infrastructure.configuration.yaml.JsonNodeConfig import com.hotels.styx.proxy.ProxyServerConfig import com.hotels.styx.proxy.encoders.ConfigurableUnwiseCharsEncoder.ENCODE_UNWISECHARS @@ -101,6 +107,14 @@ internal class StyxHttpServerFactory : StyxServerFactory { val config = serverConfig(configuration) val environment = context.environment() + val bossExecutor = context.executors()[config.bossExecutor] + .orElseThrow { IllegalArgumentException("StyxHttpServer($name) configuration error: bossExecutor='${config.bossExecutor}' not declared.") } + .styxService + + val workerExecutor = context.executors()[config.workerExecutor] + .orElseThrow { IllegalArgumentException("StyxHttpServer($name) configuration error: workerExecutor='${config.workerExecutor}' not declared.") } + .styxService + return NettyServerBuilder() .setMetricsRegistry(environment.metricRegistry()) .setProtocolConnector( @@ -137,17 +151,8 @@ internal class StyxHttpServerFactory : StyxServerFactory { .protocols(*config.tlsSettings.protocols.toTypedArray()) .build() })) - - // TODO: Unknown executor name - .bossExecutor(context.executors().get(config.bossExecutor) - .map { it.styxService } - .orElseThrow()) - - // TODO: Unknown executor name - .workerExecutor(context.executors().get(config.workerExecutor) - .map { it.styxService } - .orElseThrow()) - + .bossExecutor(bossExecutor) + .workerExecutor(workerExecutor) .handler({ request, ctx -> context.refLookup() .apply(StyxObjectReference(config.handler)) From 194e5164f97941553cfb3ae030f11ef5b51dc6ba Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 13 Feb 2020 19:16:27 +0000 Subject: [PATCH 09/16] Replace orTimeout() with get(). --- .../styx/services/YamlFileConfigurationServiceTest.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt b/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt index 43a067bc24..0551c8fddb 100644 --- a/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt +++ b/components/proxy/src/test/kotlin/com/hotels/styx/services/YamlFileConfigurationServiceTest.kt @@ -66,7 +66,7 @@ class YamlFileConfigurationServiceTest : FunSpec() { try { action(service) } finally { - service.stop().orTimeout(2, SECONDS).join() + service.stop().get(2, SECONDS) } } @@ -91,7 +91,7 @@ class YamlFileConfigurationServiceTest : FunSpec() { OriginsConfigConverter(serviceDb, RoutingObjectFactoryContext(objectStore = routeDb).get(), "origins-cookie"), YamlFileConfigurationServiceConfig(originsConfig.absolutePath, pollInterval = pollInterval), serviceDb)) { - it.start().orTimeout(2, SECONDS).join() + it.start().get(2, SECONDS) eventually(2.seconds, AssertionError::class.java) { routeDb.entrySet().size shouldBe 4 } @@ -786,7 +786,7 @@ internal data class CreatedService(val config: YamlFileConfigurationServiceTest. val startFuture = this.service.start() if (wait) { - startFuture.orTimeout(2, SECONDS).join() + startFuture.get(2, SECONDS) } return this @@ -801,7 +801,7 @@ internal data class CreatedService(val config: YamlFileConfigurationServiceTest. } fun stop() { - service.stop().orTimeout(2, SECONDS).join() + service.stop().get(2, SECONDS) } } From 3eb7b54a5c3976496d353440aed9fe78c8525ec8 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 13 Feb 2020 20:44:26 +0000 Subject: [PATCH 10/16] Back off unnecessary changes. --- .../proxy/src/main/java/com/hotels/styx/StyxServer.java | 4 +--- components/proxy/src/test/resources/logback.xml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java index b87de72220..028f391d50 100644 --- a/components/proxy/src/main/java/com/hotels/styx/StyxServer.java +++ b/components/proxy/src/main/java/com/hotels/styx/StyxServer.java @@ -111,10 +111,8 @@ private static StyxServer createStyxServer(String[] args) { LOG.info("Styx configFileLocation={}", startupConfig.configFileLocation()); LOG.info("Styx logConfigLocation={}", startupConfig.logConfigLocation()); - StyxConfig styxConfig = parseConfiguration(startupConfig); - StyxServerComponents components = new StyxServerComponents.Builder() - .styxConfig(styxConfig) + .styxConfig(parseConfiguration(startupConfig)) .startupConfig(startupConfig) .loggingSetUp(environment -> activateLogbackConfigurer(startupConfig)) .build(); diff --git a/components/proxy/src/test/resources/logback.xml b/components/proxy/src/test/resources/logback.xml index b0e9ff26a2..c1fd57d080 100644 --- a/components/proxy/src/test/resources/logback.xml +++ b/components/proxy/src/test/resources/logback.xml @@ -7,7 +7,7 @@ - + From 452a62f7149328d38227599ed446c0976293a23a Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Fri, 14 Feb 2020 10:21:20 +0000 Subject: [PATCH 11/16] Add executors configuration to YamlFileConfigurationService. --- .../styx/admin/handlers/UrlPatternRouter.java | 2 +- .../styx/services/OriginsConfigConverter.kt | 78 +++++------ .../services/YamlFileConfigurationService.kt | 22 ++- .../admin/OriginsFileCompatibilitySpec.kt | 129 ++++++++++++++++-- .../hotels/styx/support/StyxServerProvider.kt | 2 +- .../ft-suite/src/test/resources/logback.xml | 1 + 6 files changed, 175 insertions(+), 59 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java index 9221f46a6e..6b96d32958 100644 --- a/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java +++ b/components/proxy/src/main/java/com/hotels/styx/admin/handlers/UrlPatternRouter.java @@ -175,7 +175,7 @@ public List placeholderNames() { private static Pattern compilePattern(String pattern) { Matcher matcher = PLACEHOLDER_PATTERN.matcher(pattern); - return Pattern.compile(matcher.replaceAll("(?<$1>[a-zA-Z0-9-_]+)")); + return Pattern.compile(matcher.replaceAll("(?<$1>[a-zA-Z0-9-_.]+)")); } private static List placeholders(String pattern) { diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt b/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt index 4d57573352..d06ab350f8 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/services/OriginsConfigConverter.kt @@ -56,7 +56,8 @@ import org.slf4j.LoggerFactory internal class OriginsConfigConverter( val serviceDb: StyxObjectStore, val context: RoutingObjectFactory.Context, - val originRestrictionCookie: String?) { + val originRestrictionCookie: String?, + val executor: String = "Styx-Client-Global-Worker") { internal fun routingObjects(apps: List) = routingObjectConfigs(apps) @@ -120,6 +121,43 @@ internal class OriginsConfigConverter( providerObject) } + private fun toBackendServiceObjects(app: BackendService, originRestrictionCookie: String? = null) = app + .origins() + .sortedBy { it.id().toString() } + .map { hostProxy(app, it) } + .plus(loadBalancingGroup(app, originRestrictionCookie)) + + internal fun hostProxy(app: BackendService, origin: Origin) : StyxObjectDefinition { + val healthCheckTag :String = if (isHealthCheckConfigured(app)) stateTag(STATE_UNREACHABLE) else stateTag(STATE_ACTIVE) + + return StyxObjectDefinition( + "${app.id()}.${origin.id()}", + HOST_PROXY, + listOf(lbGroupTag(app.id().toString()), healthCheckTag), + hostProxyConfig( + app.connectionPoolConfig(), + app.tlsSettings().orElse(null), + app.responseTimeoutMillis(), + app.maxHeaderSize(), + origin, + "origins")); + } + + private fun hostProxyConfig(poolSettings: ConnectionPoolSettings, + tlsSettings: TlsSettings?, + responseTimeout: Int, + maxHeaderSize: Int, + origin: Origin, + metricsPrefix: String): JsonNode = MAPPER.valueToTree( + HostProxyConfiguration( + "${origin.host()}:${origin.port()}", + poolSettings, + tlsSettings, + responseTimeout, + maxHeaderSize, + metricsPrefix, + executor)) + companion object { val LOGGER = LoggerFactory.getLogger(this::class.java) val ROOT_OBJECT_NAME = "pathPrefixRouter" @@ -136,12 +174,6 @@ internal class OriginsConfigConverter( private val TYPE = object : TypeReference>() { } - private fun toBackendServiceObjects(app: BackendService, originRestrictionCookie: String? = null) = app - .origins() - .sortedBy { it.id().toString() } - .map { hostProxy(app, it) } - .plus(loadBalancingGroup(app, originRestrictionCookie)) - internal fun loadBalancingGroup(app: BackendService, originRestrictionCookie: String? = null) = if (app.rewrites().isEmpty()) { StyxObjectDefinition( "${app.id()}", @@ -151,22 +183,6 @@ internal class OriginsConfigConverter( interceptorPipelineConfig(app, originRestrictionCookie) } - internal fun hostProxy(app: BackendService, origin: Origin) : StyxObjectDefinition { - val healthCheckTag :String = if (isHealthCheckConfigured(app)) stateTag(STATE_UNREACHABLE) else stateTag(STATE_ACTIVE) - - return StyxObjectDefinition( - "${app.id()}.${origin.id()}", - HOST_PROXY, - listOf(lbGroupTag(app.id().toString()), healthCheckTag), - hostProxyConfig( - app.connectionPoolConfig(), - app.tlsSettings().orElse(null), - app.responseTimeoutMillis(), - app.maxHeaderSize(), - origin, - "origins")); - } - private fun isHealthCheckConfigured(app: BackendService): Boolean { return (app.healthCheckConfig() != null && app.healthCheckConfig().uri().isPresent @@ -221,21 +237,5 @@ internal class OriginsConfigConverter( .build() .parse(configSource(yamlConfig)).`as`(StyxObjectDefinition::class.java) } - - // TODO: Executor: Must add executor configuration settings: - private fun hostProxyConfig(poolSettings: ConnectionPoolSettings, - tlsSettings: TlsSettings?, - responseTimeout: Int, - maxHeaderSize: Int, - origin: Origin, - metricsPrefix: String): JsonNode = MAPPER.valueToTree( - HostProxyConfiguration( - "${origin.host()}:${origin.port()}", - poolSettings, - tlsSettings, - responseTimeout, - maxHeaderSize, - metricsPrefix, - "Styx-Client-Global-Worker")) } } diff --git a/components/proxy/src/main/kotlin/com/hotels/styx/services/YamlFileConfigurationService.kt b/components/proxy/src/main/kotlin/com/hotels/styx/services/YamlFileConfigurationService.kt index 75cb824bcb..1e9ea3f455 100644 --- a/components/proxy/src/main/kotlin/com/hotels/styx/services/YamlFileConfigurationService.kt +++ b/components/proxy/src/main/kotlin/com/hotels/styx/services/YamlFileConfigurationService.kt @@ -18,6 +18,7 @@ package com.hotels.styx.services import com.fasterxml.jackson.databind.JsonNode import com.google.common.net.MediaType.HTML_UTF_8 import com.google.common.net.MediaType.PLAIN_TEXT_UTF_8 +import com.hotels.styx.NettyExecutor import com.hotels.styx.common.http.handler.HttpContentHandler import com.hotels.styx.api.extension.service.spi.StyxService import com.hotels.styx.common.http.handler.HttpAggregator @@ -40,6 +41,7 @@ import com.hotels.styx.sourceTag import org.slf4j.LoggerFactory import java.io.PrintWriter import java.io.StringWriter +import java.lang.IllegalArgumentException import java.lang.RuntimeException import java.nio.charset.StandardCharsets.UTF_8 import java.time.Duration @@ -80,7 +82,8 @@ internal class YamlFileConfigurationService( field("originsFile", string()), optional("monitor", bool()), optional("ingressObject", string()), - optional("pollInterval", string())) + optional("pollInterval", string()), + optional("executor", string())) private val LOGGER = LoggerFactory.getLogger(YamlFileConfigurationService::class.java) } @@ -220,13 +223,26 @@ internal class YamlFileConfigurationService( private class DuplicateObjectException(message: String): RuntimeException(message) } -internal data class YamlFileConfigurationServiceConfig(val originsFile: String, val ingressObject: String = "", val monitor: Boolean = true, val pollInterval: String = "") +internal data class YamlFileConfigurationServiceConfig( + val originsFile: String, + val ingressObject: String = "", + val monitor: Boolean = true, + val executor: String = "Styx-Client-Global-Worker", + val pollInterval: String = "") internal class YamlFileConfigurationServiceFactory : ServiceProviderFactory { override fun create(name: String, context: RoutingObjectFactory.Context, jsonConfig: JsonNode, serviceDb: StyxObjectStore): StyxService { val serviceConfig = JsonNodeConfig(jsonConfig).`as`(YamlFileConfigurationServiceConfig::class.java)!! val originRestrictionCookie = context.environment().configuration().get("originRestrictionCookie").orElse(null) - return YamlFileConfigurationService(name, context.routeDb(), OriginsConfigConverter(serviceDb, context, originRestrictionCookie), serviceConfig, serviceDb) + context.executors().get((serviceConfig.executor)) + .orElseThrow { IllegalArgumentException("YamlFileConfigurationService($name) configuration error: executor='${serviceConfig.executor}' not declared.") } + + return YamlFileConfigurationService( + name, + context.routeDb(), + OriginsConfigConverter(serviceDb, context, originRestrictionCookie, serviceConfig.executor), + serviceConfig, + serviceDb) } } diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/admin/OriginsFileCompatibilitySpec.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/admin/OriginsFileCompatibilitySpec.kt index 223b1ed3df..da180c072c 100644 --- a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/admin/OriginsFileCompatibilitySpec.kt +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/admin/OriginsFileCompatibilitySpec.kt @@ -23,9 +23,10 @@ import com.hotels.styx.admin.handlers.ServiceProviderHandler import com.hotels.styx.api.HttpHeaderNames.CONTENT_TYPE import com.hotels.styx.api.HttpHeaderNames.HOST import com.hotels.styx.api.HttpHeaderValues.APPLICATION_JSON -import com.hotels.styx.api.HttpHeaderValues.HTML import com.hotels.styx.api.HttpRequest.get -import com.hotels.styx.api.HttpResponseStatus.* +import com.hotels.styx.api.HttpResponseStatus.BAD_GATEWAY +import com.hotels.styx.api.HttpResponseStatus.NOT_FOUND +import com.hotels.styx.api.HttpResponseStatus.OK import com.hotels.styx.api.RequestCookie.requestCookie import com.hotels.styx.client.StyxHttpClient import com.hotels.styx.routing.config.StyxObjectDefinition @@ -36,6 +37,7 @@ import com.hotels.styx.support.ResourcePaths import com.hotels.styx.support.StyxServerProvider import com.hotels.styx.support.adminHostHeader import com.hotels.styx.support.proxyHttpHostHeader +import com.hotels.styx.support.routingObject import com.hotels.styx.support.wait import io.kotlintest.Spec import io.kotlintest.eventually @@ -50,7 +52,6 @@ import kotlinx.coroutines.delay import org.slf4j.LoggerFactory import java.io.File import java.nio.charset.StandardCharsets.UTF_8 -import kotlin.io.writeText class OriginsFileCompatibilitySpec : FunSpec() { val tempDir = createTempDir(suffix = "-${this.javaClass.simpleName}") @@ -60,6 +61,17 @@ class OriginsFileCompatibilitySpec : FunSpec() { val styxServer = StyxServerProvider( defaultConfig = """ --- + providers: + originsFileLoader: + type: YamlFileConfigurationService + config: + originsFile: ${originsFile.absolutePath} + ingressObject: pathPrefixRouter + monitor: True + pollInterval: PT0.1S + + httpPipeline: pathPrefixRouter + proxy: connectors: http: @@ -74,18 +86,7 @@ class OriginsFileCompatibilitySpec : FunSpec() { inbound: enabled: true outbound: - enabled: true - - providers: - originsFileLoader: - type: YamlFileConfigurationService - config: - originsFile: ${originsFile.absolutePath} - ingressObject: pathPrefixRouter - monitor: True - pollInterval: PT0.1S - - httpPipeline: pathPrefixRouter + enabled: true """.trimIndent(), defaultLoggingConfig = ResourcePaths.fixturesHome( OriginsFileCompatibilitySpec::class.java, @@ -673,6 +674,104 @@ class OriginsFileCompatibilitySpec : FunSpec() { } } + context("Executors configuration") { + writeOrigins(""" + - id: appA + path: "/" + origins: + - { id: "appA-01", host: "localhost:${mockServerA01.port()}" } + """.trimIndent()) + + test("Uses named executor") { + styxServer.restart( + configuration = """ + --- + executors: + test-executor: + type: NettyExecutor + config: + threads: 1 + namePattern: MyTestExecutor + + providers: + originsFileLoader: + type: YamlFileConfigurationService + config: + originsFile: ${originsFile.absolutePath} + ingressObject: pathPrefixRouter + monitor: True + pollInterval: PT0.1S + executor: test-executor + + httpPipeline: pathPrefixRouter + + proxy: + connectors: + http: + port: 0 + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + + eventually(2.seconds, AssertionError::class.java) { + client.send(get("/foo") + .header(HOST, styxServer().proxyHttpHostHeader()) + .build()) + .wait()!! + .let { + it.status() shouldBe OK + } + } + + styxServer().routingObject("appA.appA-01", debug = true).get() + .shouldContain("executor: \"test-executor\"") + } + + test("Uses default executor, when named executor is not provided") { + styxServer.restart( + configuration = """ + --- + providers: + originsFileLoader: + type: YamlFileConfigurationService + config: + originsFile: ${originsFile.absolutePath} + ingressObject: pathPrefixRouter + monitor: True + pollInterval: PT0.1S + + httpPipeline: pathPrefixRouter + + proxy: + connectors: + http: + port: 0 + + admin: + connectors: + http: + port: 0 + """.trimIndent()) + + eventually(2.seconds, AssertionError::class.java) { + client.send(get("/foo/2") + .header(HOST, styxServer().proxyHttpHostHeader()) + .build()) + .wait()!! + .let { + it.status() shouldBe OK + } + } + + styxServer().routingObject("appA.appA-01", debug = true).get() + .shouldContain("executor: \"Styx-Client-Global-Worker\"") + } + } + context("Error scenarios") { diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt index eae2243923..2f57d76ba8 100644 --- a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/support/StyxServerProvider.kt @@ -135,7 +135,7 @@ fun StyxServerProvider.adminRequest(endpoint: String, debug: Boolean = false): H fun CompletableFuture.wait(debug: Boolean = false) = this.toMono() .doOnNext { if (debug) { - LOGGER.debug("${it.status()} - ${it.headers()} - ${it.bodyAs(UTF_8)}") + LOGGER.info("${it.status()} - ${it.headers()} - ${it.bodyAs(UTF_8)}") } } .block() diff --git a/system-tests/ft-suite/src/test/resources/logback.xml b/system-tests/ft-suite/src/test/resources/logback.xml index 28bb56db81..ffdbc892cf 100644 --- a/system-tests/ft-suite/src/test/resources/logback.xml +++ b/system-tests/ft-suite/src/test/resources/logback.xml @@ -16,5 +16,6 @@ + From 0fe6ae0efa88fdf3385338f6cb3f09ae3de3d2d2 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Fri, 14 Feb 2020 11:50:30 +0000 Subject: [PATCH 12/16] Fix intermittent test failure. --- .../kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt index 6d4f8dec28..3fc8ad0d74 100644 --- a/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt +++ b/system-tests/ft-suite/src/test/kotlin/com/hotels/styx/config/ExecutorSettingsSpec.kt @@ -28,6 +28,9 @@ import com.hotels.styx.support.serverPort import com.hotels.styx.support.threadCount import com.hotels.styx.support.wait import io.kotlintest.Spec +import io.kotlintest.eventually +import io.kotlintest.matchers.numerics.shouldBeGreaterThan +import io.kotlintest.seconds import io.kotlintest.shouldBe import io.kotlintest.specs.FeatureSpec import java.nio.charset.StandardCharsets.UTF_8 @@ -82,7 +85,7 @@ class ExecutorSettingsSpec : FeatureSpec() { threadCount("host-proxy") shouldBe 1 } - scenario("!HostProxy uses default configuration when `executors` are not specified") { + scenario("HostProxy uses default configuration when `executors` are not specified") { // It is impossible to verify, externally, on what thread HostProxy runs. // The best we can do is to make sure it still works. styxServer.restart(configuration = """ @@ -203,6 +206,9 @@ class ExecutorSettingsSpec : FeatureSpec() { port: 0 """.trimIndent()) + eventually(2.seconds, AssertionError::class.java) { + styxServer().serverPort("http") shouldBeGreaterThan 0 + } val httpPort = styxServer().serverPort("http") (1..10).map { client.send(get("/").header(HOST, "localhost:$httpPort").build()) } From e3f15427bbad6b0cf68c785210ea73865ff3ff60 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Wed, 26 Feb 2020 16:26:04 +0000 Subject: [PATCH 13/16] Re-set interrupted flag. --- .../java/com/hotels/styx/NettyExecutor.java | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/components/common/src/main/java/com/hotels/styx/NettyExecutor.java b/components/common/src/main/java/com/hotels/styx/NettyExecutor.java index 86d8c0435b..cafc3d78d4 100644 --- a/components/common/src/main/java/com/hotels/styx/NettyExecutor.java +++ b/components/common/src/main/java/com/hotels/styx/NettyExecutor.java @@ -26,10 +26,9 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.concurrent.TimeUnit; - import static com.hotels.styx.EventLoopGroups.epollEventLoopGroup; import static com.hotels.styx.EventLoopGroups.nioEventLoopGroup; +import static java.util.concurrent.TimeUnit.SECONDS; /** * A netty based executor for styx. @@ -43,7 +42,8 @@ public class NettyExecutor { /** * Constructs an netty/io event executor. - * @param name thread group name. + * + * @param name thread group name. * @param count thread count. * @return */ @@ -64,17 +64,18 @@ public static NettyExecutor create(String name, int count) { } private NettyExecutor(EventLoopGroup eventLoopGroup, - Class serverEventLoopClass, - Class clientEventLoopClass) { - this.serverEventLoopClass = serverEventLoopClass; - this.clientEventLoopClass = clientEventLoopClass; - this.eventLoopGroup = eventLoopGroup; + Class serverEventLoopClass, + Class clientEventLoopClass) { + this.serverEventLoopClass = serverEventLoopClass; + this.clientEventLoopClass = clientEventLoopClass; + this.eventLoopGroup = eventLoopGroup; } public void shut() { try { - eventLoopGroup.shutdownGracefully(0, 0, TimeUnit.SECONDS).await(5000); + eventLoopGroup.shutdownGracefully(0, 0, SECONDS).await(5000); } catch (InterruptedException e) { + Thread.currentThread().interrupt(); throw new RuntimeException(e); } } From 2b1d11c9c7de1449a37d6e79a9d56fd5c3305466 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 5 Mar 2020 10:28:55 +0000 Subject: [PATCH 14/16] Tidy up. --- .../hotels/styx/routing/config/Builtins.java | 2 +- .../styx/startup/StyxServerComponents.java | 28 +++++++++++-------- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java index 4d206364b5..c242c8632c 100644 --- a/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java +++ b/components/proxy/src/main/java/com/hotels/styx/routing/config/Builtins.java @@ -233,7 +233,7 @@ public static InetServer buildServer( } /** - * Builds a Styx server. + * Builds a Styx executor object. * * Styx server is a service that can accept incoming traffic from the client hosts. * diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 86174d86c5..72da005574 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -84,6 +84,10 @@ public class StyxServerComponents { private static final Logger LOGGER = getLogger(StyxServerComponents.class); private final NettyExecutor executor; + private final String NETTY_EXECUTOR = "NettyExecutor"; + private final String GLOBAL_SERVER_BOSS_NAME = "StyxHttpServer-Global-Boss"; + private final String GLOBAL_SERVER_WORKER_NAME = "StyxHttpServer-Global-Worker"; + private final String GLOBAL_CLIENT_WORKER_NAME = "Styx-Client-Global-Worker"; // CHECKSTYLE:OFF private StyxServerComponents(Builder builder) { @@ -101,27 +105,27 @@ private StyxServerComponents(Builder builder) { this.executor = NettyExecutor.create("Styx-Client-Worker", environment.configuration().proxyServerConfig().clientWorkerThreadsCount()); // Overwrite any existing or user-supplied values: - executorObjectStore.insert("StyxHttpServer-Global-Boss", new StyxObjectRecord<>( - "NettyExecutor", + executorObjectStore.insert(GLOBAL_SERVER_BOSS_NAME, new StyxObjectRecord<>( + NETTY_EXECUTOR, ImmutableSet.of("StyxInternal"), - new NettyExecutorConfig(0, "StyxHttpServer-Global-Boss").asJsonNode(), - NettyExecutor.create("StyxHttpServer-Global-Boss", 0))); + new NettyExecutorConfig(0, GLOBAL_SERVER_BOSS_NAME).asJsonNode(), + NettyExecutor.create(GLOBAL_SERVER_BOSS_NAME, 0))); // Overwrite any existing or user-supplied values: - executorObjectStore.insert("StyxHttpServer-Global-Worker", + executorObjectStore.insert(GLOBAL_SERVER_WORKER_NAME, new StyxObjectRecord<>( - "NettyExecutor", + NETTY_EXECUTOR, ImmutableSet.of("StyxInternal"), - new NettyExecutorConfig(0, "StyxHttpServer-Global-Worker").asJsonNode(), - NettyExecutor.create("StyxHttpServer-Global-Worker", 0))); + new NettyExecutorConfig(0, GLOBAL_SERVER_WORKER_NAME).asJsonNode(), + NettyExecutor.create(GLOBAL_SERVER_WORKER_NAME, 0))); // Overwrite any existing or user-supplied values: - executorObjectStore.insert("Styx-Client-Global-Worker", + executorObjectStore.insert(GLOBAL_CLIENT_WORKER_NAME, new StyxObjectRecord<>( - "NettyExecutor", + NETTY_EXECUTOR, ImmutableSet.of("StyxInternal"), - new NettyExecutorConfig(0, "Styx-Client-Global-Worker").asJsonNode(), - NettyExecutor.create("Styx-Client-Global-Worker", 0))); + new NettyExecutorConfig(0, GLOBAL_CLIENT_WORKER_NAME).asJsonNode(), + NettyExecutor.create(GLOBAL_CLIENT_WORKER_NAME, 0))); this.environment.configuration().get("executors", JsonNode.class) .map(StyxServerComponents::readComponents) From 83ba726081f6348e7532e73b9faba3177389c3ff Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 5 Mar 2020 10:30:17 +0000 Subject: [PATCH 15/16] Fix conflict. --- system-tests/ft-suite/src/test/resources/logback.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/system-tests/ft-suite/src/test/resources/logback.xml b/system-tests/ft-suite/src/test/resources/logback.xml index ffdbc892cf..9e521b9a4d 100644 --- a/system-tests/ft-suite/src/test/resources/logback.xml +++ b/system-tests/ft-suite/src/test/resources/logback.xml @@ -17,5 +17,6 @@ + From 1949da6d43756cef913182bfcc16a213195a2081 Mon Sep 17 00:00:00 2001 From: Mikko Karjalainen Date: Thu, 5 Mar 2020 11:02:40 +0000 Subject: [PATCH 16/16] Fix static analysis errors. --- .../styx/startup/StyxServerComponents.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java index 72da005574..9536bdc49d 100644 --- a/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java +++ b/components/proxy/src/main/java/com/hotels/styx/startup/StyxServerComponents.java @@ -22,10 +22,11 @@ import com.google.common.collect.ImmutableSet; import com.google.common.eventbus.AsyncEventBus; import com.hotels.styx.Environment; -import com.hotels.styx.NettyExecutor; import com.hotels.styx.InetServer; +import com.hotels.styx.NettyExecutor; import com.hotels.styx.StartupConfig; import com.hotels.styx.StyxConfig; +import com.hotels.styx.StyxObjectRecord; import com.hotels.styx.Version; import com.hotels.styx.api.MetricRegistry; import com.hotels.styx.api.configuration.Configuration; @@ -43,7 +44,6 @@ import com.hotels.styx.routing.config.StyxObjectDefinition; import com.hotels.styx.routing.db.StyxObjectStore; import com.hotels.styx.routing.handlers.RouteRefLookup.RouteDbRefLookup; -import com.hotels.styx.StyxObjectRecord; import com.hotels.styx.startup.extensions.ConfiguredPluginFactory; import org.slf4j.Logger; @@ -72,6 +72,12 @@ * Configuration required to set-up the core Styx services, such as the proxy and admin servers. */ public class StyxServerComponents { + private static final Logger LOGGER = getLogger(StyxServerComponents.class); + private static final String NETTY_EXECUTOR = "NettyExecutor"; + private static final String GLOBAL_SERVER_BOSS_NAME = "StyxHttpServer-Global-Boss"; + private static final String GLOBAL_SERVER_WORKER_NAME = "StyxHttpServer-Global-Worker"; + private static final String GLOBAL_CLIENT_WORKER_NAME = "Styx-Client-Global-Worker"; + private final Environment environment; private final Map services; private final List plugins; @@ -81,13 +87,7 @@ public class StyxServerComponents { private final StyxObjectStore> executorObjectStore = new StyxObjectStore<>(); private final RoutingObjectFactory.Context routingObjectContext; private final StartupConfig startupConfig; - - private static final Logger LOGGER = getLogger(StyxServerComponents.class); private final NettyExecutor executor; - private final String NETTY_EXECUTOR = "NettyExecutor"; - private final String GLOBAL_SERVER_BOSS_NAME = "StyxHttpServer-Global-Boss"; - private final String GLOBAL_SERVER_WORKER_NAME = "StyxHttpServer-Global-Worker"; - private final String GLOBAL_CLIENT_WORKER_NAME = "Styx-Client-Global-Worker"; // CHECKSTYLE:OFF private StyxServerComponents(Builder builder) {