相关文章推荐
酷酷的煎鸡蛋  ·  Golang WebSocket Ping ...·  1 月前    · 
飘逸的荔枝  ·  WebSockets support in ...·  3 周前    · 
彷徨的热带鱼  ·  Visual studio ...·  9 月前    · 
逃跑的键盘  ·  VS2019 mfc ...·  1 年前    · 
俊逸的仙人球  ·  http - How to convert ...·  1 年前    · 

在Tomcat 7升级到8后,使用NIO连接器,WebSocket出现故障,同时在代码中使用ThreadLocal和Spring的TextWebSocketHandler。

1 人关注

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的选择器来管理单线程上的工作负载(我猜是用事件循环,需要确认)。 单个线程(我猜是用一个事件循环,需要确认)。由于我们现在