diff --git a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java index f2c1bb4e3..1b1ded997 100644 --- a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java +++ b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java @@ -27,6 +27,7 @@ import com.netflix.hystrix.HystrixEventType; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolMetrics; +import com.netflix.hystrix.util.PlatformSpecific; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import rx.functions.Func0; @@ -74,7 +75,15 @@ public class HystrixMetricsPoller { */ public HystrixMetricsPoller(MetricsAsJsonPollerListener listener, int delay) { this.listener = listener; - executor = new ScheduledThreadPoolExecutor(1, new MetricsPollerThreadFactory()); + + ThreadFactory threadFactory = null; + if (!PlatformSpecific.isAppEngine()) { + threadFactory = new MetricsPollerThreadFactory(); + } else { + threadFactory = PlatformSpecific.getAppEngineThreadFactory(); + } + + executor = new ScheduledThreadPoolExecutor(1, threadFactory); this.delay = delay; } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java index 3d5f92c64..a95668896 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/concurrency/HystrixConcurrencyStrategy.java @@ -15,6 +15,13 @@ */ package com.netflix.hystrix.strategy.concurrency; +import com.netflix.hystrix.HystrixCommand; +import com.netflix.hystrix.HystrixThreadPool; +import com.netflix.hystrix.HystrixThreadPoolKey; +import com.netflix.hystrix.strategy.HystrixPlugins; +import com.netflix.hystrix.strategy.properties.HystrixProperty; +import com.netflix.hystrix.util.PlatformSpecific; + import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; @@ -24,12 +31,6 @@ import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; -import com.netflix.hystrix.HystrixCommand; -import com.netflix.hystrix.HystrixThreadPool; -import com.netflix.hystrix.HystrixThreadPoolKey; -import com.netflix.hystrix.strategy.HystrixPlugins; -import com.netflix.hystrix.strategy.properties.HystrixProperty; - /** * Abstract class for defining different behavior or implementations for concurrency related aspects of the system with default implementations. *

@@ -70,17 +71,24 @@ public abstract class HystrixConcurrencyStrategy { * @return instance of {@link ThreadPoolExecutor} */ public ThreadPoolExecutor getThreadPool(final HystrixThreadPoolKey threadPoolKey, HystrixProperty corePoolSize, HystrixProperty maximumPoolSize, HystrixProperty keepAliveTime, TimeUnit unit, BlockingQueue workQueue) { - return new ThreadPoolExecutor(corePoolSize.get(), maximumPoolSize.get(), keepAliveTime.get(), unit, workQueue, new ThreadFactory() { + ThreadFactory threadFactory = null; + if (!PlatformSpecific.isAppEngine()) { + threadFactory = new ThreadFactory() { + protected final AtomicInteger threadNumber = new AtomicInteger(0); - protected final AtomicInteger threadNumber = new AtomicInteger(0); + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet()); + thread.setDaemon(true); + return thread; + } + + }; + } else { + threadFactory = PlatformSpecific.getAppEngineThreadFactory(); + } - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "hystrix-" + threadPoolKey.name() + "-" + threadNumber.incrementAndGet()); - thread.setDaemon(true); - return thread; - } - }); + return new ThreadPoolExecutor(corePoolSize.get(), maximumPoolSize.get(), keepAliveTime.get(), unit, workQueue, threadFactory); } /** diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java b/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java index 123eea989..722b1dff1 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/util/HystrixTimer.java @@ -15,6 +15,13 @@ */ package com.netflix.hystrix.util; +import com.netflix.hystrix.HystrixCollapser; +import com.netflix.hystrix.HystrixCommand; +import com.netflix.hystrix.strategy.HystrixPlugins; +import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import java.lang.ref.Reference; import java.lang.ref.SoftReference; import java.util.concurrent.ScheduledFuture; @@ -24,14 +31,6 @@ import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; -import com.netflix.hystrix.strategy.HystrixPlugins; -import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import com.netflix.hystrix.HystrixCollapser; -import com.netflix.hystrix.HystrixCommand; - /** * Timer used by {@link HystrixCommand} to timeout async executions and {@link HystrixCollapser} to trigger batch executions. */ @@ -153,17 +152,24 @@ public void initialize() { HystrixPropertiesStrategy propertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); int coreSize = propertiesStrategy.getTimerThreadPoolProperties().getCorePoolSize().get(); - executor = new ScheduledThreadPoolExecutor(coreSize, new ThreadFactory() { - final AtomicInteger counter = new AtomicInteger(); - - @Override - public Thread newThread(Runnable r) { - Thread thread = new Thread(r, "HystrixTimer-" + counter.incrementAndGet()); - thread.setDaemon(true); - return thread; - } + ThreadFactory threadFactory = null; + if (!PlatformSpecific.isAppEngine()) { + threadFactory = new ThreadFactory() { + final AtomicInteger counter = new AtomicInteger(); + + @Override + public Thread newThread(Runnable r) { + Thread thread = new Thread(r, "HystrixTimer-" + counter.incrementAndGet()); + thread.setDaemon(true); + return thread; + } + + }; + } else { + threadFactory = PlatformSpecific.getAppEngineThreadFactory(); + } - }); + executor = new ScheduledThreadPoolExecutor(coreSize, threadFactory); initialized = true; } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java b/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java new file mode 100644 index 000000000..263b63667 --- /dev/null +++ b/hystrix-core/src/main/java/com/netflix/hystrix/util/PlatformSpecific.java @@ -0,0 +1,77 @@ +/** + * Copyright 2016 Netflix, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.netflix.hystrix.util; + +import java.lang.reflect.InvocationTargetException; +import java.util.concurrent.ThreadFactory; + +public class PlatformSpecific { + private final boolean isAppEngine; + + private static PlatformSpecific INSTANCE = new PlatformSpecific(); + + private PlatformSpecific() { + isAppEngine = determineAppEngineReflectively(); + } + + public static boolean isAppEngine() { + return INSTANCE.isAppEngine; + } + + /* + * This detection mechanism is from Guava - specifically + * http://docs.guava-libraries.googlecode.com/git/javadoc/src-html/com/google/common/util/concurrent/MoreExecutors.html#line.766 + */ + private static boolean determineAppEngineReflectively() { + if (System.getProperty("com.google.appengine.runtime.environment") == null) { + return false; + } + try { + // If the current environment is null, we're not inside AppEngine. + return Class.forName("com.google.apphosting.api.ApiProxy") + .getMethod("getCurrentEnvironment") + .invoke(null) != null; + } catch (ClassNotFoundException e) { + // If ApiProxy doesn't exist, we're not on AppEngine at all. + return false; + } catch (InvocationTargetException e) { + // If ApiProxy throws an exception, we're not in a proper AppEngine environment. + return false; + } catch (IllegalAccessException e) { + // If the method isn't accessible, we're not on a supported version of AppEngine; + return false; + } catch (NoSuchMethodException e) { + // If the method doesn't exist, we're not on a supported version of AppEngine; + return false; + } + } + + public static ThreadFactory getAppEngineThreadFactory() { + try { + return (ThreadFactory) Class.forName("com.google.appengine.api.ThreadManager") + .getMethod("currentRequestThreadFactory") + .invoke(null); + } catch (IllegalAccessException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (ClassNotFoundException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (NoSuchMethodException e) { + throw new RuntimeException("Couldn't invoke ThreadManager.currentRequestThreadFactory", e); + } catch (InvocationTargetException e) { + throw new RuntimeException(e.getCause()); + } + } +}