ThreadLocal和InheritableThreadLocal深入探究(三)在Spring Cloud Netflix中的应用
ThreadLocal
在 Spring Cloud Netflix
中的实现:
com.netflix.zuul.context.RequestContext
请求上下文包含请求、响应、状态信息和数据, 以便 ZuulFilters 访问和共享。在请求的持续时间内, 请求上下文处于 “线程本地” 的基础上。可以通过设置上下文类替换请求上下文的扩展。这里的大多数方法都是方便包装的方法,请求上下文是并发哈希图的扩展。
The Request Context holds request, response, state information and data for ZuulFilters to access and share The RequestContext lives for the duration of the request and is ThreadLocal.extensions of RequestContext can be substituted by setting the contextClass Most methods here are convenience wrapper methods; the RequestContext is an extension of a ConcurrentHashMap
该类暴露支持了Spring Cloud Netflix
中对ZuulServletFilter
的支持,在如下代码中具体实现:
源码分析
public class RequestContext extends ConcurrentHashMap<String, Object> {
protected static Class<? extends RequestContext> contextClass = RequestContext.class;
private static RequestContext testContext = null;
// 定义ThreadLocal
protected static final ThreadLocal<? extends RequestContext> threadLocal = new ThreadLocal<RequestContext>() {
@Override
protected RequestContext initialValue() {
try {
return contextClass.newInstance();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
};
// 获得请求上下文
public static RequestContext getCurrentContext() {
if (testContext != null) return testContext;
RequestContext context = threadLocal.get();
return context;
}
}
// Filter执行器
public class FilterProcessor {
// 筛选过滤器,并进行路由调用,异常时使用500状态代码抛出 zuulexception
public void postRoute() throws ZuulException {
try {
runFilters("post");
} catch (ZuulException e) {
throw e;
} catch (Throwable e) {
throw new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + e.getClass().getName());
}
}
// 执行所有Filter,包括自定义Filter
public Object runFilters(String sType) throws Throwable {
if (RequestContext.getCurrentContext().debugRouting()) {
Debug.addRoutingDebug("Invoking {" + sType + "} type filters");
}
boolean bResult = false;
List<ZuulFilter> list = FilterLoader.getInstance().getFiltersByType(sType);
if (list != null) {
for (int i = 0; i < list.size(); i++) {
ZuulFilter zuulFilter = list.get(i);
Object result = processZuulFilter(zuulFilter);
if (result != null && result instanceof Boolean) {
bResult |= ((Boolean) result);
}
}
}
return bResult;
}
...
}
// servlet 过滤器中运行 zuul 过滤器。首先调用路由前过滤器,
// 后发布路过滤器。处理了预路由和路由中的异常过滤器, 然后调用异常过滤器
public class ZuulServletFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
try {
init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
try {
preRouting();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
// Only forward onto to the chain if a zuul response is not being sent
if (!RequestContext.getCurrentContext().sendZuulResponse()) {
filterChain.doFilter(servletRequest, servletResponse);
return;
}
try {
routing();
} catch (ZuulException e) {
error(e);
postRouting();
return;
}
try {
postRouting();
} catch (ZuulException e) {
error(e);
return;
}
} catch (Throwable e) {
error(new ZuulException(e, 500, "UNCAUGHT_EXCEPTION_FROM_FILTER_" + e.getClass().getName()));
} finally {
RequestContext.getCurrentContext().unset();
}
}
...
}
总结
经过对
Spring Cloud Netflix
源码的分析,我们得出在spring-cloud-netflix-zuul
中对于ThreadLocal
父子类继承关系的动态设置,以及调用、实现。与Spring
中的区别是在于设计不同,该处的设计只是采用
ThreadLocal
,没有使用InheritableThreadLocal
,所以在子线程中不能获取父请求上下文中的属性值。最后,该设计的对比也表现了
Spring Framework
的优秀之处。
相关文章推荐
ThreadLocal深入理解与内存泄漏分析
https://blog.youkuaiyun.com/shang_xs/article/details/87874477
ThreadLocal和InheritableThreadLocal深入探究(一)源码分析
https://blog.youkuaiyun.com/shang_xs/article/details/87889079
ThreadLocal和InheritableThreadLocal深入探究(二)在Spring中的应用
https://blog.youkuaiyun.com/shang_xs/article/details/87889219