案例简介
本人最近由于公司项目需求,有幸接触websocket,公司项目系统消息模板欲实现添加公告时实现用户的定向推送,分立即推送和定时推送,本人实现项目思路是前端页面在登录成功时通过后台redis验证用户身份,以用户id标识安全订阅消息推送接口,在添加系统公告成功时开启定时任务每隔30秒查询未推送的消息实现定向推送,使用Websocket,STOMP技术完成项目需求,期间由于没有heartbeats导致会话可能会过期在服务器端报错。我最终每隔20000ms从浏览器启用STOMP心跳,以保持Spring会话最后访问时间在STOMP心跳上更新而不会丢失会话。服务端我使用AbstractWebSocketMessageBrokerConfigurer作为基础来启用握手前身份认证和心跳设置。初次接触websocket,不足之处,请见解。
关键技术
WebSocket介绍:
很多网站为了实现实时推送技术,所用的技术都是轮询。轮询是在特定的时间间隔(如每1秒),由浏览器对服务器发出HTTP请求,然后由服务器返回最新的数据给客户端。这种传统的模式带来的缺点很明显,即浏览器需要不断的向服务器发出请求,然而HTTP请求包含较多的请求头信息,而其中真正有效的数据只是很小的一部分,显然这样会浪费很多的带宽等资源。在这种情况下,HTML5定义了WebSocket协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。
WebSocket,一种在单个TCP连接上进行全双工通讯的协议,建立在 TCP 协议之上,服务器端的实现比较容易,使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket API中,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。与 HTTP 协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用 HTTP 协议,因此握手时不容易屏蔽,能通过各种 HTTP 代理服务器。同 HTTP 在 TCP 套接字上添加请求-响应模型层一样,STOMP 在 WebSocket 之上提供了一个基于帧的线路格式层,用来定义消息语义
STOMP介绍:
STOMP,面向消息的简单文本协议,WebSocket处在TCP上非常薄的一层,会将字节流转换为文本/二进制消息,因此,对于实际应用来说,WebSocket的通信形式层级过低,因此,可以在 WebSocket 之上使用 STOMP协议,来为浏览器 和 server间的 通信增加适当的消息语义。
SockJS介绍:
SockJS 是 WebSocket 技术的一种模拟。为了应对许多浏览器不支持WebSocket协议的问题,设计了备选SockJs
。
案例环境要求
- 添加pom.xml websocket 支持
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
- 在项目中添加 websocket 的 Spring 配置
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
public static final Logger LOGGER = LoggerFactory.getLogger(WebSocketConfig.class);
/**
* 定义一些消息连接规范(也可不设置)
* @param config
*/
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
//服务端发送消息给客户端的域,多个用逗号隔开
config.enableSimpleBroker("/topic", "/user")
.setTaskScheduler(new DefaultManagedTaskScheduler())
.setHeartbeatValue(new long[]{20000,20000}); //设置心跳
config.setApplicationDestinationPrefixes("/app");
}
/**
* 注册STOMP协议节点并映射url
* @param registry
*/
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notify") //注册一个 /chebaba 的 websocket 节点
.addInterceptors(notifyHandshakeInterceptor()) //添加 websocket握手拦截器
.setHandshakeHandler(notifyDefaultHandshakeHandler()) //添加 websocket握手处理器
.setAllowedOrigins("*")//设置允许可跨域的域名
.withSockJS();//指定使用SockJS协议
}
/**
* 实施定制来断开会话
* @param registration
*/
@Override