From add0d98a4e79f86d4ed34be8b31d80c3d8e1533e Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 3 Dec 2012 21:49:52 -0800 Subject: [PATCH 1/7] Version 1.1.0 Changes to default metrics publishing behavior are significant enough to warrant a bump from 1.0.x to 1.1.x --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index 7679d323c..191b2b0ea 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.0.5-SNAPSHOT +version=1.1.0-SNAPSHOT From e95e9f81aee9df9e6c95245dbdb8fb80167123ce Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 3 Dec 2012 21:50:37 -0800 Subject: [PATCH 2/7] Add license header --- .../HystrixEventStreamMetricsObserver.java | 15 +++++++++++++++ .../HystrixMetricsStreamServlet.java | 15 +++++++++++++++ .../servostream/HystrixServoPoller.java | 15 +++++++++++++++ .../contrib/servostream/MetricGroup.java | 15 +++++++++++++++ .../SynchronizedHttpServletResponse.java | 18 ++++++++++++++++++ .../HystrixMetricsPublisherCommandDefault.java | 15 +++++++++++++++ ...strixMetricsPublisherThreadPoolDefault.java | 15 +++++++++++++++ 7 files changed, 108 insertions(+) diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixEventStreamMetricsObserver.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixEventStreamMetricsObserver.java index c508f0bd9..b3d937156 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixEventStreamMetricsObserver.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixEventStreamMetricsObserver.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.contrib.servostream; import java.util.ArrayList; diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java index d66f80595..387ca54c0 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.contrib.servostream; import java.io.IOException; diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixServoPoller.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixServoPoller.java index 6ae844211..5126ae1ed 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixServoPoller.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixServoPoller.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.contrib.servostream; import java.util.concurrent.Executors; diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/MetricGroup.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/MetricGroup.java index 50dfd53c2..ccea06ffa 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/MetricGroup.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/MetricGroup.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.contrib.servostream; import static org.junit.Assert.*; diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/SynchronizedHttpServletResponse.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/SynchronizedHttpServletResponse.java index e607a1913..2f87539d7 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/SynchronizedHttpServletResponse.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/SynchronizedHttpServletResponse.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.contrib.servostream; import java.io.IOException; @@ -47,6 +62,7 @@ public synchronized String encodeRedirectURL(String arg0) { return actualResponse.encodeRedirectURL(arg0); } + @SuppressWarnings("deprecation") public synchronized String encodeRedirectUrl(String arg0) { return actualResponse.encodeRedirectUrl(arg0); } @@ -55,6 +71,7 @@ public synchronized String encodeURL(String arg0) { return actualResponse.encodeURL(arg0); } + @SuppressWarnings("deprecation") public synchronized String encodeUrl(String arg0) { return actualResponse.encodeUrl(arg0); } @@ -141,6 +158,7 @@ public synchronized void setLocale(Locale arg0) { actualResponse.setLocale(arg0); } + @SuppressWarnings("deprecation") public synchronized void setStatus(int arg0, String arg1) { actualResponse.setStatus(arg0, arg1); } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherCommandDefault.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherCommandDefault.java index 8c6f11eb3..e9305ecb1 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherCommandDefault.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherCommandDefault.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.strategy.metrics; import com.netflix.hystrix.HystrixCircuitBreaker; diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherThreadPoolDefault.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherThreadPoolDefault.java index 2ac0e4b5b..d2d328473 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherThreadPoolDefault.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherThreadPoolDefault.java @@ -1,3 +1,18 @@ +/** + * Copyright 2012 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.strategy.metrics; import com.netflix.hystrix.HystrixThreadPoolKey; From 8b76e2f103a11fa31b1904be1aa30aa53bbf5eb1 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Mon, 3 Dec 2012 21:51:08 -0800 Subject: [PATCH 3/7] New submodule hystrix-metrics-event-stream --- hystrix-contrib/hystrix-metrics-event-stream/build.gradle | 5 +++++ settings.gradle | 7 ++++++- 2 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 hystrix-contrib/hystrix-metrics-event-stream/build.gradle diff --git a/hystrix-contrib/hystrix-metrics-event-stream/build.gradle b/hystrix-contrib/hystrix-metrics-event-stream/build.gradle new file mode 100644 index 000000000..0364cb354 --- /dev/null +++ b/hystrix-contrib/hystrix-metrics-event-stream/build.gradle @@ -0,0 +1,5 @@ + apply plugin: 'java' + dependencies { + compile project(':hystrix-core') + compile 'javax.servlet:javax.servlet-api:3.0.1' + } diff --git a/settings.gradle b/settings.gradle index 8a6f186ae..3713694bd 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1,7 @@ rootProject.name='hystrix' -include 'hystrix-core', 'hystrix-examples', 'hystrix-contrib:hystrix-request-servlet', 'hystrix-contrib:hystrix-servo-stream', 'hystrix-contrib:hystrix-servo-metrics-publisher' +include 'hystrix-core', \ +'hystrix-examples', \ +'hystrix-contrib:hystrix-request-servlet', \ +'hystrix-contrib:hystrix-servo-stream', \ +'hystrix-contrib:hystrix-servo-metrics-publisher', \ +'hystrix-contrib:hystrix-metrics-event-stream' From bfb66969baaa38a16e9dfce872e6ad6a4012d443 Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 4 Dec 2012 13:21:40 -0800 Subject: [PATCH 4/7] Remove Strategy Injection on HystrixCommand https://github.com/Netflix/Hystrix/issues/34 --- .../com/netflix/hystrix/HystrixCollapser.java | 40 +----- .../com/netflix/hystrix/HystrixCommand.java | 116 ++++-------------- .../netflix/hystrix/HystrixRequestLog.java | 2 +- .../netflix/hystrix/HystrixThreadPool.java | 14 +-- .../hystrix/strategy/HystrixPlugins.java | 59 +++------ .../HystrixMetricsPublisherFactory.java | 82 ++++++------- .../properties/HystrixPropertiesFactory.java | 31 ++--- 7 files changed, 101 insertions(+), 243 deletions(-) diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCollapser.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCollapser.java index f7d9340b4..9bb3d59a3 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCollapser.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCollapser.java @@ -136,12 +136,12 @@ protected HystrixCollapser(HystrixCollapserKey collapserKey) { * Fluent interface for constructor arguments */ protected HystrixCollapser(Setter setter) { - this(setter.collapserKey, setter.scope, new RealCollapserTimer(), setter.propertiesStrategy, setter.propertiesSetter, setter.concurrencyStrategy); + this(setter.collapserKey, setter.scope, new RealCollapserTimer(), setter.propertiesSetter); } - private HystrixCollapser(HystrixCollapserKey collapserKey, Scope scope, CollapserTimer timer, HystrixPropertiesStrategy propertiesFactory, HystrixCollapserProperties.Setter propertiesBuilder, HystrixConcurrencyStrategy concurrencyStrategy) { + private HystrixCollapser(HystrixCollapserKey collapserKey, Scope scope, CollapserTimer timer, HystrixCollapserProperties.Setter propertiesBuilder) { /* strategy: ConcurrencyStrategy */ - this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(concurrencyStrategy); + this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); this.timer = timer; this.scope = scope; @@ -152,7 +152,7 @@ private HystrixCollapser(HystrixCollapserKey collapserKey, Scope scope, Collapse this.collapserKey = collapserKey; } this.requestCache = HystrixRequestCache.getInstance(this.collapserKey, this.concurrencyStrategy); - this.properties = HystrixPropertiesFactory.getCollapserProperties(propertiesFactory, this.collapserKey, propertiesBuilder); + this.properties = HystrixPropertiesFactory.getCollapserProperties(this.collapserKey, propertiesBuilder); } /** @@ -1010,9 +1010,7 @@ private static String getDefaultNameFromClass(@SuppressWarnings("rawtypes") Clas public static class Setter { private final HystrixCollapserKey collapserKey; private Scope scope = Scope.REQUEST; // default if nothing is set - private HystrixPropertiesStrategy propertiesStrategy; private HystrixCollapserProperties.Setter propertiesSetter; - private HystrixConcurrencyStrategy concurrencyStrategy; private Setter(HystrixCollapserKey collapserKey) { this.collapserKey = collapserKey; @@ -1043,20 +1041,6 @@ public Setter andScope(Scope scope) { return this; } - /** - * @param propertiesStrategy - * {@link HystrixPropertiesStrategy} implementation to override the default behavior. - *

- * See JavaDoc on {@link HystrixPropertiesStrategy} class header for more information. - *

- * Will use default if left NULL. - * @return Setter for fluent interface via method chaining - */ - public Setter andPropertiesStrategy(HystrixPropertiesStrategy propertiesStrategy) { - this.propertiesStrategy = propertiesStrategy; - return this; - } - /** * @param propertiesSetter * {@link HystrixCollapserProperties.Setter} that allows instance specific property overrides (which can then be overridden by dynamic properties, see @@ -1071,20 +1055,6 @@ public Setter andCollapserPropertiesDefaults(HystrixCollapserProperties.Setter p return this; } - /** - * - * @param concurrencyStrategy - * {@link HystrixConcurrencyStrategy} implementation to override the default behavior. - *

- * See JavaDoc on {@link HystrixConcurrencyStrategy} class header for more information. - *

- * Will use default if left NULL. - * @return Setter for fluent interface via method chaining - */ - public Setter andConcurrencyStrategy(HystrixConcurrencyStrategy concurrencyStrategy) { - this.concurrencyStrategy = concurrencyStrategy; - return this; - } } // this is a micro-optimization but saves about 1-2microseconds (on 2011 MacBook Pro) @@ -1819,7 +1789,7 @@ public TestRequestCollapser(TestCollapserTimer timer, AtomicInteger counter, Str public TestRequestCollapser(Scope scope, TestCollapserTimer timer, AtomicInteger counter, String value, int defaultMaxRequestsInBatch, int defaultTimerDelayInMilliseconds, ConcurrentLinkedQueue>> executionLog) { // use a CollapserKey based on the CollapserTimer object reference so it's unique for each timer as we don't want caching // of properties to occur and we're using the default HystrixProperty which typically does caching - super(collapserKeyFromString(timer), scope, timer, null, HystrixCollapserProperties.Setter().withMaxRequestsInBatch(defaultMaxRequestsInBatch).withTimerDelayInMilliseconds(defaultTimerDelayInMilliseconds), null); + super(collapserKeyFromString(timer), scope, timer, HystrixCollapserProperties.Setter().withMaxRequestsInBatch(defaultMaxRequestsInBatch).withTimerDelayInMilliseconds(defaultTimerDelayInMilliseconds)); this.count = counter; this.value = value; this.commandsExecuted = executionLog; diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java index fc67e5a94..fb839f149 100755 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java @@ -61,7 +61,6 @@ import com.netflix.hystrix.strategy.concurrency.HystrixContextRunnable; import com.netflix.hystrix.strategy.concurrency.HystrixRequestContext; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; -import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherFactory; import com.netflix.hystrix.strategy.properties.HystrixPropertiesFactory; import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; @@ -129,6 +128,7 @@ public abstract class HystrixCommand implements HystrixExecutable { /** * Construct a {@link HystrixCommand} with defined {@link HystrixCommandGroupKey}. *

