Websocket连接在Spring的WebSocket 4.1.6的
上工作。对于连接的建立,我们有
TextWebSocketHandler
,它在其
HandshakeInterceptor
方法中设置了用户上下文。
beforeHandshake()
AuthenticatedUser authUser = (AuthenticatedUser) httpServletRequest
.getSession().getAttribute(WEB_SOCKET_USER);
UserContextHolder.setUserContext(new UserContext(authUser));
UserContextHolder
类的模型类似于Spring的org.springframework.context.i18n.LocaleContextHolder
--一个用于在线程本地存储中保存当前UserContext
的类。
private static ThreadLocal<UserContext> userContextHolder = new InheritableThreadLocal<UserContext>();
public static void setUserContext(UserContext userContext) {
userContextHolder.set(userContext);
if (userContext != null) {
LocaleContextHolder.setLocaleContext(userContext);
} else {
LocaleContextHolder.setLocaleContext(new LocaleContext() {
public Locale getLocale() {
return UserContext.getDefaultLocale();
这个UserContext
为整个应用程序保存线程的认证User
信息,不仅仅是Spring(我们还使用另一个软件,如Quartz,并存于JVM的这个代码库中,所以我们需要在它们之间进行通信)。
当我们在以前的Tomcat 7上运行时,一切都正常,有标准的BIO连接器。问题出现在升级到Tomcat 8时,新的NIO连接器被默认启用。
当WebSocket的消息到达时,它们会在对服务方法的调用中得到处理,该方法在@SecurityValidation
注解中得到验证,MethodInterceptor
会检查给定线程是否设置了用户上下文。
AuthenticatedUser user = UserContextHolder.getUser();
if (user == null) {
throw new Exception(/* ... */);
但它是null
,所以抛出了Exception
。
我们认为,问题出在从BIO到NIO连接器切换后的线程变化。
BIO场景--我们在每个WebSocket模型中都有一个线程,所以一个
Handshake设置一个UserContext
,并且在这个确切的线程上工作。它
即使有更多的套接字,也能正常工作,因为,当我们
有4个不同的WebSockets打开,有4个不同的线程
处理它们,这就是为什么ThreadLocal
的使用效果好。
NIO方案 - 非阻塞性IO概念是为了减少线程数
数量(为我们的案例简化),在第三方NIO内部
连接器中使用NIO的选择器来管理单线程上的工作负载(我猜是用事件循环,需要确认)。
单个线程(我猜是用一个事件循环,需要确认)。由于我们现在