内容简介:最近在进行压测发现,有一些接口时好时坏,通过sentry日志平台及sky walking平台跟踪发现,用户张三获取到的用户上下文确是李四。用户登录下上文在拦截器中有调用UserContext.set恢复用户登录上下文,并在请求结束时调用UserContext.clear清理用户登录上下文。
最近在进行压测发现,有一些接口时好时坏,通过sentry日志平台及sky walking平台跟踪发现,用户张三获取到的用户上下文确是李四。
代码走读
用户登录下上文
/**
* 用户登录下上文
*
* @author : jamesfu
* @date : 22/5/2019
* @time : 9:18 AM
*/
@Data
public class UserContext {
private final static ThreadLocal<UserContext> threadLocal = new ThreadLocal<>();
private Long id;
private String loginName;
public static UserContext get() {
UserContext context = threadLocal.get();
if (context == null) {
// TODO(james.h.fu):根据请求上下文获取token, 然后恢复用户登录下上文
context = new UserContext() {{
setId(1L);
setLoginName("james.h.fu1");
}};
threadLocal.set(context);
}
return context;
}
public static void clear() {
threadLocal.remove();
}
public static void set(UserContext context) {
if (context != null) {
threadLocal.set(context);
}
}
}
复制代码
在拦截器中有调用UserContext.set恢复用户登录上下文,并在请求结束时调用UserContext.clear清理用户登录上下文。
拦截器注册配置
/**
* 拦截器注册配置
*
* @author : jamesfu
* @date : 22/5/2019
* @time : 9:15 AM
*/
@Configuration
public class FilterConfig implements WebMvcConfigurer {
@Autowired
private JsonRpcInterceptor jsonRpcInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jsonRpcInterceptor)
.addPathPatterns("/json.rpc");
}
}
复制代码
通过debug可以发现UserContext中的ThreadLocal的清理工作没有得到执行。导致请求进来时,有可能ThreadLocal已存在了,就不会再根据请求上下文恢复了。
springmvc 源码走读
tomcat 在收到http请求后,最终会交由spring mvc的 DispatcherServlet 处理。 这里可以从doDispatch按图索骥,顺藤摸瓜地往下看起走。
源码走读:DispatcherServlet
/** * Process the actual dispatching to the handler. * <p>The handler will be obtained by applying the servlet's HandlerMappings in order. * The HandlerAdapter will be obtained by querying the servlet's installed HandlerAdapters * to find the first that supports the handler class. * <p>All HTTP methods are handled by this method. It's up to HandlerAdapters or handlers * themselves to decide which methods are acceptable. * @param request current HTTP request * @param response current HTTP response * @throws Exception in case of any kind of processing failure */ protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception 复制代码
请求会得到分发,然后执行各个已注册Handler的preHandle-->postHandle-->afterCompletion。
源码走读:HandlerExecutionChain
applyPreHandle
/**
* Apply preHandle methods of registered interceptors.
* @return {@code true} if the execution chain should proceed with the
* next interceptor or the handler itself. Else, DispatcherServlet assumes
* that this interceptor has already dealt with the response itself.
*/
boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = 0; i < interceptors.length; i++) {
HandlerInterceptor interceptor = interceptors[i];
if (!interceptor.preHandle(request, response, this.handler)) {
triggerAfterCompletion(request, response, null);
return false;
}
this.interceptorIndex = i;
}
}
return true;
}
复制代码
当执行到preHandle返回false时,它就会从上一个返回true的handler依次往前执行afterCompletion,它自己的afterCompletion得不到执行。
triggerAfterCompletion
/**
* Trigger afterCompletion callbacks on the mapped HandlerInterceptors.
* Will just invoke afterCompletion for all interceptors whose preHandle invocation
* has successfully completed and returned true.
*/
void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, @Nullable Exception ex)
throws Exception {
HandlerInterceptor[] interceptors = getInterceptors();
if (!ObjectUtils.isEmpty(interceptors)) {
for (int i = this.interceptorIndex; i >= 0; i--) {
HandlerInterceptor interceptor = interceptors[i];
try {
interceptor.afterCompletion(request, response, this.handler, ex);
}
catch (Throwable ex2) {
logger.error("HandlerInterceptor.afterCompletion threw exception", ex2);
}
}
}
}
复制代码
triggerAfterCompletion只会在(1)出现异常,(2)preHandle返回false 或(3)正常执行结束才会从索引interceptorIndex依次往前执行。
所以基于以上源码可以得知,在写拦截器时preHandle返回false时,afterCompletion是不会执行的。所以一些必要的清理工作得不到执行,会出现类似我们遇到的帐号串的问题。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Paradigms of Artificial Intelligence Programming
Peter Norvig / Morgan Kaufmann / 1991-10-01 / USD 77.95
Paradigms of AI Programming is the first text to teach advanced Common Lisp techniques in the context of building major AI systems. By reconstructing authentic, complex AI programs using state-of-the-......一起来看看 《Paradigms of Artificial Intelligence Programming》 这本书的介绍吧!