All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.netflix.hystrix.strategy.concurrency.HystrixRequestVariableDefault Maven / Gradle / Ivy

There is a newer version: 1.5.18
Show newest version
/**
 * 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.concurrency;

import java.util.concurrent.ConcurrentHashMap;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Default implementation of {@link HystrixRequestVariable}. Similar to {@link ThreadLocal} but scoped at the user request level. Context is managed via {@link HystrixRequestContext}.
 * 

* All statements below assume that child threads are spawned and initialized with the use of {@link HystrixContextCallable} or {@link HystrixContextRunnable} which capture state from a parent thread * and propagate to the child thread. *

* Characteristics that differ from ThreadLocal: *

    *
  • HystrixRequestVariable context must be initialized at the beginning of every request by {@link HystrixRequestContext#initializeContext}
  • *
  • HystrixRequestVariables attached to a thread will be cleared at the end of every user request by {@link HystrixRequestContext#shutdown} which execute {@link #remove} for each * HystrixRequestVariable
  • *
  • HystrixRequestVariables have a {@link #shutdown} lifecycle method that gets called at the end of every user request (invoked when {@link HystrixRequestContext#shutdown} is called) to allow for * resource cleanup.
  • *
  • HystrixRequestVariables are copied (by reference) to child threads via the {@link HystrixRequestContext#getContextForCurrentThread} and {@link HystrixRequestContext#setContextOnCurrentThread} * functionality.
  • *
  • HystrixRequestVariables created on a child thread are available on sibling and parent threads.
  • *
  • HystrixRequestVariables created on a child thread will be cleaned up by the parent thread via the {@link #shutdown} method.
  • *
* *

* Note on thread-safety: By design a HystrixRequestVariables is intended to be accessed by all threads in a user request, thus anything stored in a HystrixRequestVariables must be thread-safe and * plan on being accessed/mutated concurrently. *

* For example, a HashMap would likely not be a good choice for a RequestVariable value, but ConcurrentHashMap would. * * @param * Type to be stored on the HystrixRequestVariable *

* Example 1: {@code HystrixRequestVariable>}

* Example 2: {@code HystrixRequestVariable} * * @ExcludeFromJavadoc * @ThreadSafe */ public class HystrixRequestVariableDefault implements HystrixRequestVariable { static final Logger logger = LoggerFactory.getLogger(HystrixRequestVariableDefault.class); /** * Creates a new HystrixRequestVariable that will exist across all threads * within a {@link HystrixRequestContext} */ public HystrixRequestVariableDefault() { } /** * Get the current value for this variable for the current request context. * * @return the value of the variable for the current request, * or null if no value has been set and there is no initial value */ @SuppressWarnings("unchecked") public T get() { if (HystrixRequestContext.getContextForCurrentThread() == null) { throw new IllegalStateException(HystrixRequestContext.class.getSimpleName() + ".initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used."); } ConcurrentHashMap, LazyInitializer> variableMap = HystrixRequestContext.getContextForCurrentThread().state; // short-circuit the synchronized path below if we already have the value in the ConcurrentHashMap LazyInitializer v = variableMap.get(this); if (v != null) { return (T) v.get(); } /* * Optimistically create a LazyInitializer to put into the ConcurrentHashMap. * * The LazyInitializer will not invoke initialValue() unless the get() method is invoked * so we can optimistically instantiate LazyInitializer and then discard for garbage collection * if the putIfAbsent fails. * * Whichever instance of LazyInitializer succeeds will then have get() invoked which will call * the initialValue() method once-and-only-once. */ LazyInitializer l = new LazyInitializer(this); LazyInitializer existing = variableMap.putIfAbsent(this, l); if (existing == null) { /* * We won the thread-race so can use 'l' that we just created. */ return l.get(); } else { /* * We lost the thread-race so let 'l' be garbage collected and instead return 'existing' */ return (T) existing.get(); } } /** * Computes the initial value of the HystrixRequestVariable in a request. *

* This is called the first time the value of the HystrixRequestVariable is fetched in a request. Override this to provide an initial value for a HystrixRequestVariable on each request on which it * is used. * * The default implementation returns null. * * @return initial value of the HystrixRequestVariable to use for the instance being constructed */ public T initialValue() { return null; } /** * Sets the value of the HystrixRequestVariable for the current request context. *

* Note, if a value already exists, the set will result in overwriting that value. It is up to the caller to ensure the existing value is cleaned up. The {@link #shutdown} method will not be * called * * @param value * the value to set */ public void set(T value) { HystrixRequestContext.getContextForCurrentThread().state.put(this, new LazyInitializer(this, value)); } /** * Removes the value of the HystrixRequestVariable from the current request. *

* This will invoke {@link #shutdown} if implemented. *

* If the value is subsequently fetched in the thread, the {@link #initialValue} method will be called again. */ public void remove() { if (HystrixRequestContext.getContextForCurrentThread() != null) { remove(HystrixRequestContext.getContextForCurrentThread(), this); } } @SuppressWarnings("unchecked") /* package */static void remove(HystrixRequestContext context, HystrixRequestVariableDefault v) { // remove first so no other threads get it LazyInitializer o = context.state.remove(v); if (o != null) { // this thread removed it so let's execute shutdown v.shutdown((T) o.get()); } } /** * Provide life-cycle hook for a HystrixRequestVariable implementation to perform cleanup * before the HystrixRequestVariable is removed from the current thread. *

* This is executed at the end of each user request when {@link HystrixRequestContext#shutdown} is called or whenever {@link #remove} is invoked. *

* By default does nothing. *

* NOTE: Do not call get() from within this method or initialValue() will be invoked again. The current value is passed in as an argument. * * @param value * the value of the HystrixRequestVariable being removed */ public void shutdown(T value) { // do nothing by default } /** * Holder for a value that can be derived from the {@link HystrixRequestVariableDefault#initialValue} method that needs * to be executed once-and-only-once. *

* This class can be instantiated and garbage collected without calling initialValue() as long as the get() method is not invoked and can thus be used with compareAndSet in * ConcurrentHashMap.putIfAbsent and allow "losers" in a thread-race to be discarded. * * @param */ /* package */static final class LazyInitializer { // @GuardedBy("synchronization on get() or construction") private T value; /* * Boolean to ensure only-once initialValue() execution instead of using * a null check in case initialValue() returns null */ // @GuardedBy("synchronization on get() or construction") private boolean initialized = false; private final HystrixRequestVariableDefault rv; private LazyInitializer(HystrixRequestVariableDefault rv) { this.rv = rv; } private LazyInitializer(HystrixRequestVariableDefault rv, T value) { this.rv = rv; this.value = value; this.initialized = true; } public synchronized T get() { if (!initialized) { value = rv.initialValue(); initialized = true; } return value; } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy