/** * 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 java.util.concurrent.ConcurrentHashMap; import com.netflix.hystrix.HystrixCircuitBreaker; import com.netflix.hystrix.HystrixCollapser; import com.netflix.hystrix.HystrixCollapserKey; import com.netflix.hystrix.HystrixCollapserMetrics; import com.netflix.hystrix.HystrixCollapserProperties; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; import com.netflix.hystrix.HystrixCommandKey; import com.netflix.hystrix.HystrixCommandMetrics; import com.netflix.hystrix.HystrixCommandProperties; import com.netflix.hystrix.HystrixThreadPool; import com.netflix.hystrix.HystrixThreadPoolKey; import com.netflix.hystrix.HystrixThreadPoolMetrics; import com.netflix.hystrix.HystrixThreadPoolProperties; import com.netflix.hystrix.strategy.HystrixPlugins; /** * Factory for retrieving metrics publisher implementations. * <p> * This uses given {@link HystrixMetricsPublisher} implementations to construct publisher instances and caches each instance according to the cache key provided. * * @ExcludeFromJavadoc */ public class HystrixMetricsPublisherFactory { /** * The SINGLETON instance for real use. * <p> * Unit tests will create instance methods for testing and injecting different publishers. */ private static HystrixMetricsPublisherFactory SINGLETON = new HystrixMetricsPublisherFactory(); /** * Get an instance of {@link HystrixMetricsPublisherThreadPool} with the given factory {@link HystrixMetricsPublisher} implementation for each {@link HystrixThreadPool} instance. * * @param threadPoolKey * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForThreadPool} implementation * @param metrics * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForThreadPool} implementation * @param properties * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForThreadPool} implementation * @return {@link HystrixMetricsPublisherThreadPool} instance */ public static HystrixMetricsPublisherThreadPool createOrRetrievePublisherForThreadPool(HystrixThreadPoolKey threadPoolKey, HystrixThreadPoolMetrics metrics, HystrixThreadPoolProperties properties) { return SINGLETON.getPublisherForThreadPool(threadPoolKey, metrics, properties); } /** * Get an instance of {@link HystrixMetricsPublisherCommand} with the given factory {@link HystrixMetricsPublisher} implementation for each {@link HystrixCommand} instance. * * @param commandKey * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @param commandOwner * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @param metrics * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @param circuitBreaker * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @param properties * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCommand} implementation * @return {@link HystrixMetricsPublisherCommand} instance */ public static HystrixMetricsPublisherCommand createOrRetrievePublisherForCommand(HystrixCommandKey commandKey, HystrixCommandGroupKey commandOwner, HystrixCommandMetrics metrics, HystrixCircuitBreaker circuitBreaker, HystrixCommandProperties properties) { return SINGLETON.getPublisherForCommand(commandKey, commandOwner, metrics, circuitBreaker, properties); } /** * Resets the SINGLETON object. * Clears all state from publishers. If new requests come in instances will be recreated. * */ public static void reset() { SINGLETON = new HystrixMetricsPublisherFactory(); SINGLETON.commandPublishers.clear(); SINGLETON.threadPoolPublishers.clear(); SINGLETON.collapserPublishers.clear(); } /* package */ HystrixMetricsPublisherFactory() {} // String is CommandKey.name() (we can't use CommandKey directly as we can't guarantee it implements hashcode/equals correctly) private final ConcurrentHashMap<String, HystrixMetricsPublisherCommand> commandPublishers = new ConcurrentHashMap<String, HystrixMetricsPublisherCommand>(); /* package */ HystrixMetricsPublisherCommand getPublisherForCommand(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; } else { synchronized (this) { HystrixMetricsPublisherCommand existingPublisher = commandPublishers.get(commandKey.name()); if (existingPublisher != null) { return existingPublisher; } else { HystrixMetricsPublisherCommand newPublisher = HystrixPlugins.getInstance().getMetricsPublisher().getMetricsPublisherForCommand(commandKey, commandOwner, metrics, circuitBreaker, properties); commandPublishers.putIfAbsent(commandKey.name(), newPublisher); newPublisher.initialize(); return newPublisher; } } } } // String is ThreadPoolKey.name() (we can't use ThreadPoolKey directly as we can't guarantee it implements hashcode/equals correctly) private final ConcurrentHashMap<String, HystrixMetricsPublisherThreadPool> threadPoolPublishers = new ConcurrentHashMap<String, HystrixMetricsPublisherThreadPool>(); /* package */ HystrixMetricsPublisherThreadPool getPublisherForThreadPool(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().getMetricsPublisherForThreadPool(threadPoolKey, metrics, properties); // attempt to store it (race other threads) HystrixMetricsPublisherThreadPool existing = threadPoolPublishers.putIfAbsent(threadPoolKey.name(), publisher); if (existing == null) { // we won the thread-race to store the instance we created so initialize it publisher.initialize(); // done registering, return instance that got cached return publisher; } else { // we lost so return 'existing' and let the one we created be garbage collected // without calling initialize() on it return existing; } } /** * Get an instance of {@link HystrixMetricsPublisherCollapser} with the given factory {@link HystrixMetricsPublisher} implementation for each {@link HystrixCollapser} instance. * * @param collapserKey * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCollapser} implementation * @param metrics * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCollapser} implementation * @param properties * Pass-thru to {@link HystrixMetricsPublisher#getMetricsPublisherForCollapser} implementation * @return {@link HystrixMetricsPublisherCollapser} instance */ public static HystrixMetricsPublisherCollapser createOrRetrievePublisherForCollapser(HystrixCollapserKey collapserKey, HystrixCollapserMetrics metrics, HystrixCollapserProperties properties) { return SINGLETON.getPublisherForCollapser(collapserKey, metrics, properties); } // String is CollapserKey.name() (we can't use CollapserKey directly as we can't guarantee it implements hashcode/equals correctly) private final ConcurrentHashMap<String, HystrixMetricsPublisherCollapser> collapserPublishers = new ConcurrentHashMap<String, HystrixMetricsPublisherCollapser>(); /* package */ HystrixMetricsPublisherCollapser getPublisherForCollapser(HystrixCollapserKey collapserKey, HystrixCollapserMetrics metrics, HystrixCollapserProperties properties) { // attempt to retrieve from cache first HystrixMetricsPublisherCollapser publisher = collapserPublishers.get(collapserKey.name()); if (publisher != null) { return publisher; } // it doesn't exist so we need to create it publisher = HystrixPlugins.getInstance().getMetricsPublisher().getMetricsPublisherForCollapser(collapserKey, metrics, properties); // attempt to store it (race other threads) HystrixMetricsPublisherCollapser existing = collapserPublishers.putIfAbsent(collapserKey.name(), publisher); if (existing == null) { // we won the thread-race to store the instance we created so initialize it publisher.initialize(); // done registering, return instance that got cached return publisher; } else { // we lost so return 'existing' and let the one we created be garbage collected // without calling initialize() on it return existing; } } }