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.
/** * 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.io.Closeable; import java.util.concurrent.ConcurrentHashMap; import com.netflix.hystrix.HystrixCollapser; import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixRequestCache; import com.netflix.hystrix.HystrixRequestLog;
Contains the state and manages the lifecycle of HystrixRequestVariableDefault objects that provide request scoped (rather than only thread scoped) variables so that multiple threads within a single request can share state:

If those features are not used then this does not need to be used. If those features are used then this must be initialized or a custom implementation of HystrixRequestVariable must be returned from HystrixConcurrencyStrategy.getRequestVariable.

If HystrixRequestVariableDefault is used (directly or indirectly by above-mentioned features) and this context has not been initialized then an IllegalStateException will be thrown with a message such as:

HystrixRequestContext.initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used.

Example ServletFilter for initializing HystrixRequestContext at the beginning of an HTTP request and shutting down at the end:

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
     HystrixRequestContext context = HystrixRequestContext.initializeContext();
     try {
          chain.doFilter(request, response);
     } finally {
          context.shutdown();
     }
}

You can find an implementation at hystrix-contrib/hystrix-request-servlet on GitHub.

NOTE: If initializeContext() is called then shutdown() must also be called or a memory leak will occur.

/** * Contains the state and manages the lifecycle of {@link HystrixRequestVariableDefault} objects that provide request scoped (rather than only thread scoped) variables so that multiple threads within * a * single request can share state: * <ul> * <li>request scoped caching as in {@link HystrixRequestCache} for de-duping {@link HystrixCommand} executions</li> * <li>request scoped log of all events as in {@link HystrixRequestLog}</li> * <li>automated batching of {@link HystrixCommand} executions within the scope of a request as in {@link HystrixCollapser}</li> * </ul> * <p> * If those features are not used then this does not need to be used. If those features are used then this must be initialized or a custom implementation of {@link HystrixRequestVariable} must be * returned from {@link HystrixConcurrencyStrategy#getRequestVariable}. * <p> * If {@link HystrixRequestVariableDefault} is used (directly or indirectly by above-mentioned features) and this context has not been initialized then an {@link IllegalStateException} will be thrown * with a * message such as: <blockquote> HystrixRequestContext.initializeContext() must be called at the beginning of each request before RequestVariable functionality can be used. </blockquote> * <p> * Example ServletFilter for initializing {@link HystrixRequestContext} at the beginning of an HTTP request and shutting down at the end: * * <blockquote> * * <pre> * public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { * HystrixRequestContext context = HystrixRequestContext.initializeContext(); * try { * chain.doFilter(request, response); * } finally { * context.shutdown(); * } * } * </pre> * * </blockquote> * <p> * You can find an implementation at <a target="_top" href="https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-request-servlet">hystrix-contrib/hystrix-request-servlet</a> on GitHub. * <p> * <b>NOTE:</b> If <code>initializeContext()</code> is called then <code>shutdown()</code> must also be called or a memory leak will occur. */
public class HystrixRequestContext implements Closeable { /* * ThreadLocal on each thread will hold the HystrixRequestVariableState. * * Shutdown will clear the state inside HystrixRequestContext but not nullify the ThreadLocal on all * child threads as these threads will not be known by the parent when cleanupAfterRequest() is called. * * However, the only thing held by those child threads until they are re-used and re-initialized is an empty * HystrixRequestContext object with the ConcurrentHashMap within it nulled out since once it is nullified * from the parent thread it is shared across all child threads. */ private static ThreadLocal<HystrixRequestContext> requestVariables = new ThreadLocal<HystrixRequestContext>(); public static boolean isCurrentThreadInitialized() { HystrixRequestContext context = requestVariables.get(); return context != null && context.state != null; } public static HystrixRequestContext getContextForCurrentThread() { HystrixRequestContext context = requestVariables.get(); if (context != null && context.state != null) { // context.state can be null when context is not null // if a thread is being re-used and held a context previously, the context was shut down // but the thread was not cleared return context; } else { return null; } } public static void setContextOnCurrentThread(HystrixRequestContext state) { requestVariables.set(state); }
Call this at the beginning of each request (from parent thread) to initialize the underlying context so that HystrixRequestVariableDefault can be used on any children threads and be accessible from the parent thread.

NOTE: If this method is called then shutdown() must also be called or a memory leak will occur.

See class header JavaDoc for example Servlet Filter implementation that initializes and shuts down the context.

/** * Call this at the beginning of each request (from parent thread) * to initialize the underlying context so that {@link HystrixRequestVariableDefault} can be used on any children threads and be accessible from * the parent thread. * <p> * <b>NOTE: If this method is called then <code>shutdown()</code> must also be called or a memory leak will occur.</b> * <p> * See class header JavaDoc for example Servlet Filter implementation that initializes and shuts down the context. */
public static HystrixRequestContext initializeContext() { HystrixRequestContext state = new HystrixRequestContext(); requestVariables.set(state); return state; } /* * This ConcurrentHashMap should not be made publicly accessible. It is the state of RequestVariables for a given RequestContext. * * Only HystrixRequestVariable has a reason to be accessing this field. */ /* package */ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>> state = new ConcurrentHashMap<HystrixRequestVariableDefault<?>, HystrixRequestVariableDefault.LazyInitializer<?>>(); // instantiation should occur via static factory methods. private HystrixRequestContext() { }
Shutdown HystrixRequestVariableDefault objects in this context.

NOTE: This must be called if initializeContext() was called or a memory leak will occur.

/** * Shutdown {@link HystrixRequestVariableDefault} objects in this context. * <p> * <b>NOTE: This must be called if <code>initializeContext()</code> was called or a memory leak will occur.</b> */
public void shutdown() { if (state != null) { for (HystrixRequestVariableDefault<?> v : state.keySet()) { // for each RequestVariable we call 'remove' which performs the shutdown logic try { HystrixRequestVariableDefault.remove(this, v); } catch (Throwable t) { HystrixRequestVariableDefault.logger.error("Error in shutdown, will continue with shutdown of other variables", t); } } // null out so it can be garbage collected even if the containing object is still // being held in ThreadLocals on threads that weren't cleaned up state = null; } }
Shutdown HystrixRequestVariableDefault objects in this context.

NOTE: This must be called if initializeContext() was called or a memory leak will occur. This method invokes shutdown()

/** * Shutdown {@link HystrixRequestVariableDefault} objects in this context. * <p> * <b>NOTE: This must be called if <code>initializeContext()</code> was called or a memory leak will occur.</b> * * This method invokes <code>shutdown()</code> */
public void close() { shutdown(); } }