boot-websocket.md 8.0 KB
Newer Older
茶陵後's avatar
茶陵後 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
# Spring Session - WebSocket

本指南描述了如何使用 Spring Session 来确保 WebSocket 消息使你的 HttpSession 保持活跃。

|   |Spring session 的 WebSocket 支持仅对 Spring 的 WebSocket 支持有效。<br/>具体来说,它不能直接使用[JSR-356](https://www.jcp.org/en/jsr/detail?id=356),因为 JSR-356 没有拦截传入 WebSocket 消息的机制。|
|---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

[Index](../index.html)

## HttpSession 设置

第一步是将 Spring Session 与 HttpSession 集成在一起。这些步骤已经在[HttpSession with Redis 指南](./boot-redis.html)中进行了概述。

在继续之前,请确保你已经将 Spring Session 集成到 HttpSession 中。

##  Spring 配置

在典型的 Spring  WebSocket 应用程序中,你将实现`WebSocketMessageBrokerConfigurer`。例如,配置可能如下所示:

```
@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 支持。下面的示例展示了如何做到这一点:

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 Session 支持,我们只需要更改两件事:

|**1**|而不是实现`WebSocketMessageBrokerConfigurer`,我们扩展`AbstractSessionWebSocketMessageBrokerConfigurer`|
|-----|-----------------------------------------------------------------------------------------------------------------------|
|**2**|我们将`registerStompEndpoints`方法重命名为`configureStompEndpoints`|

`AbstractSessionWebSocketMessageBrokerConfigurer`在幕后做什么?

* `WebSocketConnectHandlerDecoratorFactory`作为`WebSocketHandlerDecoratorFactory`添加到`WebSocketTransportRegistration`。这确保了一个包含`WebSocketSession`的自定义`SessionConnectEvent`被触发。当 Spring Session 结束时,要结束任何仍处于打开状态的 WebSocket 连接,`WebSocketSession`是必需的。

* `SessionRepositoryMessageInterceptor`作为`HandshakeInterceptor`添加到每个`StompWebSocketEndpointRegistration`。这确保将`Session`添加到 WebSocket 属性中,以允许更新上次访问的时间。

* `SessionRepositoryMessageInterceptor`作为`ChannelInterceptor`添加到我们的入站`ChannelRegistration`中。这确保了每次接收入站消息时,都会更新我们 Spring Session 的最后一次访问时间。

* `WebSocketRegistryListener`被创建为 Spring  Bean。这确保了我们将所有`Session`ID 映射到相应的 WebSocket 连接。通过维护此映射,我们可以在 Spring Session 结束时关闭所有 WebSocket 连接。

## `websocket`示例应用程序

`websocket`示例应用程序演示了如何在 WebSockets 中使用 Spring Session 。

### 运行`websocket`示例应用程序

你可以通过获取[源代码](https://github.com/spring-projects/spring-session/archive/main.zip)并调用以下命令来运行示例:

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

|   |为了测试会话过期,你可能希望在启动应用程序之前添加以下配置属性,从而将会话过期时间更改为 1 分钟(默认为 30 分钟):<br/><br/>SRC/main/resources/application.properties<br/><br/>```<br/>server.servlet.session.timeout=1m # Session timeout. If a duration suffix is not specified, seconds will be used.<br/>```|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

|   |要使示例工作,你必须在 localhost 上[安装 Redis2.8+](https://redis.io/download)并使用默认端口(6379)运行它。<br/>或者,你可以更新`RedisConnectionFactory`以指向 Redis 服务器。<br/>另一个选项是使用[Docker](https://www.docker.com/)在 localhost 上运行 Redis。<br/>有关详细说明,请参见[Docker Redis 存储库](https://hub.docker.com/_/redis/)。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

现在你应该可以在[http://localhost:8080/](http://localhost:8080/)上访问应用程序了。

### 探索`websocket`示例应用程序

现在你可以尝试使用该应用程序了。使用以下信息进行身份验证:

* **用户 Name ** *罗布*

* **密码** *密码*

现在点击**登录**按钮。你现在应该被验证为用户**罗布**

打开一个隐身窗口并访问[http://localhost:8080/](http://localhost:8080/)

系统会提示你输入登录表单。使用以下信息进行身份验证:

* **用户 Name ** *卢克*

* **密码** *密码*

现在把罗布的话传给卢克。消息应该会出现。

等两分钟,再试着把罗布的信息发送给卢克。你可以看到该消息已不再发送。

|   |为什么是两分钟?<br/><br/> Spring Session 在 60 秒内到期,但是来自 Redis 的通知不能保证在 60 秒内发生。<br/>以确保套接字在合理的时间内关闭, Spring Session 在 00 秒时每分钟运行一个后台任务,该任务强制清除任何过期的会话。<br/>这意味着在关闭 WebSocket 连接之前,你最多需要等待两分钟。|
|---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|

你现在可以尝试访问[http://localhost:8080/](http://localhost:8080/),提示你再次进行身份验证。这表明会话正确地过期了。

现在重复同样的练习,但不是等待两分钟,而是每 30 秒发送一条来自每个用户的消息。你可以看到消息继续被发送。尝试访问[http://localhost:8080/](http://localhost:8080/),没有提示你再次进行身份验证。这表明会话是保持活力的。

|   |只有来自用户的消息才能使会话保持活跃。<br/>这是因为只有来自用户的消息才意味着用户活动。<br/>收到的消息并不意味着活动,因此不会更新会话过期时间。|
|---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|