CAMEL-11321: Start Camel faster by letting LRUCache warmup concurrently as that takes up 150 millis or more.
Project: http://git-wip-us.apache.org/repos/asf/camel/repo Commit: http://git-wip-us.apache.org/repos/asf/camel/commit/f8e68bac Tree: http://git-wip-us.apache.org/repos/asf/camel/tree/f8e68bac Diff: http://git-wip-us.apache.org/repos/asf/camel/diff/f8e68bac Branch: refs/heads/master Commit: f8e68bac676d9a1a43f1f2744aa467cba77ec169 Parents: f647c22 Author: Claus Ibsen <davscl...@apache.org> Authored: Tue Jul 4 15:46:17 2017 +0200 Committer: Claus Ibsen <davscl...@apache.org> Committed: Wed Jul 5 09:28:22 2017 +0200 ---------------------------------------------------------------------- .../apache/camel/impl/DefaultCamelContext.java | 13 +++- .../impl/DefaultPackageScanClassResolver.java | 4 +- .../camel/impl/ProvisionalEndpointRegistry.java | 74 ++++++++++++++++++++ .../management/DefaultManagementAgent.java | 10 ++- .../DefaultManagementMBeanAssembler.java | 13 +++- .../camel/management/MBeanInfoAssembler.java | 6 +- .../org/apache/camel/util/LRUCacheFactory.java | 59 ++++++++++++++++ .../org/apache/camel/ContextTestSupport.java | 2 + 8 files changed, 173 insertions(+), 8 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java index ef50425..3ebad6d 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultCamelContext.java @@ -178,6 +178,7 @@ import org.apache.camel.util.EventHelper; import org.apache.camel.util.IOHelper; import org.apache.camel.util.IntrospectionSupport; import org.apache.camel.util.JsonSchemaHelper; +import org.apache.camel.util.LRUCacheFactory; import org.apache.camel.util.LoadPropertiesException; import org.apache.camel.util.ObjectHelper; import org.apache.camel.util.OrderedComparator; @@ -316,10 +317,18 @@ public class DefaultCamelContext extends ServiceSupport implements ModelCamelCon * Use one of the other constructors to force use an explicit registry / JNDI. */ public DefaultCamelContext() { + boolean warmUp = "true".equalsIgnoreCase(System.getProperty("CamelWarmUpLRUCacheFactory", "true")); + if (warmUp) { + // warm-up LRUCache which happens in a background test, which can speedup starting Camel + // as the warm-up can run concurrently with starting up Camel and the runtime container Camel may be running inside + LRUCacheFactory.warmUp(); + } + this.executorServiceManager = new DefaultExecutorServiceManager(this); - // create endpoint registry at first since end users may access endpoints before CamelContext is started - this.endpoints = new DefaultEndpointRegistry(this); + // create a provisional (temporary) endpoint registry at first since end users may access endpoints before CamelContext is started + // we will later transfer the endpoints to the actual DefaultEndpointRegistry later, but we do this to starup Camel faster. + this.endpoints = new ProvisionalEndpointRegistry(); // add the defer service startup listener this.startupListeners.add(deferStartupListener); http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/impl/DefaultPackageScanClassResolver.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/DefaultPackageScanClassResolver.java b/camel-core/src/main/java/org/apache/camel/impl/DefaultPackageScanClassResolver.java index a9074f8..13dfa3d 100644 --- a/camel-core/src/main/java/org/apache/camel/impl/DefaultPackageScanClassResolver.java +++ b/camel-core/src/main/java/org/apache/camel/impl/DefaultPackageScanClassResolver.java @@ -47,7 +47,7 @@ import org.apache.camel.spi.PackageScanClassResolver; import org.apache.camel.spi.PackageScanFilter; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.IOHelper; -import org.apache.camel.util.LRUSoftCache; +import org.apache.camel.util.LRUCacheFactory; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -510,7 +510,7 @@ public class DefaultPackageScanClassResolver extends ServiceSupport implements P protected void doStart() throws Exception { if (jarCache == null) { // use a JAR cache to speed up scanning JARs, but let it be soft referenced so it can claim the data when memory is needed - jarCache = new LRUSoftCache<String, List<String>>(1000); + jarCache = LRUCacheFactory.newLRUCache(1000); } } http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/impl/ProvisionalEndpointRegistry.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/impl/ProvisionalEndpointRegistry.java b/camel-core/src/main/java/org/apache/camel/impl/ProvisionalEndpointRegistry.java new file mode 100644 index 0000000..0600066 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/impl/ProvisionalEndpointRegistry.java @@ -0,0 +1,74 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.impl; + +import java.util.HashMap; + +import org.apache.camel.Endpoint; +import org.apache.camel.spi.EndpointRegistry; + +/** + * A provisional (temporary) {@link EndpointRegistry} that is only used during startup of Apache Camel to + * make starting Camel faster while {@link org.apache.camel.util.LRUCacheFactory} is warming up etc. + */ +class ProvisionalEndpointRegistry extends HashMap<EndpointKey, Endpoint> implements EndpointRegistry<EndpointKey> { + + @Override + public void start() throws Exception { + // noop + } + + @Override + public void stop() throws Exception { + // noop + } + + @Override + public int staticSize() { + return 0; + } + + @Override + public int dynamicSize() { + return 0; + } + + @Override + public int getMaximumCacheSize() { + return 0; + } + + @Override + public void purge() { + // noop + } + + @Override + public boolean isStatic(String key) { + return false; + } + + @Override + public boolean isDynamic(String key) { + return false; + } + + @Override + public void cleanUp() { + // noop + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/management/DefaultManagementAgent.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/DefaultManagementAgent.java b/camel-core/src/main/java/org/apache/camel/management/DefaultManagementAgent.java index 7f6449b..d3f7280 100644 --- a/camel-core/src/main/java/org/apache/camel/management/DefaultManagementAgent.java +++ b/camel-core/src/main/java/org/apache/camel/management/DefaultManagementAgent.java @@ -49,6 +49,7 @@ import org.apache.camel.spi.ManagementMBeanAssembler; import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.InetAddressUtil; import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.ServiceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -66,6 +67,8 @@ public class DefaultManagementAgent extends ServiceSupport implements Management private CamelContext camelContext; private MBeanServer server; + private ManagementMBeanAssembler assembler; + // need a name -> actual name mapping as some servers changes the names (such as WebSphere) private final ConcurrentMap<ObjectName, ObjectName> mbeansRegistered = new ConcurrentHashMap<ObjectName, ObjectName>(); private JMXConnectorServer cs; @@ -339,7 +342,6 @@ public class DefaultManagementAgent extends ServiceSupport implements Management registerMBeanWithServer(obj, name, forceRegistration); } catch (NotCompliantMBeanException e) { // If this is not a "normal" MBean, then try to deploy it using JMX annotations - ManagementMBeanAssembler assembler = camelContext.getManagementMBeanAssembler(); ObjectHelper.notNull(assembler, "ManagementMBeanAssembler", camelContext); Object mbean = assembler.assemble(server, obj, name); if (mbean != null) { @@ -386,6 +388,10 @@ public class DefaultManagementAgent extends ServiceSupport implements Management createMBeanServer(); } + // ensure assembler is started + assembler = camelContext.getManagementMBeanAssembler(); + ServiceHelper.startService(assembler); + LOG.debug("Starting JMX agent on server: {}", getMBeanServer()); } @@ -432,6 +438,8 @@ public class DefaultManagementAgent extends ServiceSupport implements Management + " exceptions caught while unregistering MBeans during stop operation." + " See INFO log for details."); } + + ServiceHelper.stopService(assembler); } private void registerMBeanWithServer(Object obj, ObjectName name, boolean forceRegistration) http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/management/DefaultManagementMBeanAssembler.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/DefaultManagementMBeanAssembler.java b/camel-core/src/main/java/org/apache/camel/management/DefaultManagementMBeanAssembler.java index a28bcbb..dc8f970 100644 --- a/camel-core/src/main/java/org/apache/camel/management/DefaultManagementMBeanAssembler.java +++ b/camel-core/src/main/java/org/apache/camel/management/DefaultManagementMBeanAssembler.java @@ -29,7 +29,9 @@ import org.apache.camel.api.management.ManagedInstance; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.api.management.NotificationSenderAware; import org.apache.camel.spi.ManagementMBeanAssembler; +import org.apache.camel.support.ServiceSupport; import org.apache.camel.util.ObjectHelper; +import org.apache.camel.util.ServiceHelper; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,7 +42,7 @@ import org.slf4j.LoggerFactory; * * @version */ -public class DefaultManagementMBeanAssembler implements ManagementMBeanAssembler { +public class DefaultManagementMBeanAssembler extends ServiceSupport implements ManagementMBeanAssembler { private static final Logger LOG = LoggerFactory.getLogger(DefaultManagementMBeanAssembler.class); protected final MBeanInfoAssembler assembler; protected final CamelContext camelContext; @@ -114,4 +116,13 @@ public class DefaultManagementMBeanAssembler implements ManagementMBeanAssembler return mbean; } + @Override + protected void doStart() throws Exception { + ServiceHelper.startService(assembler); + } + + @Override + protected void doStop() throws Exception { + ServiceHelper.stopService(assembler); + } } http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/management/MBeanInfoAssembler.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/management/MBeanInfoAssembler.java b/camel-core/src/main/java/org/apache/camel/management/MBeanInfoAssembler.java index 09cbf89..4dfb0b7 100644 --- a/camel-core/src/main/java/org/apache/camel/management/MBeanInfoAssembler.java +++ b/camel-core/src/main/java/org/apache/camel/management/MBeanInfoAssembler.java @@ -40,6 +40,7 @@ import org.apache.camel.api.management.ManagedOperation; import org.apache.camel.api.management.ManagedResource; import org.apache.camel.util.IntrospectionSupport; import org.apache.camel.util.LRUCache; +import org.apache.camel.util.LRUCacheFactory; import org.apache.camel.util.LRUWeakCache; import org.apache.camel.util.ObjectHelper; import org.slf4j.Logger; @@ -57,7 +58,7 @@ public class MBeanInfoAssembler implements Service { // use a cache to speedup gathering JMX MBeanInfo for known classes // use a weak cache as we dont want the cache to keep around as it reference classes // which could prevent classloader to unload classes if being referenced from this cache - private final LRUCache<Class<?>, MBeanAttributesAndOperations> cache = new LRUWeakCache<Class<?>, MBeanAttributesAndOperations>(1000); + private LRUCache<Class<?>, MBeanAttributesAndOperations> cache; public MBeanInfoAssembler() { } @@ -67,8 +68,9 @@ public class MBeanInfoAssembler implements Service { } @Override + @SuppressWarnings("unchecked") public void start() throws Exception { - // noop + cache = LRUCacheFactory.newLRUWeakCache(1000); } @Override http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/main/java/org/apache/camel/util/LRUCacheFactory.java ---------------------------------------------------------------------- diff --git a/camel-core/src/main/java/org/apache/camel/util/LRUCacheFactory.java b/camel-core/src/main/java/org/apache/camel/util/LRUCacheFactory.java new file mode 100644 index 0000000..59b4620 --- /dev/null +++ b/camel-core/src/main/java/org/apache/camel/util/LRUCacheFactory.java @@ -0,0 +1,59 @@ +/** + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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 org.apache.camel.util; + +import org.apache.camel.util.concurrent.ThreadHelper; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Factory to create {@link LRUCache} instances. + */ +public final class LRUCacheFactory { + + // TODO: use LRUCacheFactory in other places to create the LRUCaches + + private static final Logger LOG = LoggerFactory.getLogger(LRUCacheFactory.class); + + private LRUCacheFactory() { + } + + @SuppressWarnings("unchecked") + public static void warmUp() { + // create a dummy map in a separate thread to warmup the Caffeine cache + // as we want to do this as early as possible while creating CamelContext + // so when Camel is starting up its faster as the Caffeine cache has been initialized + Runnable warmup = () -> { + LOG.debug("Warming up LRUCache ..."); + newLRUCache(16); + LOG.debug("Warming up LRUCache complete"); + }; + + String threadName = ThreadHelper.resolveThreadName(null, "LRUCacheFactory"); + + Thread thread = new Thread(warmup, threadName); + thread.start(); + } + + public static LRUCache newLRUCache(int maximumCacheSize) { + return new LRUCache(maximumCacheSize); + } + + public static LRUWeakCache newLRUWeakCache(int maximumCacheSize) { + return new LRUWeakCache(maximumCacheSize); + } +} http://git-wip-us.apache.org/repos/asf/camel/blob/f8e68bac/camel-core/src/test/java/org/apache/camel/ContextTestSupport.java ---------------------------------------------------------------------- diff --git a/camel-core/src/test/java/org/apache/camel/ContextTestSupport.java b/camel-core/src/test/java/org/apache/camel/ContextTestSupport.java index e4544ce..8b9dfe4 100644 --- a/camel-core/src/test/java/org/apache/camel/ContextTestSupport.java +++ b/camel-core/src/test/java/org/apache/camel/ContextTestSupport.java @@ -83,6 +83,8 @@ public abstract class ContextTestSupport extends TestSupport { protected void setUp() throws Exception { // make SEDA testing faster System.setProperty("CamelSedaPollTimeout", "10"); + // no need to warm-up when testing camel-core as that creates a new thread per CamelContext and Caffiene is initialized only once + System.setProperty("CamelWarmUpLRUCacheFactory", "false"); if (!useJmx()) { disableJMX();