春季会话 - WebSocket

本指南介绍了如何使用 Spring Session 确保 WebSocket 消息保持你的 HttpSession 活跃。spring-doc.cadn.net.cn

Spring Session 的 WebSocket 支持仅与 Spring 的 WebSocket 支持相配合。 具体来说,它无法直接使用 JSR-356,因为 JSR-356 没有拦截 WebSocket 消息的机制。

HttpSession 设置

第一步是将春季会话与HttpSession集成。这些步骤已经在 HttpSession with Redis 指南中列出。spring-doc.cadn.net.cn

请确保你已经将春季会话与HttpSession集成后再继续。spring-doc.cadn.net.cn

Spring配置

在典型的 Spring WebSocket 应用中,你会实现WebSocketMessageBrokerConfigurer. 例如,配置可能如下:spring-doc.cadn.net.cn

@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/messages").withSockJS();
	}

	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {
		registry.enableSimpleBroker("/queue/", "/topic/");
		registry.setApplicationDestinationPrefixes("/app");
	}

}

我们可以更新配置以使用Spring Session的WebSocket支持。 以下示例展示了如何实现:spring-doc.cadn.net.cn

src/main/java/samples/config/WebSocketConfig.java
@Configuration
@EnableScheduling
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractSessionWebSocketMessageBrokerConfigurer<Session> { (1)

	@Override
	protected void configureStompEndpoints(StompEndpointRegistry registry) { (2)
		registry.addEndpoint("/messages").withSockJS();
	}

	@Override
	public void configureMessageBroker(MessageBrokerRegistry registry) {
		registry.enableSimpleBroker("/queue/", "/topic/");
		registry.setApplicationDestinationPrefixes("/app");
	}

}

要连接春季课程的支持,我们只需要改变两点:spring-doc.cadn.net.cn

1 而不是实现WebSocketMessageBrokerConfigurer,我们延展AbstractSessionWebSocketMessageBrokerConfigurer
2 我们会重新命名registerStompEndpoints方法configureStompEndpoints

什么会AbstractSessionWebSocketMessageBrokerConfigurer幕后工作?spring-doc.cadn.net.cn

  • WebSocketConnectHandlerDecoratorFactory作为WebSocketHandlerDecoratorFactoryWebSocketTransportRegistration. 这确保了习惯会话连接事件是包含WebSocketSession. 这WebSocketSession是结束任何在春季会话结束时仍然开放的WebSocket连接所必需的。spring-doc.cadn.net.cn

  • 会话存储器MessageInterceptor作为握手拦截者每一个StompWebSocketEndpointRegistration. 这确保了会期被添加到WebSocket属性中,以便更新最后访问时间。spring-doc.cadn.net.cn

  • 会话存储器MessageInterceptor作为通道拦截者前往我们的入境频道注册. 这确保每次收到入站消息时,我们Spring Session的最后访问时间都会被更新。spring-doc.cadn.net.cn

  • WebSocketRegistryListener作为春豆诞生。 这确保了我们拥有所有会期对应WebSocket连接的ID。 通过维护这种映射,我们可以在春季会话(HttpSession)结束时关闭所有WebSocket连接。spring-doc.cadn.net.cn

Websocket示例应用

Websocket示例应用演示如何使用 WebSockets 的 Spring Session。spring-doc.cadn.net.cn

运行Websocket示例应用

您可以通过获取源代码并调用以下命令来运行示例:spring-doc.cadn.net.cn

$ ./gradlew :spring-session-sample-boot-websocket:bootRun

为了测试会话到期,你可能需要在启动应用程序前添加以下配置属性,将会话到期时间改为1分钟(默认为30分钟):spring-doc.cadn.net.cn

src/main/resources/application.properties
server.servlet.session.timeout=1m # Session timeout. If a duration suffix is not specified, seconds will be used.
为了让样本正常工作,你必须在localhost上安装Redis 2.8+,并用默认端口(6379)运行。 或者,你也可以更新RedisConnection工厂指向一个Redis服务器。 另一个选择是用 Docker 在 localhost 上运行 Redis。 详见 Docker Redis 仓库中的详细说明。

你现在应该可以在localhost:8080/访问该应用了spring-doc.cadn.net.cn

探索Websocket示例应用

现在你可以试试用这个应用了。请用以下信息进行认证:spring-doc.cadn.net.cn

现在点击登录按钮。你现在应该被认证为用户盗窃者。spring-doc.cadn.net.cn

打开一个无痕窗口访问 localhost:8080/spring-doc.cadn.net.cn

系统会提示你登录一个表格。请用以下信息进行认证:spring-doc.cadn.net.cn

现在给Rob发消息给Luke。消息应该会出现。spring-doc.cadn.net.cn

等两分钟,再试着把Rob发消息给Luke。 你可以看到消息已经不再发送了。spring-doc.cadn.net.cn

为什么是两分钟?

春季会话将在60秒后到期,但Redis的通知并不保证会在60秒内出现。 为了确保套接字在合理时间内关闭,Spring Session 每分钟在 00 秒执行一次后台任务,强制清理所有过期的会话。 这意味着你最多需要等待两分钟,WebSocket连接才会关闭。spring-doc.cadn.net.cn

你现在可以尝试访问localhost:8080/,提示你再次认证。 这表明会话确实有效到期。spring-doc.cadn.net.cn

现在重复同样的练习,但不要等两分钟,而是每30秒发送一次用户的信息。 你可以看到这些消息还在不断发送。 尝试访问localhost:8080/,你不会被提示再次认证。 这表明会议得以保持活力。spring-doc.cadn.net.cn

只有用户发送的消息才能保持会话存活。 这是因为只有来自用户的消息才意味着用户活动。 收到的消息并不意味着有活动,因此不会续期会话。