Spring整合WebSocket应用示例(上)

网友投稿 238 2023-07-19


Spring整合WebSocket应用示例(上)

以下教程是在参与开发公司的一个crm系统,整理些相关资料,在该系统中有很多消息推送功能,在其中用到了websocket技术。下面整理分享到我们平台供大家参考

1. maven依赖

javax.servlet

javax.servlet-api

3.1.0

com.fasterxml.jackson.core

jackson-core

2.3.0

com.fasterxml.jackson.core

jackson-databind

2.3.0

org.springframework

spring-websocket

4.0.1.RELEASE

org.springframework

spring-messaging

4.0.1.RELEASE

2. spring-servlet的配置

xmlns:context="http://springframework.org/schema/context"

xmlns:mvc="http://springframework.org/schema/mvcKxEOLFcqF"

xmlns:tx="http://springframework.org/schema/tx" xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:websocket="http://springframework.org/schema/websocket"

xsi:schemaLocation="

http://springframework.org/schema/beans

http://springframework.org/schema/beans/spring-beans-3.1.xsd

http://springframework.org/schema/context

http://springframework.org/schema/context/spring-context-3.1.xsd

http://springframework.org/schema/mvc

http://springframework.org/schema/mvc/spring-mvc-3.1.xsd

http://springframework.org/schema/tx

http://springframework.org/schema/tx/spring-tx-3.1.xsd

http://springframework.org/schema/websocket

http://springframework.org/schema/websocket/spring-websocket.xsd">

......

xmlns:context="http://springframework.org/schema/context"

xmlns:mvc="http://springframework.org/schema/mvcKxEOLFcqF"

xmlns:tx="http://springframework.org/schema/tx" xmlns:xsi="http://w3.org/2001/XMLSchema-instance"

xmlns:websocket="http://springframework.org/schema/websocket"

xsi:schemaLocation="

http://springframework.org/schema/beans

http://springframework.org/schema/beans/spring-beans-3.1.xsd

http://springframework.org/schema/context

http://springframework.org/schema/context/spring-context-3.1.xsd

http://springframework.org/schema/mvc

http://springframework.org/schema/mvc/spring-mvc-3.1.xsd

http://springframework.org/schema/tx

http://springframework.org/schema/tx/spring-tx-3.1.xsd

http://springframework.org/schema/websocket

http://springframework.org/schema/websocket/spring-websocket.xsd">

......

其中,path对应的路径就是前段通过ws协议调的接口路径

3. HandshakeInterceptor的实现

package cn.bridgeli.websocket;

import cn.bridgeli.utils.UserManager;

import cn.bridgeli.util.DateUtil;

import cn.bridgeli.sharesession.UserInfo;

import org.apache.commons.lang.StringUtils;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.http.server.ServerHttpRequest;

import org.springframework.http.server.ServerHttpResponse;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

import org.springframework.web.socket.WebSocketHandler;

import org.springframework.web.socket.server.support.HttpSessionHandshakeInterceptor;

import java.util.Date;

import java.util.Map;

/**

* @Description :创建握手(handshake)接口

* @Date : 16-3-3

*/

public class HandshakeInterceptor extends HttpSessionHandshakeInterceptor{

private static final Logger logger = LoggerFactory.getLogger(HandshakeInterceptor.class);

@Override

public boolean beforeHandshake(ServerHttpRequest request,

ServerHttpResponse response, WebSocketHandler wsHandler,

Map attributes) throws Exception {

logger.info("建立握手前...");

ServletRequestAttributes attrs = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

UserInfo currUser = UserManager.getSessionUser(attrs.getRequest());

UserSocketVo userSocketVo = new UserSocketVo();

String email= "";

if(null != currUser){

email = currUser.getEmail();

}

if(StringUtils.isBlank(email)){

email = DateUtil.date2String(new Date());

}

userSocketVo.setUserEmail(email);

attributes.put("SESSION_USER", userSocketVo);

return super.beforeHandshake(request, response, wsHandler, attributes);

}

@Override

public void afterHandshake(ServerHtthttp://pRequest request,

ServerHttpResponse response, WebSocketHandler wsHandler,

Exception ex) {

logger.info("建立握手后...");

super.afterHandshake(request, response, wsHandler, ex);

}

}

因为老夫不是很懂,所以最大限度的保留原代码,这其实就是从单点登录中取出当前登录用户,转成UserSocketVo对象,放到Map中。所以接下来我们看看UserSocketVo对象的定义

4. UserSocketVo的定义

package cn.bridgeli.websocket;

import org.springframework.web.socket.WebSocketSession;

import java.util.Date;

/**

* @Description : 用户socket连接实体

* @Date : 16-3-7

*/

public class UserSocketVo {

private String userEmail; //用户邮箱

private Date connectionTime; //成功连接时间

private Date preRequestTime; //上次请求时间

private Date newRequestTime; //新请求时间

private Date lastSendTime = new Date(); //下架消息最近一次发送时间

private Date lastTaskSendTime = new Date(); //待处理任务最近一次发送时间

private WebSocketSession webSocketSession; //用户对应的wsSession 默认仅缓存一个

// getXX and setXX

}

其中最重要的就是这个WebSocketSession这个属性了,后面我们要用到

5. WebsocketEndPoint的实现

package cn.bridgeli.websocket;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.socket.CloseStatus;

import org.springframework.web.socket.TextMessage;

import org.springframework.web.socket.WebSocketSession;

import org.springframework.web.socket.handler.TextWebSocketHandler;

/**

* @Description : websocket处理类

* @Date : 16-3-3

*/

public class WebsocketEndPoint extends TextWebSocketHandler{

private static final Logger logger = LoggerFactory.getLogger(WebsocketEndPoint.class);

@Autowired

private NewsListenerImpl newsListener;

@Override

protected void handleTextMessage(WebSocketSession session,

TextMessage message) throws Exception {

super.handleTextMessage(session, message);

TextMessage returnMessage = new TextMessage(message.getPayload()+" received at server");

session.sendMessage(returnMessage);

}

/**

* @Description : 建立连接后

* @param session

* @throws Exception

*/

@Override

public void afterConnectionEstablished(WebSocketSession session) throws Exception{

UserSocketVo userSocketVo = (UserSocketVo)session.getAttributes().get("SESSION_USER");

if(null != userSocketVo){

userSocketVo.setWebSocketSession(session);

if(WSSessionLocalCache.exists(userSocketVo.getUserEmail())){

WSSessionLocalCache.remove(userSocketVo.getUserEmail());

}

WSSessionLocalCache.put(userSocketVo.getUserEmail(), userSocketVo);

newsListener.afterConnectionEstablished(userSocketVo.getUserEmail());

}

logger.info("socket成功建立连接...");

super.afterConnectionEstablished(session);

}

@Override

public void afterConnectionClosed(WebSocketSession session,CloseStatus status) throws Exception{

UserSocketVo userSocketVo = (UserSocketVo)session.getAttributes().get("SESSION_USER");

if(null != userSocketVo){

WSSessionLocalCache.remove(userSocketVo.getUserEmail());

}

logger.info("socket成功关闭连接...");

super.afterConnectionClosed(session, status);

}

}

6. WSSessionLocalCache的实现

package cn.bridgeli.websocket;

import java.io.Serializable;

import java.util.ArrayList;

import java.util.HashMap;

import java.util.List;

import java.util.Map;

/**

* @Description :本地缓存WebSocketSession实例

* @Date : 16-3-7

*/

public class WSSessionLocalCache implements Serializable {

private static Map wsSessionCache = new HashMap<>();

public static boolean exists(String userEmail){

if(!wsSessionCache.containsKey(userEmail)){

return false;

}else{

return true;

}

}

public static void put(String userEmail, UserSocketVo UserSocketVo){

wsSessionCache.put(userEmail, UserSocketVo);

}

public static UserSocketVo get(String userEmail){

return wsSehttp://ssionCache.get(userEmail);

}

public static void remove(String userEmail){

wsSessionCache.remove(userEmail);

}

public static List getAllSessions(){

return new ArrayList<>(wsSessionCache.values());

}

}

看了其实现,作用就比较明显了吧,存放每个UserSocketVo的最新数据,其实到这里我们websocket的实现已经算完了,但还有一个核心类(关于业务逻辑查理的类)没有实现,下篇Spring整合WebSocket应用示例(下),我们就看怎么实现这个类。

WebSocket协议介绍

WebSocket协议是RFC-6455规范定义的一个Web领域的重要的功能:全双工,即客户端和服务器之间的双向通信。它是一个令人兴奋的功能,业界在此领域上已经探索很久,使用的技术包括Java Applet、XMLHttpRequest、Adobe Flash、ActiveXObject、各种Comet技术、服务器端的发送事件等。

需要理解一点,在使用WebSocket协议前,需要先使用HTTP协议用于构建最初的握手。这依赖于一个机制——建立HTTP,请求协议升级(或叫协议转换)。当服务器同意后,它会响应HTTP状态码101,表示同意切换协议。假设通过TCP套接字成功握手,HTTP协议升级请求通过,那么客户端和服务器端都可以彼此互发消息。

Spring框架4.0以上版本引入了一个新模块,即spring-websocket模块。它对WebSocket通信提供了支持。它兼容Java WebSocket API规范jsR-356,同时提供了额外的功能。

什么场景下该使用WebSocket

在Web应用中,客户端和服务器端需要以较高频率和较低延迟来交换事件时,适合用WebSocket。因此WebSocket适合财经、游戏、协作等应用场景。

对于其他应用场景则未必适合。例如,某个新闻订阅需要显示突发新闻,使用间隔几分钟的长轮询也是可以的,这里的延迟可以接受。

即使在要求低延迟的应用场景,如果传输的消息数很低(比如监测网络故障的场景),那么应该考虑使用长轮询技术。

而只有在低延迟和高频消息通信的场景下,选用WebSocket协议才是非常适合的。即使是这样的应用场景,仍然存在是选择WebSocket通信呢?又或者是选择REST HTTP通信呢?

答案是会根据应用程序的需求而定。但是,也可能同时使用这两种技术,把需要频繁交换的数据放到WebSocket中实现,而把REST API作为过程性的业务的实现技术。另外,当REST API的调用中需要把某个信息广播给多个客户端是,也可以通过WebSocket连接来实现。

Spring框架提供了@Controller注释和@RestController注释,两者都可以用于HTTP请求的处理以及WebSocket消息的处理。另外,Spring MVC的请求处理方法,或其它应用程序的请求处理方法,都可以很容易地使用WebSocket协议来广播消息到所有感兴趣的客户端或指定用户。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:Java编写计算器的常见方法实例总结
下一篇:struts2过滤器和拦截器的区别分析
相关文章

 发表评论

暂时没有评论,来抢沙发吧~