+ * The {@link HystrixCommandKey} will be derived from the implementing class name. * * @param group * {@link HystrixCommandGroupKey} used to group together multiple {@link HystrixCommand} objects. @@ -142,17 +142,18 @@ protected HystrixCommand(HystrixCommandGroupKey group) { } /** - * Construct a {@link HystrixCommand} with defined {@link Setter} that allows - * injecting property and strategy overrides and other optional arguments. + * Construct a {@link HystrixCommand} with defined {@link Setter} that allows injecting property and strategy overrides and other optional arguments. *

- * Null values on everything except 'group' will result in the default being used. + * NOTE: The {@link HystrixCommandKey} is used to associate a {@link HystrixCommand} with {@link HystrixCircuitBreaker}, {@link HystrixCommandMetrics} and other objects. + *

+ * Do not create multiple {@link HystrixCommand} implementations with the same {@link HystrixCommandKey} but different injected default properties as the first instantiated will win. * * @param setter * Fluent interface for constructor arguments */ protected HystrixCommand(Setter setter) { // use 'null' to specify use the default - this(setter.groupKey, setter.commandKey, setter.threadPoolKey, null, null, setter.propertiesStrategy, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, setter.notifier, setter.concurrencyStrategy, null, setter.metricsPublisher, null, null); + this(setter.groupKey, setter.commandKey, setter.threadPoolKey, null, null, setter.commandPropertiesDefaults, setter.threadPoolPropertiesDefaults, null, null, null); } /** @@ -163,8 +164,8 @@ protected HystrixCommand(Setter setter) { * Most of the args will revert to a valid default if 'null' is passed in. */ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, HystrixThreadPoolKey threadPoolKey, HystrixCircuitBreaker circuitBreaker, HystrixThreadPool threadPool, - HystrixPropertiesStrategy propertiesFactory, HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults, HystrixEventNotifier notifier, - HystrixConcurrencyStrategy concurrencyStrategy, HystrixCommandMetrics metrics, HystrixMetricsPublisher metricsPublisher, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore) { + HystrixCommandProperties.Setter commandPropertiesDefaults, HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults, + HystrixCommandMetrics metrics, TryableSemaphore fallbackSemaphore, TryableSemaphore executionSemaphore) { /* * CommandGroup initialization */ @@ -187,7 +188,7 @@ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, Hyst /* * Properties initialization */ - this.properties = HystrixPropertiesFactory.getCommandProperties(propertiesFactory, this.commandKey, commandPropertiesDefaults); + this.properties = HystrixPropertiesFactory.getCommandProperties(this.commandKey, commandPropertiesDefaults); /* * ThreadPoolKey @@ -212,15 +213,17 @@ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, Hyst } /* strategy: HystrixEventNotifier */ - this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier(notifier); + this.eventNotifier = HystrixPlugins.getInstance().getEventNotifier(); /* strategy: HystrixConcurrentStrategy */ - this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(concurrencyStrategy); + this.concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); /* * Metrics initialization */ if (metrics == null) { + // TODO this caches the first time it's loaded and will thus miss changes to threadPoolKey, properties and eventNotifier + // We need a better way of handling this now that we have HystrixPlugins this.metrics = HystrixCommandMetrics.getInstance(this.commandKey, this.commandGroup, this.threadPoolKey, this.properties, this.eventNotifier); } else { this.metrics = metrics; @@ -232,6 +235,8 @@ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, Hyst if (this.properties.circuitBreakerEnabled().get()) { if (circuitBreaker == null) { // get the default implementation of HystrixCircuitBreaker + // TODO this caches the first time it's loaded and will thus miss changes to properties + // We need a better way of handling this now that we have HystrixPlugins this.circuitBreaker = HystrixCircuitBreaker.Factory.getInstance(this.commandKey, this.commandGroup, this.properties, this.metrics); } else { this.circuitBreaker = circuitBreaker; @@ -241,14 +246,16 @@ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, Hyst } /* strategy: HystrixMetricsPublisherCommand */ - HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(metricsPublisher, this.commandKey, this.commandGroup, this.metrics, this.circuitBreaker, this.properties); + // TODO this caches the first time it's loaded and will thus miss changes to properties + // We need a better way of handling this now that we have HystrixPlugins + HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(this.commandKey, this.commandGroup, this.metrics, this.circuitBreaker, this.properties); /* * ThreadPool initialization */ if (threadPool == null) { // get the default implementation of HystrixThreadPool - this.threadPool = HystrixThreadPool.Factory.getInstance(this.threadPoolKey, this.concurrencyStrategy, metricsPublisher, propertiesFactory, threadPoolPropertiesDefaults); + this.threadPool = HystrixThreadPool.Factory.getInstance(this.threadPoolKey, threadPoolPropertiesDefaults); } else { this.threadPool = threadPool; } @@ -1547,12 +1554,8 @@ public static class Setter { private final HystrixCommandGroupKey groupKey; private HystrixCommandKey commandKey; private HystrixThreadPoolKey threadPoolKey; - private HystrixPropertiesStrategy propertiesStrategy; private HystrixCommandProperties.Setter commandPropertiesDefaults; private HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults; - private HystrixEventNotifier notifier; - private HystrixConcurrencyStrategy concurrencyStrategy; - private HystrixMetricsPublisher metricsPublisher; /** * Setter factory method containing required values. @@ -1618,18 +1621,6 @@ public Setter andThreadPoolKey(HystrixThreadPoolKey threadPoolKey) { return this; } - /** - * @param propertiesStrategy - * {@link HystrixPropertiesStrategy} implementation used to retrieve {@link HystrixCommandProperties} and {@link HystrixThreadPoolProperties}. - *

- * See the {@link HystrixPropertiesStrategy} JavaDocs for more information. - * @return Setter for fluent interface via method chaining - */ - public Setter andPropertiesStrategy(HystrixPropertiesStrategy propertiesStrategy) { - this.propertiesStrategy = propertiesStrategy; - return this; - } - /** * Optional * @@ -1658,42 +1649,6 @@ public Setter andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter return this; } - /** - * @param notifier - * {@link HystrixEventNotifier} implementation used to perform event notification. - *

- * See the {@link HystrixEventNotifier} JavaDocs for more information. - * @return Setter for fluent interface via method chaining - */ - public Setter andEventNotifier(HystrixEventNotifier notifier) { - this.notifier = notifier; - return this; - } - - /** - * @param concurrencyStrategy - * {@link HystrixConcurrencyStrategy} implementation used for concurrency related behavior. - *

- * See {@link HystrixConcurrencyStrategy} JavaDocs for more information. - * @return Setter for fluent interface via method chaining - */ - public Setter andConcurrencyStrategy(HystrixConcurrencyStrategy concurrencyStrategy) { - this.concurrencyStrategy = concurrencyStrategy; - return this; - } - - /** - * @param metricsPublisher - * {@link HystrixMetricsPublisher} implementation used for publishing metrics. - *

- * See {@link HystrixMetricsPublisher} JavaDocs for more information. - * @return Setter for fluent interface via method chaining - */ - public Setter andMetricsPublisher(HystrixMetricsPublisher metricsPublisher) { - this.metricsPublisher = metricsPublisher; - return this; - } - } public static class UnitTest { @@ -1702,6 +1657,7 @@ public static class UnitTest { public void prepareForTest() { /* we must call this to simulate a new request lifecycle running and clearing caches */ HystrixRequestContext.initializeContext(); + HystrixPlugins.getInstance().registerPropertiesStrategy(TEST_PROPERTIES_FACTORY); } @After @@ -1714,6 +1670,8 @@ public void cleanup() { // force properties to be clean as well ConfigurationManager.getConfigInstance().clear(); + + HystrixPlugins.getInstance().registerPropertiesStrategy(null); } /** @@ -4121,9 +4079,9 @@ public void testBadRequestExceptionViaQueueInSemaphore() { final TestCommandBuilder builder; TestHystrixCommand(TestCommandBuilder builder) { - super(builder.owner, builder.dependencyKey, builder.threadPoolKey, builder.circuitBreaker, builder.threadPool, builder.propertiesFactory, - builder.commandPropertiesDefaults, builder.threadPoolPropertiesDefaults, builder.notifier, builder.concurrencyStrategy, builder.metrics, - builder.metricsPublisher, builder.fallbackSemaphore, builder.executionSemaphore); + super(builder.owner, builder.dependencyKey, builder.threadPoolKey, builder.circuitBreaker, builder.threadPool, + builder.commandPropertiesDefaults, builder.threadPoolPropertiesDefaults, builder.metrics, + builder.fallbackSemaphore, builder.executionSemaphore); this.builder = builder; } @@ -4138,13 +4096,9 @@ static class TestCommandBuilder { HystrixThreadPoolKey threadPoolKey = null; HystrixCircuitBreaker circuitBreaker = _cb; HystrixThreadPool threadPool = null; - HystrixPropertiesStrategy propertiesFactory = TEST_PROPERTIES_FACTORY; HystrixCommandProperties.Setter commandPropertiesDefaults = HystrixCommandProperties.Setter.getUnitTestPropertiesSetter(); HystrixThreadPoolProperties.Setter threadPoolPropertiesDefaults = HystrixThreadPoolProperties.Setter.getUnitTestPropertiesBuilder(); - HystrixEventNotifier notifier = null; - HystrixConcurrencyStrategy concurrencyStrategy = null; HystrixCommandMetrics metrics = _cb.metrics; - HystrixMetricsPublisher metricsPublisher = null; TryableSemaphore fallbackSemaphore = null; TryableSemaphore executionSemaphore = null; @@ -4173,11 +4127,6 @@ TestCommandBuilder setThreadPool(HystrixThreadPool threadPool) { return this; } - TestCommandBuilder setPropertiesFactory(HystrixPropertiesStrategy propertiesFactory) { - this.propertiesFactory = propertiesFactory; - return this; - } - TestCommandBuilder setCommandPropertiesDefaults(HystrixCommandProperties.Setter commandPropertiesDefaults) { this.commandPropertiesDefaults = commandPropertiesDefaults; return this; @@ -4188,26 +4137,11 @@ TestCommandBuilder setThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.S return this; } - TestCommandBuilder setNotifier(HystrixEventNotifier notifier) { - this.notifier = notifier; - return this; - } - - TestCommandBuilder setConcurrencyStrategy(HystrixConcurrencyStrategy concurrencyStrategy) { - this.concurrencyStrategy = concurrencyStrategy; - return this; - } - TestCommandBuilder setMetrics(HystrixCommandMetrics metrics) { this.metrics = metrics; return this; } - TestCommandBuilder setMetricsPublisher(HystrixMetricsPublisher metricsPublisher) { - this.metricsPublisher = metricsPublisher; - return this; - } - TestCommandBuilder setFallbackSemaphore(TryableSemaphore fallbackSemaphore) { this.fallbackSemaphore = fallbackSemaphore; return this; diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixRequestLog.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixRequestLog.java index c3c696413..232182290 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixRequestLog.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixRequestLog.java @@ -82,7 +82,7 @@ public static HystrixRequestLog getCurrentRequest(HystrixConcurrencyStrategy con * @return {@link HystrixRequestLog} */ public static HystrixRequestLog getCurrentRequest() { - return getCurrentRequest(HystrixPlugins.getInstance().getConcurrencyStrategy(null)); + return currentRequestLog.get(HystrixPlugins.getInstance().getConcurrencyStrategy()); } /** diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java index a07fe6f1e..af30f8870 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java @@ -22,11 +22,10 @@ import javax.annotation.concurrent.ThreadSafe; +import com.netflix.hystrix.strategy.HystrixPlugins; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; -import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisher; import com.netflix.hystrix.strategy.metrics.HystrixMetricsPublisherFactory; import com.netflix.hystrix.strategy.properties.HystrixPropertiesFactory; -import com.netflix.hystrix.strategy.properties.HystrixPropertiesStrategy; /** * ThreadPool used to executed {@link HystrixCommand#run()} on separate threads when configured to do so with {@link HystrixCommandProperties#executionIsolationStrategy()}. @@ -87,7 +86,7 @@ public interface HystrixThreadPool { * * @return {@link HystrixThreadPool} instance */ - /* package */static HystrixThreadPool getInstance(HystrixThreadPoolKey threadPoolKey, HystrixConcurrencyStrategy concurrencyStrategy, HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesFactory, HystrixThreadPoolProperties.Setter propertiesBuilder) { + /* package */static HystrixThreadPool getInstance(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesBuilder) { // get the key to use instead of using the object itself so that if people forget to implement equals/hashcode things will still work String key = threadPoolKey.name(); @@ -102,7 +101,7 @@ public interface HystrixThreadPool { // Create and add to the map ... use putIfAbsent to atomically handle the possible race-condition of // 2 threads hitting this point at the same time and let ConcurrentHashMap provide us our thread-safety // If 2 threads hit here only one will get added and the other will get a non-null response instead. - HystrixThreadPool poolForKey = threadPools.putIfAbsent(key, new HystrixThreadPoolDefault(threadPoolKey, concurrencyStrategy, metricsPublisher, propertiesFactory, propertiesBuilder)); + HystrixThreadPool poolForKey = threadPools.putIfAbsent(key, new HystrixThreadPoolDefault(threadPoolKey, propertiesBuilder)); if (poolForKey == null) { // this means the putIfAbsent step just created a new one so let's retrieve and return it HystrixThreadPool threadPoolJustCreated = threadPools.get(key); @@ -126,14 +125,15 @@ public interface HystrixThreadPool { private final ThreadPoolExecutor threadPool; private final HystrixThreadPoolMetrics metrics; - public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixConcurrencyStrategy concurrencyStrategy, HystrixMetricsPublisher metricsPublisher, HystrixPropertiesStrategy propertiesFactory, HystrixThreadPoolProperties.Setter propertiesDefaults) { - this.properties = HystrixPropertiesFactory.getThreadPoolProperties(propertiesFactory, threadPoolKey, propertiesDefaults); + public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolProperties.Setter propertiesDefaults) { + this.properties = HystrixPropertiesFactory.getThreadPoolProperties(threadPoolKey, propertiesDefaults); + HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); this.queue = concurrencyStrategy.getBlockingQueue(properties.maxQueueSize().get()); this.threadPool = concurrencyStrategy.getThreadPool(threadPoolKey, properties.coreSize(), properties.coreSize(), properties.keepAliveTimeMinutes(), TimeUnit.MINUTES, queue); this.metrics = new HystrixThreadPoolMetrics(threadPoolKey, threadPool, properties); /* strategy: HystrixMetricsPublisherThreadPool */ - HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(metricsPublisher, threadPoolKey, this.metrics, this.properties); + HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(threadPoolKey, this.metrics, this.properties); } @Override diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java index b18404e82..546845dbe 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java @@ -1,12 +1,12 @@ /** * Copyright 2012 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 - * + * + * 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. @@ -15,7 +15,6 @@ */ package com.netflix.hystrix.strategy; -import com.netflix.hystrix.HystrixCollapser; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategy; import com.netflix.hystrix.strategy.concurrency.HystrixConcurrencyStrategyDefault; @@ -30,14 +29,8 @@ * Registry for plugin implementations that allows global override and handles the retrieval of correct implementation based on order of precedence: *

    *
  1. plugin registered globally via register methods in this class
  2. - *
  3. injected via {@link HystrixCommand} and {@link HystrixCollapser} constructors
  4. *
  5. default implementations
  6. *
- *

- * The injection approach is effective for {@link HystrixCommand} and {@link HystrixCollapser} implementations where you wish to have a different implementation without - * overriding all implementations. It is also useful when distributing a library where static override should not be used. - *

- * The globally registered plugin is useful when using commands from 3rd party libraries and you want to override all implementations in your entire system. */ public class HystrixPlugins { @@ -63,18 +56,13 @@ public static HystrixPlugins getInstance() { * {@link HystrixEventNotifier} implementation as injected via {@link HystrixCommand} * @return {@link HystrixEventNotifier} implementation to use */ - public HystrixEventNotifier getEventNotifier(HystrixEventNotifier injected) { + public HystrixEventNotifier getEventNotifier() { if (notifier != null) { // we have a global override so use it return notifier; } else { - if (injected != null) { - // we have an injected default - return injected; - } else { - // we don't have an injected default nor an override so construct a default - return HystrixEventNotifierDefault.getInstance(); - } + // we don't have an injected default nor an override so construct a default + return HystrixEventNotifierDefault.getInstance(); } } @@ -102,18 +90,13 @@ public void resetToDefaults() { * {@link HystrixConcurrencyStrategy} implementation as injected via {@link HystrixCommand} * @return {@link HystrixConcurrencyStrategy} implementation to use */ - public HystrixConcurrencyStrategy getConcurrencyStrategy(HystrixConcurrencyStrategy injected) { + public HystrixConcurrencyStrategy getConcurrencyStrategy() { if (concurrencyStrategy != null) { // we have a global override so use it return concurrencyStrategy; } else { - if (injected != null) { - // we have an injected default - return injected; - } else { - // we don't have an injected default nor an override so construct a default - return HystrixConcurrencyStrategyDefault.getInstance(); - } + // we don't have an injected default nor an override so construct a default + return HystrixConcurrencyStrategyDefault.getInstance(); } } @@ -134,18 +117,13 @@ public void registerConcurrencyStrategy(HystrixConcurrencyStrategy impl) { * {@link HystrixMetricsPublisher} implementation as injected via {@link HystrixCommand} * @return {@link HystrixMetricsPublisher} implementation to use */ - public HystrixMetricsPublisher getMetricsPublisher(HystrixMetricsPublisher injected) { + public HystrixMetricsPublisher getMetricsPublisher() { if (metricsPublisher != null) { // we have a global override so use it return metricsPublisher; } else { - if (injected != null) { - // we have an injected default - return injected; - } else { - // we don't have an injected default nor an override so construct a default - return HystrixMetricsPublisherDefault.getInstance(); - } + // we don't have an injected default nor an override so construct a default + return HystrixMetricsPublisherDefault.getInstance(); } } @@ -166,18 +144,13 @@ public void registerMetricsPublisher(HystrixMetricsPublisher impl) { * {@link HystrixPropertiesStrategy} implementation as injected via {@link HystrixCommand} * @return {@link HystrixPropertiesStrategy} implementation to use */ - public HystrixPropertiesStrategy getPropertiesStrategy(HystrixPropertiesStrategy injected) { + public HystrixPropertiesStrategy getPropertiesStrategy() { if (propertiesFactory != null) { // we have a global override so use it return propertiesFactory; } else { - if (injected != null) { - // we have an injected default - return injected; - } else { - // we don't have an injected default nor an override so construct a default - return HystrixPropertiesStrategyDefault.getInstance(); - } + // we don't have an injected default nor an override so construct a default + return HystrixPropertiesStrategyDefault.getInstance(); } } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherFactory.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherFactory.java index 594488e3d..f7484ff12 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherFactory.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/metrics/HystrixMetricsPublisherFactory.java @@ -1,12 +1,12 @@ /** * Copyright 2012 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 - * + * + * 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. @@ -44,18 +44,12 @@ */ public class HystrixMetricsPublisherFactory { - //TODO implement cacheKey instead of using key objects directly similar to how it's done in HystrixPropertiesFactory - // String is CommandKey.name() (we can't use CommandKey directly as we can't guarantee it implements hashcode/equals correctly) private static final ConcurrentHashMap commandPublishers = new ConcurrentHashMap(); /** * Get an instance of {@link HystrixMetricsPublisherCommand} with the given factory {@link HystrixMetricsPublisher} implementation for each {@link HystrixCommand} instance. * - * @param metricsPublisher - * Implementation of {@link HystrixMetricsPublisher} to use. - *

- * See {@link HystrixMetricsPublisher} class header JavaDocs for precedence of how this is retrieved. * @param commandKey * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @param commandOwner @@ -68,14 +62,14 @@ public class HystrixMetricsPublisherFactory { * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @return {@link HystrixMetricsPublisherCommand} instance */ - public static HystrixMetricsPublisherCommand createOrRetrievePublisherForCommand(HystrixMetricsPublisher metricsPublisher, HystrixCommandKey commandKey, HystrixCommandGroupKey commandOwner, HystrixCommandMetrics metrics, HystrixCircuitBreaker circuitBreaker, HystrixCommandProperties properties) { + public static HystrixMetricsPublisherCommand createOrRetrievePublisherForCommand(HystrixCommandKey commandKey, HystrixCommandGroupKey commandOwner, HystrixCommandMetrics metrics, HystrixCircuitBreaker circuitBreaker, HystrixCommandProperties properties) { // attempt to retrieve from cache first HystrixMetricsPublisherCommand publisher = commandPublishers.get(commandKey.name()); if (publisher != null) { return publisher; } // it doesn't exist so we need to create it - publisher = HystrixPlugins.getInstance().getMetricsPublisher(metricsPublisher).getMetricsPublisherForCommand(commandKey, commandOwner, metrics, circuitBreaker, properties); + publisher = HystrixPlugins.getInstance().getMetricsPublisher().getMetricsPublisherForCommand(commandKey, commandOwner, metrics, circuitBreaker, properties); // attempt to store it (race other threads) HystrixMetricsPublisherCommand existing = commandPublishers.putIfAbsent(commandKey.name(), publisher); if (existing == null) { @@ -108,14 +102,14 @@ public static HystrixMetricsPublisherCommand createOrRetrievePublisherForCommand * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForThreadPool} implementation * @return {@link HystrixMetricsPublisherThreadPool} instance */ - public static HystrixMetricsPublisherThreadPool createOrRetrievePublisherForThreadPool(HystrixMetricsPublisher metricsPublisher, HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolMetrics metrics, HystrixThreadPoolProperties properties) { + public static HystrixMetricsPublisherThreadPool createOrRetrievePublisherForThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolMetrics metrics, HystrixThreadPoolProperties properties) { // attempt to retrieve from cache first HystrixMetricsPublisherThreadPool publisher = threadPoolPublishers.get(threadPoolKey.name()); if (publisher != null) { return publisher; } // it doesn't exist so we need to create it - publisher = HystrixPlugins.getInstance().getMetricsPublisher(metricsPublisher).getMetricsPublisherForThreadPool(threadPoolKey, metrics, properties); + publisher = HystrixPlugins.getInstance().getMetricsPublisher().getMetricsPublisherForThreadPool(threadPoolKey, metrics, properties); // attempt to store it (race other threads) HystrixMetricsPublisherThreadPool existing = threadPoolPublishers.putIfAbsent(threadPoolKey.name(), publisher); if (existing == null) { @@ -138,39 +132,43 @@ public static class UnitTest { @Test public void testSingleInitializePerKey() { final TestHystrixMetricsPublisher publisher = new TestHystrixMetricsPublisher(); + HystrixPlugins.getInstance().registerMetricsPublisher(publisher); + try { + ArrayList threads = new ArrayList(); + for (int i = 0; i < 20; i++) { + threads.add(new Thread(new Runnable() { + + @Override + public void run() { + HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(TestCommandKey.TEST_A, null, null, null, null); + HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(TestCommandKey.TEST_B, null, null, null, null); + HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(TestThreadPoolKey.TEST_A, null, null); + } + + })); + } - ArrayList threads = new ArrayList(); - for (int i = 0; i < 20; i++) { - threads.add(new Thread(new Runnable() { + // start them + for (Thread t : threads) { + t.start(); + } - @Override - public void run() { - HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(publisher, TestCommandKey.TEST_A, null, null, null, null); - HystrixMetricsPublisherFactory.createOrRetrievePublisherForCommand(publisher, TestCommandKey.TEST_B, null, null, null, null); - HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(publisher, TestThreadPoolKey.TEST_A, null, null); + // wait for them to finish + for (Thread t : threads) { + try { + t.join(); + } catch (InterruptedException e) { + e.printStackTrace(); } - - })); - } - - // start them - for (Thread t : threads) { - t.start(); - } - - // wait for them to finish - for (Thread t : threads) { - try { - t.join(); - } catch (InterruptedException e) { - e.printStackTrace(); } - } - - // we should see 2 commands and 1 threadPool publisher created - assertEquals(2, publisher.commandCounter.get()); - assertEquals(1, publisher.threadCounter.get()); + // we should see 2 commands and 1 threadPool publisher created + assertEquals(2, publisher.commandCounter.get()); + assertEquals(1, publisher.threadCounter.get()); + } finally { + // clear the plugin + HystrixPlugins.getInstance().registerMetricsPublisher(null); + } } } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/properties/HystrixPropertiesFactory.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/properties/HystrixPropertiesFactory.java index 64aedb228..83beb8ad9 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/properties/HystrixPropertiesFactory.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/properties/HystrixPropertiesFactory.java @@ -25,6 +25,7 @@ import com.netflix.hystrix.HystrixThreadPool; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolProperties; +import com.netflix.hystrix.strategy.HystrixPlugins; /** * Factory for retrieving properties implementations. @@ -41,20 +42,14 @@ public class HystrixPropertiesFactory { /** * Get an instance of {@link HystrixCommandProperties} with the given factory {@link HystrixPropertiesStrategy} implementation for each {@link HystrixCommand} instance. * - * @param hystrixPropertiesStrategy - * Implementation of {@link HystrixPropertiesStrategy} to use - *

- * See {@link HystrixPropertiesStrategy} class header JavaDocs for precedence of how this is retrieved. * @param key * Pass-thru to {@link HystrixPropertiesStrategy#getCommandProperties} implementation. * @param builder * Pass-thru to {@link HystrixPropertiesStrategy#getCommandProperties} implementation. * @return {@link HystrixCommandProperties} instance */ - public static HystrixCommandProperties getCommandProperties(HystrixPropertiesStrategy hystrixPropertiesStrategy, HystrixCommandKey key, HystrixCommandProperties.Setter builder) { - if (hystrixPropertiesStrategy == null) { - hystrixPropertiesStrategy = HystrixPropertiesStrategyDefault.getInstance(); - } + public static HystrixCommandProperties getCommandProperties(HystrixCommandKey key, HystrixCommandProperties.Setter builder) { + HystrixPropertiesStrategy hystrixPropertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); String cacheKey = hystrixPropertiesStrategy.getCommandPropertiesCacheKey(key, builder); if (cacheKey != null) { HystrixCommandProperties properties = commandProperties.get(cacheKey); @@ -86,20 +81,14 @@ public static HystrixCommandProperties getCommandProperties(HystrixPropertiesStr /** * Get an instance of {@link HystrixThreadPoolProperties} with the given factory {@link HystrixPropertiesStrategy} implementation for each {@link HystrixThreadPool} instance. * - * @param hystrixPropertiesStrategy - * Implementation of {@link HystrixPropertiesStrategy} to use - *

- * See {@link HystrixPropertiesStrategy} class header JavaDocs for precedence of how this is retrieved. * @param key * Pass-thru to {@link HystrixPropertiesStrategy#getThreadPoolProperties} implementation. * @param builder * Pass-thru to {@link HystrixPropertiesStrategy#getThreadPoolProperties} implementation. * @return {@link HystrixThreadPoolProperties} instance */ - public static HystrixThreadPoolProperties getThreadPoolProperties(HystrixPropertiesStrategy hystrixPropertiesStrategy, HystrixThreadPoolKey key, HystrixThreadPoolProperties.Setter builder) { - if (hystrixPropertiesStrategy == null) { - hystrixPropertiesStrategy = HystrixPropertiesStrategyDefault.getInstance(); - } + public static HystrixThreadPoolProperties getThreadPoolProperties(HystrixThreadPoolKey key, HystrixThreadPoolProperties.Setter builder) { + HystrixPropertiesStrategy hystrixPropertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); String cacheKey = hystrixPropertiesStrategy.getThreadPoolPropertiesCacheKey(key, builder); if (cacheKey != null) { HystrixThreadPoolProperties properties = threadPoolProperties.get(cacheKey); @@ -131,20 +120,14 @@ public static HystrixThreadPoolProperties getThreadPoolProperties(HystrixPropert /** * Get an instance of {@link HystrixCollapserProperties} with the given factory {@link HystrixPropertiesStrategy} implementation for each {@link HystrixCollapserKey} instance. * - * @param hystrixPropertiesStrategy - * Implementation of {@link HystrixPropertiesStrategy} to use - *

- * See {@link HystrixPropertiesStrategy} class header JavaDocs for precedence of how this is retrieved. * @param key * Pass-thru to {@link HystrixPropertiesStrategy#getCollapserProperties} implementation. * @param builder * Pass-thru to {@link HystrixPropertiesStrategy#getCollapserProperties} implementation. * @return {@link HystrixCollapserProperties} instance */ - public static HystrixCollapserProperties getCollapserProperties(HystrixPropertiesStrategy hystrixPropertiesStrategy, HystrixCollapserKey key, HystrixCollapserProperties.Setter builder) { - if (hystrixPropertiesStrategy == null) { - hystrixPropertiesStrategy = HystrixPropertiesStrategyDefault.getInstance(); - } + public static HystrixCollapserProperties getCollapserProperties(HystrixCollapserKey key, HystrixCollapserProperties.Setter builder) { + HystrixPropertiesStrategy hystrixPropertiesStrategy = HystrixPlugins.getInstance().getPropertiesStrategy(); String cacheKey = hystrixPropertiesStrategy.getCollapserPropertiesCacheKey(key, builder); if (cacheKey != null) { HystrixCollapserProperties properties = collapserProperties.get(cacheKey); From 911e6005e7c18c36a99a82d8a95a8c7773988cdb Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 4 Dec 2012 15:56:52 -0800 Subject: [PATCH 5/7] event-stream functional for Command and ThreadPool metrics This implementation works without Servo or any other metrics publisher. It goes direct against the in-memory Hystrix data structures. --- .../hystrix-metrics-event-stream/build.gradle | 1 + .../eventstream/HystrixMetricsPoller.java | 258 ++++++++++ .../HystrixMetricsStreamServlet.java | 143 ++++++ .../SynchronizedHttpServletResponse.java | 460 ++++++++++++++++++ .../HystrixMetricsStreamServlet.java | 2 +- .../hystrix/HystrixCircuitBreaker.java | 13 +- .../hystrix/HystrixCommandMetrics.java | 40 ++ .../netflix/hystrix/HystrixThreadPool.java | 2 +- .../hystrix/HystrixThreadPoolMetrics.java | 77 ++- 9 files changed, 992 insertions(+), 4 deletions(-) create mode 100644 hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java create mode 100644 hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsStreamServlet.java create mode 100644 hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/SynchronizedHttpServletResponse.java diff --git a/hystrix-contrib/hystrix-metrics-event-stream/build.gradle b/hystrix-contrib/hystrix-metrics-event-stream/build.gradle index 0364cb354..9c4e62078 100644 --- a/hystrix-contrib/hystrix-metrics-event-stream/build.gradle +++ b/hystrix-contrib/hystrix-metrics-event-stream/build.gradle @@ -2,4 +2,5 @@ dependencies { compile project(':hystrix-core') compile 'javax.servlet:javax.servlet-api:3.0.1' + compile 'org.json:json:20090211' } 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 new file mode 100644 index 000000000..731629e5a --- /dev/null +++ b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsPoller.java @@ -0,0 +1,258 @@ +/** + * Copyright 2012 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.contrib.metrics.eventstream; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.concurrent.ThreadFactory; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.servlet.http.HttpServletResponse; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.hystrix.HystrixCircuitBreaker; +import com.netflix.hystrix.HystrixCommandKey; +import com.netflix.hystrix.HystrixCommandMetrics; +import com.netflix.hystrix.HystrixCommandMetrics.HealthCounts; +import com.netflix.hystrix.HystrixCommandProperties; +import com.netflix.hystrix.HystrixThreadPoolKey; +import com.netflix.hystrix.HystrixThreadPoolMetrics; +import com.netflix.hystrix.util.HystrixRollingNumberEvent; + +/** + * Polls Hystrix metrics and writes them to an HttpServletResponse. + */ +public class HystrixMetricsPoller { + + static final Logger logger = LoggerFactory.getLogger(HystrixMetricsPoller.class); + private final ScheduledExecutorService executor; + private final int delay; + private final AtomicBoolean running = new AtomicBoolean(true); + private final int BATCH_SIZE = 10;// how many event before flushing + + public HystrixMetricsPoller(int delay) { + executor = new ScheduledThreadPoolExecutor(1, new MetricsPollerThreadFactory()); + this.delay = delay; + } + + public synchronized void start(HttpServletResponse httpResponse) { + logger.info("Starting HystrixMetricsPoller"); + executor.scheduleWithFixedDelay(new MetricsPoller(httpResponse), 0, delay, TimeUnit.MILLISECONDS); + } + + public synchronized void stop() { + logger.info("Stopping the Servo Metrics Poller"); + running.set(false); + executor.shutdownNow(); + } + + public boolean isRunning() { + return running.get(); + } + + private class MetricsPoller implements Runnable { + + private final HttpServletResponse httpResponse; + + public MetricsPoller(HttpServletResponse httpResponse) { + this.httpResponse = httpResponse; + } + + @Override + public void run() { + try { + int flushCount = 0; // use to flush batches of data rather than all at once or one at a time + + // command metrics + for (HystrixCommandMetrics commandMetrics : HystrixCommandMetrics.getInstances()) { + flushCount++; + HystrixCommandKey key = commandMetrics.getCommandKey(); + HystrixCircuitBreaker circuitBreaker = HystrixCircuitBreaker.Factory.getInstance(key); + + JSONObject json = new JSONObject(); + json.put("type", "HystrixCommand"); + json.put("name", key.name()); + json.put("group", commandMetrics.getCommandGroup().name()); + json.put("currentTime", System.currentTimeMillis()); + + // circuit breaker + json.put("isCircuitBreakerOpen", circuitBreaker.isOpen()); + HealthCounts healthCounts = commandMetrics.getHealthCounts(); + json.put("errorPercentage", healthCounts.getErrorPercentage()); + json.put("errorCount", healthCounts.getErrorCount()); + json.put("requestCount", healthCounts.getTotalRequests()); + + // rolling counters + json.put("rollingCountCollapsedRequests", commandMetrics.getRollingCount(HystrixRollingNumberEvent.COLLAPSED)); + json.put("rollingCountExceptionsThrown", commandMetrics.getRollingCount(HystrixRollingNumberEvent.EXCEPTION_THROWN)); + json.put("rollingCountFailure", commandMetrics.getRollingCount(HystrixRollingNumberEvent.FAILURE)); + json.put("rollingCountFallbackFailure", commandMetrics.getRollingCount(HystrixRollingNumberEvent.FALLBACK_FAILURE)); + json.put("rollingCountFallbackRejection", commandMetrics.getRollingCount(HystrixRollingNumberEvent.FALLBACK_REJECTION)); + json.put("rollingCountFallbackSuccess", commandMetrics.getRollingCount(HystrixRollingNumberEvent.FALLBACK_SUCCESS)); + json.put("rollingCountResponsesFromCache", commandMetrics.getRollingCount(HystrixRollingNumberEvent.RESPONSE_FROM_CACHE)); + json.put("rollingCountSemaphoreRejected", commandMetrics.getRollingCount(HystrixRollingNumberEvent.SEMAPHORE_REJECTED)); + json.put("rollingCountShortCircuited", commandMetrics.getRollingCount(HystrixRollingNumberEvent.SHORT_CIRCUITED)); + json.put("rollingCountSuccess", commandMetrics.getRollingCount(HystrixRollingNumberEvent.SUCCESS)); + json.put("rollingCountThreadPoolRejected", commandMetrics.getRollingCount(HystrixRollingNumberEvent.THREAD_POOL_REJECTED)); + json.put("rollingCountTimeout", commandMetrics.getRollingCount(HystrixRollingNumberEvent.TIMEOUT)); + + json.put("currentConcurrentExecutionCount", commandMetrics.getCurrentConcurrentExecutionCount()); + + // latency percentiles + json.put("latencyExecute_mean", commandMetrics.getExecutionTimeMean()); + JSONArray executionLatency = new JSONArray(); + executionLatency.put(createLatencyTuple(commandMetrics, 0)); + executionLatency.put(createLatencyTuple(commandMetrics, 25)); + executionLatency.put(createLatencyTuple(commandMetrics, 50)); + executionLatency.put(createLatencyTuple(commandMetrics, 75)); + executionLatency.put(createLatencyTuple(commandMetrics, 90)); + executionLatency.put(createLatencyTuple(commandMetrics, 95)); + executionLatency.put(createLatencyTuple(commandMetrics, 99)); + executionLatency.put(createLatencyTuple(commandMetrics, 99.5)); + executionLatency.put(createLatencyTuple(commandMetrics, 100)); + json.put("latencyExecute", executionLatency); + + json.put("latencyTotal_mean", commandMetrics.getTotalTimeMean()); + JSONArray totalLatency = new JSONArray(); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 0)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 25)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 50)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 75)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 90)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 95)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 99)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 99.5)); + totalLatency.put(createTotalLatencyTuple(commandMetrics, 100)); + json.put("latencyTotal", totalLatency); + + // property values for reporting what is actually seen by the command rather than what was set somewhere + HystrixCommandProperties commandProperties = commandMetrics.getProperties(); + + json.put("propertyValue_circuitBreakerRequestVolumeThreshold", commandProperties.circuitBreakerRequestVolumeThreshold().get()); + json.put("propertyValue_circuitBreakerSleepWindowInMilliseconds", commandProperties.circuitBreakerSleepWindowInMilliseconds().get()); + json.put("propertyValue_circuitBreakerErrorThresholdPercentage", commandProperties.circuitBreakerErrorThresholdPercentage().get()); + json.put("propertyValue_circuitBreakerForceOpen", commandProperties.circuitBreakerForceOpen().get()); + json.put("propertyValue_circuitBreakerForceClosed", commandProperties.circuitBreakerForceClosed().get()); + json.put("propertyValue_circuitBreakerEnabled", commandProperties.circuitBreakerEnabled().get()); + + json.put("propertyValue_executionIsolationStrategy", commandProperties.executionIsolationStrategy().get().name()); + json.put("propertyValue_executionIsolationThreadTimeoutInMilliseconds", commandProperties.executionIsolationThreadTimeoutInMilliseconds().get()); + json.put("propertyValue_executionIsolationThreadInterruptOnTimeout", commandProperties.executionIsolationThreadInterruptOnTimeout().get()); + json.put("propertyValue_executionIsolationThreadPoolKeyOverride", commandProperties.executionIsolationThreadPoolKeyOverride().get()); + json.put("propertyValue_executionIsolationSemaphoreMaxConcurrentRequests", commandProperties.executionIsolationSemaphoreMaxConcurrentRequests().get()); + json.put("propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests", commandProperties.fallbackIsolationSemaphoreMaxConcurrentRequests().get()); + + /* + * The following are commented out as these rarely change and are verbose for streaming for something people don't change. + * We could perhaps allow a property or request argument to include these. + */ + + // json.put("propertyValue_metricsRollingPercentileEnabled", commandProperties.metricsRollingPercentileEnabled().get()); + // json.put("propertyValue_metricsRollingPercentileBucketSize", commandProperties.metricsRollingPercentileBucketSize().get()); + // json.put("propertyValue_metricsRollingPercentileWindow", commandProperties.metricsRollingPercentileWindow().get()); + // json.put("propertyValue_metricsRollingPercentileWindowBuckets", commandProperties.metricsRollingPercentileWindowBuckets().get()); + // json.put("propertyValue_metricsRollingStatisticalWindowBuckets", commandProperties.metricsRollingStatisticalWindowBuckets().get()); + // json.put("propertyValue_metricsRollingStatisticalWindowInMilliseconds", commandProperties.metricsRollingStatisticalWindowInMilliseconds().get()); + + json.put("propertyValue_requestCacheEnabled", commandProperties.requestCacheEnabled().get()); + json.put("propertyValue_requestLogEnabled", commandProperties.requestLogEnabled().get()); + + // output to stream + httpResponse.getWriter().println("data: " + json.toString() + "\n"); + + // flush a batch if applicable + if (flushCount == BATCH_SIZE) { + flushCount = 0; + httpResponse.flushBuffer(); + } + + } + + // thread pool metrics + for (HystrixThreadPoolMetrics threadPoolMetrics : HystrixThreadPoolMetrics.getInstances()) { + flushCount++; + HystrixThreadPoolKey key = threadPoolMetrics.getThreadPoolKey(); + + JSONObject json = new JSONObject(); + json.put("type", "HystrixThreadPool"); + json.put("name", key.name()); + json.put("currentTime", System.currentTimeMillis()); + + json.put("currentActiveCount", threadPoolMetrics.getCurrentActiveCount()); + json.put("currentCompletedTaskCount", threadPoolMetrics.getCurrentCompletedTaskCount()); + json.put("currentCorePoolSize", threadPoolMetrics.getCurrentCorePoolSize()); + json.put("currentLargestPoolSize", threadPoolMetrics.getCurrentLargestPoolSize()); + json.put("currentMaximumPoolSize", threadPoolMetrics.getCurrentMaximumPoolSize()); + json.put("currentPoolSize", threadPoolMetrics.getCurrentPoolSize()); + json.put("currentQueueSize", threadPoolMetrics.getCurrentQueueSize()); + json.put("currentTaskCount", threadPoolMetrics.getCurrentTaskCount()); + json.put("rollingCountThreadsExecuted", threadPoolMetrics.getRollingCountThreadsExecuted()); + json.put("rollingMaxActiveThreads", threadPoolMetrics.getRollingMaxActiveThreads()); + + // output to stream + httpResponse.getWriter().println("data: " + json.toString() + "\n"); + + // flush a batch if applicable + if (flushCount == BATCH_SIZE) { + flushCount = 0; + httpResponse.flushBuffer(); + } + } + + // flush again at the end for anything not flushed above + httpResponse.flushBuffer(); + } catch (Exception e) { + logger.warn("Failed to stream metrics", e); + // shutdown + stop(); + return; + } + } + + private JSONObject createLatencyTuple(HystrixCommandMetrics commandMetrics, double percentile) throws JSONException { + JSONObject latency = new JSONObject(); + latency.put(String.valueOf(percentile), commandMetrics.getExecutionTimePercentile(percentile)); + return latency; + } + + private JSONObject createTotalLatencyTuple(HystrixCommandMetrics commandMetrics, double percentile) throws JSONException { + JSONObject latency = new JSONObject(); + latency.put(String.valueOf(percentile), commandMetrics.getTotalTimePercentile(percentile)); + return latency; + } + + } + + private class MetricsPollerThreadFactory implements ThreadFactory { + private static final String MetricsThreadName = "ServoMetricPoller"; + + private final ThreadFactory defaultFactory = Executors.defaultThreadFactory(); + + public Thread newThread(Runnable r) { + Thread thread = defaultFactory.newThread(r); + thread.setName(MetricsThreadName); + return thread; + } + } + +} diff --git a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsStreamServlet.java b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsStreamServlet.java new file mode 100644 index 000000000..0ed4dbef5 --- /dev/null +++ b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/HystrixMetricsStreamServlet.java @@ -0,0 +1,143 @@ +/** + * Copyright 2012 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.contrib.metrics.eventstream; + +import java.io.IOException; +import java.util.concurrent.atomic.AtomicInteger; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.netflix.config.DynamicIntProperty; +import com.netflix.config.DynamicPropertyFactory; + +/** + * Streams Hystrix metrics in text/event-stream format. + *

+ * Install by: + *

+ * 1) Including hystrix-metrics-event-stream-*.jar in your classpath. + *

+ * 2) Adding the following to web.xml: + *

{@code
+ * 
+ *  
+ *  HystrixMetricsStreamServlet
+ *  HystrixMetricsStreamServlet
+ *  com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet
+ * 
+ * 
+ *  HystrixMetricsStreamServlet
+ *  /hystrix.stream
+ * 
+ * } 
+ */ +public class HystrixMetricsStreamServlet extends HttpServlet { + + private static final long serialVersionUID = -7548505095303313237L; + + private static final Logger logger = LoggerFactory.getLogger(HystrixMetricsStreamServlet.class); + + /* used to track number of connections and throttle */ + private static AtomicInteger concurrentConnections = new AtomicInteger(0); + private static DynamicIntProperty maxConcurrentConnections = DynamicPropertyFactory.getInstance().getIntProperty("hystrix.stream.maxConcurrentConnections", 5); + + /** + * Handle incoming GETs + */ + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + handleRequest(request, response); + } + + /** + * - maintain an open connection with the client + * - on initial connection send latest data of each requested event type + * - subsequently send all changes for each requested event type + * + * @param request + * @param response + * @throws javax.servlet.ServletException + * @throws java.io.IOException + */ + private void handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + /* wrap so we synchronize writes since the response object will be shared across multiple threads for async writing */ + response = new SynchronizedHttpServletResponse(response); + + /* ensure we aren't allowing more connections than we want */ + int numberConnections = concurrentConnections.incrementAndGet(); + + int delay = 500; + try { + String d = request.getParameter("delay"); + if (d != null) { + delay = Integer.parseInt(d); + } + } catch (Exception e) { + // ignore if it's not a number + } + + HystrixMetricsPoller poller = null; + try { + if (numberConnections > maxConcurrentConnections.get()) { + response.sendError(503, "MaxConcurrentConnections reached: " + maxConcurrentConnections.get()); + } else { + + /* initialize response */ + response.setHeader("Content-Type", "text/event-stream"); + response.setHeader("Cache-Control", "no-cache, no-store, max-age=0, must-revalidate"); + response.setHeader("Pragma", "no-cache"); + + poller = new HystrixMetricsPoller(delay); + // start polling and it will write directly to the output stream + poller.start(response); + logger.info("Starting poller"); + + try { + while (poller.isRunning()) { + /* + * The 'ping' ensures the client is still connected by writing to it. + * + * It will receive an exception if the client is disconnected and then shut down the poller. + * + * Without this we are vulnerable to permanently holding a connection open even if the client has disconnected if the + * poller is not actually finding data and not trying to write to the stream. + */ + response.getWriter().println(":ping\n"); + response.flushBuffer(); + Thread.sleep(2000); + } + } catch (Exception e) { + // do nothing on interruptions. + logger.error("Failed to write", e); + } + logger.error("Stopping Turbine stream to connection"); + } + } catch (Exception e) { + logger.error("Error initializing servlet for Servo event stream.", e); + } finally { + concurrentConnections.decrementAndGet(); + if (poller != null) { + poller.stop(); + } + } + } +} diff --git a/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/SynchronizedHttpServletResponse.java b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/SynchronizedHttpServletResponse.java new file mode 100644 index 000000000..5918bd084 --- /dev/null +++ b/hystrix-contrib/hystrix-metrics-event-stream/src/main/java/com/netflix/hystrix/contrib/metrics/eventstream/SynchronizedHttpServletResponse.java @@ -0,0 +1,460 @@ +/** + * Copyright 2012 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.contrib.metrics.eventstream; + +import java.io.IOException; +import java.io.PrintWriter; +import java.util.Collection; +import java.util.Locale; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletResponse; + +/** + * Thread-safe HttpResponse wrapper to allow multi-threaded services (such as progressive, asynchronous rendering) + * to have multiple threads writing to the stream. + */ +public class SynchronizedHttpServletResponse implements HttpServletResponse { + + private final HttpServletResponse actualResponse; + private SynchronizedOutputStream outputStream; + private SynchronizedPrintWriter writer; + + public SynchronizedHttpServletResponse(HttpServletResponse response) throws IOException { + this.actualResponse = response; + } + + public synchronized void addCookie(Cookie arg0) { + actualResponse.addCookie(arg0); + } + + public synchronized void addDateHeader(String arg0, long arg1) { + actualResponse.addDateHeader(arg0, arg1); + } + + public synchronized void addHeader(String arg0, String arg1) { + actualResponse.addHeader(arg0, arg1); + } + + public synchronized void addIntHeader(String arg0, int arg1) { + actualResponse.addIntHeader(arg0, arg1); + } + + public synchronized boolean containsHeader(String arg0) { + return actualResponse.containsHeader(arg0); + } + + public synchronized String encodeRedirectURL(String arg0) { + return actualResponse.encodeRedirectURL(arg0); + } + + @SuppressWarnings("deprecation") + public synchronized String encodeRedirectUrl(String arg0) { + return actualResponse.encodeRedirectUrl(arg0); + } + + public synchronized String encodeURL(String arg0) { + return actualResponse.encodeURL(arg0); + } + + @SuppressWarnings("deprecation") + public synchronized String encodeUrl(String arg0) { + return actualResponse.encodeUrl(arg0); + } + + public synchronized void flushBuffer() throws IOException { + actualResponse.flushBuffer(); + } + + public synchronized int getBufferSize() { + return actualResponse.getBufferSize(); + } + + public synchronized String getCharacterEncoding() { + return actualResponse.getCharacterEncoding(); + } + + public synchronized Locale getLocale() { + return actualResponse.getLocale(); + } + + public synchronized ServletOutputStream getOutputStream() throws IOException { + if (outputStream == null) { + outputStream = new SynchronizedOutputStream(actualResponse.getOutputStream()); + } + return outputStream; + } + + public synchronized PrintWriter getWriter() throws IOException { + if (writer == null) { + writer = new SynchronizedPrintWriter(actualResponse.getWriter()); + } + return writer; + } + + public synchronized boolean isCommitted() { + return actualResponse.isCommitted(); + } + + public synchronized void reset() { + actualResponse.reset(); + } + + public synchronized void resetBuffer() { + actualResponse.resetBuffer(); + } + + public synchronized void sendError(int arg0, String arg1) throws IOException { + actualResponse.sendError(arg0, arg1); + } + + public synchronized void sendError(int arg0) throws IOException { + actualResponse.sendError(arg0); + } + + public synchronized void sendRedirect(String arg0) throws IOException { + actualResponse.sendRedirect(arg0); + } + + public synchronized void setBufferSize(int arg0) { + actualResponse.setBufferSize(arg0); + } + + public synchronized void setContentLength(int arg0) { + actualResponse.setContentLength(arg0); + } + + public synchronized void setContentType(String arg0) { + actualResponse.setContentType(arg0); + } + + public synchronized void setDateHeader(String arg0, long arg1) { + actualResponse.setDateHeader(arg0, arg1); + } + + public synchronized void setHeader(String arg0, String arg1) { + actualResponse.setHeader(arg0, arg1); + } + + public synchronized void setIntHeader(String arg0, int arg1) { + actualResponse.setIntHeader(arg0, arg1); + } + + public synchronized void setLocale(Locale arg0) { + actualResponse.setLocale(arg0); + } + + @SuppressWarnings("deprecation") + public synchronized void setStatus(int arg0, String arg1) { + actualResponse.setStatus(arg0, arg1); + } + + public synchronized void setStatus(int arg0) { + actualResponse.setStatus(arg0); + } + + @Override + public synchronized String getContentType() { + return actualResponse.getContentType(); + } + + @Override + public synchronized void setCharacterEncoding(String arg0) { + actualResponse.setCharacterEncoding(arg0); + } + + @Override + public synchronized int getStatus() { + return actualResponse.getStatus(); + } + + @Override + public synchronized String getHeader(String name) { + return actualResponse.getHeader(name); + } + + @Override + public synchronized Collection getHeaders(String name) { + return actualResponse.getHeaders(name); + } + + @Override + public synchronized Collection getHeaderNames() { + return actualResponse.getHeaderNames(); + } + + private static class SynchronizedOutputStream extends ServletOutputStream { + + private final ServletOutputStream actual; + + public SynchronizedOutputStream(ServletOutputStream actual) { + this.actual = actual; + } + + public synchronized void close() throws IOException { + actual.close(); + } + + public synchronized boolean equals(Object obj) { + return actual.equals(obj); + } + + public synchronized void flush() throws IOException { + actual.flush(); + } + + public synchronized int hashCode() { + return actual.hashCode(); + } + + public synchronized void print(boolean b) throws IOException { + actual.print(b); + } + + public synchronized void print(char c) throws IOException { + actual.print(c); + } + + public synchronized void print(double d) throws IOException { + actual.print(d); + } + + public synchronized void print(float f) throws IOException { + actual.print(f); + } + + public synchronized void print(int i) throws IOException { + actual.print(i); + } + + public synchronized void print(long l) throws IOException { + actual.print(l); + } + + public synchronized void print(String s) throws IOException { + actual.print(s); + } + + public synchronized void println() throws IOException { + actual.println(); + } + + public synchronized void println(boolean b) throws IOException { + actual.println(b); + } + + public synchronized void println(char c) throws IOException { + actual.println(c); + } + + public synchronized void println(double d) throws IOException { + actual.println(d); + } + + public synchronized void println(float f) throws IOException { + actual.println(f); + } + + public synchronized void println(int i) throws IOException { + actual.println(i); + } + + public synchronized void println(long l) throws IOException { + actual.println(l); + } + + public synchronized void println(String s) throws IOException { + actual.println(s); + } + + public synchronized String toString() { + return actual.toString(); + } + + public synchronized void write(byte[] b, int off, int len) throws IOException { + actual.write(b, off, len); + } + + public synchronized void write(byte[] b) throws IOException { + actual.write(b); + } + + public synchronized void write(int b) throws IOException { + actual.write(b); + } + + } + + private static class SynchronizedPrintWriter extends PrintWriter { + private final PrintWriter actual; + + public SynchronizedPrintWriter(PrintWriter actual) { + super(actual); + this.actual = actual; + } + + public PrintWriter append(char c) { + return actual.append(c); + } + + public synchronized PrintWriter append(CharSequence csq, int start, int end) { + return actual.append(csq, start, end); + } + + public synchronized PrintWriter append(CharSequence csq) { + return actual.append(csq); + } + + public synchronized boolean checkError() { + return actual.checkError(); + } + + public synchronized void close() { + actual.close(); + } + + public synchronized boolean equals(Object obj) { + return actual.equals(obj); + } + + public synchronized void flush() { + actual.flush(); + } + + public synchronized PrintWriter format(Locale l, String format, Object... args) { + return actual.format(l, format, args); + } + + public synchronized PrintWriter format(String format, Object... args) { + return actual.format(format, args); + } + + public synchronized int hashCode() { + return actual.hashCode(); + } + + public synchronized void print(boolean b) { + actual.print(b); + } + + public synchronized void print(char c) { + actual.print(c); + } + + public synchronized void print(char[] s) { + actual.print(s); + } + + public synchronized void print(double d) { + actual.print(d); + } + + public synchronized void print(float f) { + actual.print(f); + } + + public synchronized void print(int i) { + actual.print(i); + } + + public synchronized void print(long l) { + actual.print(l); + } + + public synchronized void print(Object obj) { + actual.print(obj); + } + + public synchronized void print(String s) { + actual.print(s); + } + + public synchronized PrintWriter printf(Locale l, String format, Object... args) { + return actual.printf(l, format, args); + } + + public synchronized PrintWriter printf(String format, Object... args) { + return actual.printf(format, args); + } + + public synchronized void println() { + actual.println(); + } + + public synchronized void println(boolean x) { + actual.println(x); + } + + public synchronized void println(char x) { + actual.println(x); + } + + public synchronized void println(char[] x) { + actual.println(x); + } + + public synchronized void println(double x) { + actual.println(x); + } + + public synchronized void println(float x) { + actual.println(x); + } + + public synchronized void println(int x) { + actual.println(x); + } + + public synchronized void println(long x) { + actual.println(x); + } + + public synchronized void println(Object x) { + actual.println(x); + } + + public synchronized void println(String x) { + actual.println(x); + } + + public synchronized String toString() { + return actual.toString(); + } + + public synchronized void write(char[] buf, int off, int len) { + actual.write(buf, off, len); + } + + public synchronized void write(char[] buf) { + actual.write(buf); + } + + public synchronized void write(int c) { + actual.write(c); + } + + public synchronized void write(String s, int off, int len) { + actual.write(s, off, len); + } + + public synchronized void write(String s) { + actual.write(s); + } + + } + +} diff --git a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java index 387ca54c0..f8e6e9f26 100644 --- a/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java +++ b/hystrix-contrib/hystrix-servo-stream/src/main/java/com/netflix/hystrix/contrib/servostream/HystrixMetricsStreamServlet.java @@ -40,7 +40,7 @@ public class HystrixMetricsStreamServlet extends HttpServlet { /* used to track number of connections and throttle */ private static AtomicInteger concurrentConnections = new AtomicInteger(0); - private static DynamicIntProperty maxConcurrentConnections = DynamicPropertyFactory.getInstance().getIntProperty("hystrix.servo.stream.maxConcurrentConnections", 5); + private static DynamicIntProperty maxConcurrentConnections = DynamicPropertyFactory.getInstance().getIntProperty("hystrix.stream.maxConcurrentConnections", 5); /** * Handle incoming GETs diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java index 752519d0a..8df61f29d 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java @@ -102,6 +102,17 @@ public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCo return cbForCommand; } } + + /** + * Get the {@link HystrixCircuitBreaker} instance for a given {@link HystrixCommandKey} or null if none exists. + * + * @param key + * {@link HystrixCommandKey} of {@link HystrixCommand} instance requesting the {@link HystrixCircuitBreaker} + * @return {@link HystrixCircuitBreaker} for {@link HystrixCommandKey} + */ + public static HystrixCircuitBreaker getInstance(HystrixCommandKey key) { + return circuitBreakersByCommand.get(key.name()); + } } /** @@ -174,7 +185,7 @@ public boolean isOpen() { // we're closed, so let's see if errors have made us so we should trip the circuit open HealthCounts health = metrics.getHealthCounts(); - + // check if we are past the statisticalWindowVolumeThreshold if (health.getTotalRequests() < properties.circuitBreakerRequestVolumeThreshold().get()) { // we are not past the minimum volume threshold for the statisticalWindow so we'll return false immediately and not calculate anything diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java index 1d4310f2c..b150374e4 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java @@ -17,6 +17,8 @@ import static org.junit.Assert.*; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicInteger; @@ -93,16 +95,27 @@ public static HystrixCommandMetrics getInstance(HystrixCommandKey key) { return metrics.get(key.name()); } + /** + * All registered instances of {@link HystrixCommandMetrics} + * + * @return {@code Collection} + */ + public static Collection getInstances() { + return Collections.unmodifiableCollection(metrics.values()); + } + private final HystrixCommandProperties properties; private final HystrixRollingNumber counter; private final HystrixRollingPercentile percentileExecution; private final HystrixRollingPercentile percentileTotal; private final HystrixCommandKey key; + private final HystrixCommandGroupKey group; private final AtomicInteger executionSemaphorePermitsInUse = new AtomicInteger(); private final HystrixEventNotifier eventNotifier; /* package */HystrixCommandMetrics(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixThreadPoolKey threadPoolKey, HystrixCommandProperties properties, HystrixEventNotifier eventNotifier) { this.key = key; + this.group = commandGroup; this.properties = properties; this.counter = new HystrixRollingNumber(properties.metricsRollingStatisticalWindowInMilliseconds(), properties.metricsRollingStatisticalWindowBuckets()); this.percentileExecution = new HystrixRollingPercentile(properties.metricsRollingPercentileWindow(), properties.metricsRollingPercentileWindowBuckets(), properties.metricsRollingPercentileBucketSize()); @@ -110,6 +123,33 @@ public static HystrixCommandMetrics getInstance(HystrixCommandKey key) { this.eventNotifier = eventNotifier; } + /** + * {@link HystrixCommandKey} these metrics represent. + * + * @return HystrixCommandKey + */ + public HystrixCommandKey getCommandKey() { + return key; + } + + /** + * {@link HystrixCommandGroupKey} of the {@link HystrixCommand} these metrics represent. + * + * @return HystrixCommandGroupKey + */ + public HystrixCommandGroupKey getCommandGroup() { + return group; + } + + /** + * {@link HystrixCommandProperties} of the {@link HystrixCommand} these metrics represent. + * + * @return HystrixCommandProperties + */ + public HystrixCommandProperties getProperties() { + return properties; + } + /** * Get the cumulative count since the start of the application for the given {@link HystrixRollingNumberEvent}. * diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java index af30f8870..89761e628 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPool.java @@ -130,7 +130,7 @@ public HystrixThreadPoolDefault(HystrixThreadPoolKey threadPoolKey, HystrixThrea HystrixConcurrencyStrategy concurrencyStrategy = HystrixPlugins.getInstance().getConcurrencyStrategy(); this.queue = concurrencyStrategy.getBlockingQueue(properties.maxQueueSize().get()); this.threadPool = concurrencyStrategy.getThreadPool(threadPoolKey, properties.coreSize(), properties.coreSize(), properties.keepAliveTimeMinutes(), TimeUnit.MINUTES, queue); - this.metrics = new HystrixThreadPoolMetrics(threadPoolKey, threadPool, properties); + this.metrics = HystrixThreadPoolMetrics.getInstance(threadPoolKey, threadPool, properties); /* strategy: HystrixMetricsPublisherThreadPool */ HystrixMetricsPublisherFactory.createOrRetrievePublisherForThreadPool(threadPoolKey, this.metrics, this.properties); diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPoolMetrics.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPoolMetrics.java index 4a291b58a..6b84e11e3 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPoolMetrics.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixThreadPoolMetrics.java @@ -15,9 +15,15 @@ */ package com.netflix.hystrix; +import java.util.Collection; +import java.util.Collections; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadPoolExecutor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.netflix.hystrix.util.HystrixRollingNumber; import com.netflix.hystrix.util.HystrixRollingNumberEvent; @@ -26,14 +32,83 @@ */ public class HystrixThreadPoolMetrics { + @SuppressWarnings("unused") + private static final Logger logger = LoggerFactory.getLogger(HystrixThreadPoolMetrics.class); + + // String is HystrixThreadPoolKey.name() (we can't use HystrixThreadPoolKey directly as we can't guarantee it implements hashcode/equals correctly) + private static final ConcurrentHashMap metrics = new ConcurrentHashMap(); + + /** + * Get or create the {@link HystrixThreadPoolMetrics} instance for a given {@link HystrixThreadPoolKey}. + *

+ * This is thread-safe and ensures only 1 {@link HystrixThreadPoolMetrics} per {@link HystrixThreadPoolKey}. + * + * @param key + * {@link HystrixThreadPoolKey} of {@link HystrixThreadPool} instance requesting the {@link HystrixThreadPoolMetrics} + * @param threadPool + * Pass-thru of ThreadPoolExecutor to {@link HystrixThreadPoolMetrics} instance on first time when constructed + * @param properties + * Pass-thru to {@link HystrixThreadPoolMetrics} instance on first time when constructed + * @return {@link HystrixThreadPoolMetrics} + */ + public static HystrixThreadPoolMetrics getInstance(HystrixThreadPoolKey key, ThreadPoolExecutor threadPool, HystrixThreadPoolProperties properties) { + // attempt to retrieve from cache first + HystrixThreadPoolMetrics threadPoolMetrics = metrics.get(key.name()); + if (threadPoolMetrics != null) { + return threadPoolMetrics; + } + // it doesn't exist so we need to create it + threadPoolMetrics = new HystrixThreadPoolMetrics(key, threadPool, properties); + // attempt to store it (race other threads) + HystrixThreadPoolMetrics existing = metrics.putIfAbsent(key.name(), threadPoolMetrics); + if (existing == null) { + // we won the thread-race to store the instance we created + return threadPoolMetrics; + } else { + // we lost so return 'existing' and let the one we created be garbage collected + return existing; + } + } + + /** + * Get the {@link HystrixThreadPoolMetrics} instance for a given {@link HystrixThreadPoolKey} or null if one does not exist. + * + * @param key + * {@link HystrixThreadPoolKey} of {@link HystrixThreadPool} instance requesting the {@link HystrixThreadPoolMetrics} + * @return {@link HystrixThreadPoolMetrics} + */ + public static HystrixThreadPoolMetrics getInstance(HystrixThreadPoolKey key) { + return metrics.get(key.name()); + } + + /** + * All registered instances of {@link HystrixThreadPoolMetrics} + * + * @return {@code Collection} + */ + public static Collection getInstances() { + return Collections.unmodifiableCollection(metrics.values()); + } + + private final HystrixThreadPoolKey threadPoolKey; private final HystrixRollingNumber counter; private final ThreadPoolExecutor threadPool; - /* package */HystrixThreadPoolMetrics(HystrixThreadPoolKey threadPoolKey, ThreadPoolExecutor threadPool, HystrixThreadPoolProperties properties) { + private HystrixThreadPoolMetrics(HystrixThreadPoolKey threadPoolKey, ThreadPoolExecutor threadPool, HystrixThreadPoolProperties properties) { + this.threadPoolKey = threadPoolKey; this.threadPool = threadPool; this.counter = new HystrixRollingNumber(properties.metricsRollingStatisticalWindowInMilliseconds(), properties.metricsRollingStatisticalWindowBuckets()); } + /** + * {@link HystrixThreadPoolKey} these metrics represent. + * + * @return HystrixThreadPoolKey + */ + public HystrixThreadPoolKey getThreadPoolKey() { + return threadPoolKey; + } + /** * Value from {@link ThreadPoolExecutor#getActiveCount()} * From 966adbc8c959e08830ef2ac17e3f38c6900e79bc Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 4 Dec 2012 22:04:08 -0800 Subject: [PATCH 6/7] Removing a method that wasn't working and shouldn't be there. --- .../java/com/netflix/hystrix/strategy/HystrixPlugins.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java index 546845dbe..df7fc52b6 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/strategy/HystrixPlugins.java @@ -76,13 +76,6 @@ public void registerEventNotifier(HystrixEventNotifier impl) { this.notifier = impl; } - /** - * Allow resetting all strategies back to defaults. - */ - public void resetToDefaults() { - this.notifier = null; - } - /** * Retrieve instance of {@link HystrixConcurrencyStrategy} to use based on order of precedence as defined in {@link HystrixPlugins} class header. * From 7e65104ab8f0476b83cbcb46602f64e92ad34ddc Mon Sep 17 00:00:00 2001 From: Ben Christensen Date: Tue, 4 Dec 2012 22:04:54 -0800 Subject: [PATCH 7/7] Removing arguments that aren't needed by HystrixCommandMetrics --- .../com/netflix/hystrix/HystrixCircuitBreaker.java | 2 +- .../java/com/netflix/hystrix/HystrixCommand.java | 2 +- .../com/netflix/hystrix/HystrixCommandMetrics.java | 12 +++++------- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java index 8df61f29d..1c563def4 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCircuitBreaker.java @@ -701,7 +701,7 @@ public void testLowVolumeDoesNotTripCircuit() { * Utility method for creating {@link HystrixCommandMetrics} for unit tests. */ private static HystrixCommandMetrics getMetrics(HystrixCommandProperties.Setter properties) { - return new HystrixCommandMetrics(CommandKeyForUnitTest.KEY_ONE, CommandOwnerForUnitTest.OWNER_ONE, ThreadPoolKeyForUnitTest.THREAD_POOL_ONE, HystrixCommandProperties.Setter.asMock(properties), HystrixEventNotifierDefault.getInstance()); + return new HystrixCommandMetrics(CommandKeyForUnitTest.KEY_ONE, CommandOwnerForUnitTest.OWNER_ONE, HystrixCommandProperties.Setter.asMock(properties), HystrixEventNotifierDefault.getInstance()); } /** diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java index fb839f149..454b05199 100755 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommand.java @@ -224,7 +224,7 @@ private HystrixCommand(HystrixCommandGroupKey group, HystrixCommandKey key, Hyst if (metrics == null) { // TODO this caches the first time it's loaded and will thus miss changes to threadPoolKey, properties and eventNotifier // We need a better way of handling this now that we have HystrixPlugins - this.metrics = HystrixCommandMetrics.getInstance(this.commandKey, this.commandGroup, this.threadPoolKey, this.properties, this.eventNotifier); + this.metrics = HystrixCommandMetrics.getInstance(this.commandKey, this.commandGroup, this.properties); } else { this.metrics = metrics; } diff --git a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java index b150374e4..db9c976f7 100644 --- a/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java +++ b/hystrix-core/src/main/java/com/netflix/hystrix/HystrixCommandMetrics.java @@ -30,7 +30,7 @@ import com.netflix.hystrix.HystrixCommand.UnitTest.CommandGroupForUnitTest; import com.netflix.hystrix.HystrixCommand.UnitTest.CommandKeyForUnitTest; -import com.netflix.hystrix.HystrixCommand.UnitTest.ThreadPoolKeyForUnitTest; +import com.netflix.hystrix.strategy.HystrixPlugins; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifier; import com.netflix.hystrix.strategy.eventnotifier.HystrixEventNotifierDefault; import com.netflix.hystrix.util.HystrixRollingNumber; @@ -57,22 +57,20 @@ public class HystrixCommandMetrics { * {@link HystrixCommandKey} of {@link HystrixCommand} instance requesting the {@link HystrixCommandMetrics} * @param commandGroup * Pass-thru to {@link HystrixCommandMetrics} instance on first time when constructed - * @param threadPoolKey - * Pass-thru to {@link HystrixCommandMetrics} instance on first time when constructed * @param properties * Pass-thru to {@link HystrixCommandMetrics} instance on first time when constructed * @param eventNotifier * Pass-thru to {@link HystrixCommandMetrics} instance on first time when constructed * @return {@link HystrixCommandMetrics} */ - public static HystrixCommandMetrics getInstance(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixThreadPoolKey threadPoolKey, HystrixCommandProperties properties, HystrixEventNotifier eventNotifier) { + public static HystrixCommandMetrics getInstance(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixCommandProperties properties) { // attempt to retrieve from cache first HystrixCommandMetrics commandMetrics = metrics.get(key.name()); if (commandMetrics != null) { return commandMetrics; } // it doesn't exist so we need to create it - commandMetrics = new HystrixCommandMetrics(key, commandGroup, threadPoolKey, properties, eventNotifier); + commandMetrics = new HystrixCommandMetrics(key, commandGroup, properties, HystrixPlugins.getInstance().getEventNotifier()); // attempt to store it (race other threads) HystrixCommandMetrics existing = metrics.putIfAbsent(key.name(), commandMetrics); if (existing == null) { @@ -113,7 +111,7 @@ public static Collection getInstances() { private final AtomicInteger executionSemaphorePermitsInUse = new AtomicInteger(); private final HystrixEventNotifier eventNotifier; - /* package */HystrixCommandMetrics(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixThreadPoolKey threadPoolKey, HystrixCommandProperties properties, HystrixEventNotifier eventNotifier) { + /* package */HystrixCommandMetrics(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixCommandProperties properties, HystrixEventNotifier eventNotifier) { this.key = key; this.group = commandGroup; this.properties = properties; @@ -500,7 +498,7 @@ public void testGetErrorPercentage() { * Utility method for creating {@link HystrixCommandMetrics} for unit tests. */ private static HystrixCommandMetrics getMetrics(HystrixCommandProperties.Setter properties) { - return new HystrixCommandMetrics(CommandKeyForUnitTest.KEY_ONE, CommandGroupForUnitTest.OWNER_ONE, ThreadPoolKeyForUnitTest.THREAD_POOL_ONE, HystrixCommandProperties.Setter.asMock(properties), HystrixEventNotifierDefault.getInstance()); + return new HystrixCommandMetrics(CommandKeyForUnitTest.KEY_ONE, CommandGroupForUnitTest.OWNER_ONE, HystrixCommandProperties.Setter.asMock(properties), HystrixEventNotifierDefault.getInstance()); } }