使用spring boot+WebSocket 实现定时消息推送(基于注解)
使用spring boot+WebSocket 实现定时消息推送(基于注解)
前言
前面文章也有websocket相关的文章,为什么这次又要重新写一篇呢?第一这篇文章需求业务场景有些不同,第二这篇文章websocket基本上完全基于注解操作简单。
其实能实现定时消息推送的技术有很多,Dwr、goeasy、comer4j 、netPush等技术也可以完全实现这个功能.
- DWR之前文档的消息推送也有使用到,但是在实际项目中表现的并不是很好,毕竟技术相对较老,对于一些浏览器版本兼容性不是很好,而且容易出现消息丢失的情况,研究半天源码改动很多无法解决这个问题。
- GoEasy 其实在消息推送方面表现还是比较良好,但是其是收费的,我们项目基本使用开源产品对应收费产品合规性检测肯定无法通过,因此没有考虑。
- 后面两个技术没有具体研究过,目前觉得webSocket可以完美解决我现在业务需求。
websocket简介
websocket协议是在http协议上的一种补充协议,是html5的新特性,是一种持久化的协议。其实websocket和http关系并不是很大,不过都是属于应用层的协议。关于更多概念大家可以参考下面文章讲述的很详细,接下来我们就开始实战。
websocket 定时推送
本教程基于springboot为脚手架,没使用过springboot同学可以看往期文章,或者直接去spring官网拉一个springboot基础项目下来。
加入依赖
在springboot的项目中添加一下webSocket依赖,一般一项新技术的引入在springboot中也只是引用一个此技术starter的依赖,其他配置基本springboot帮我们解决了。
<!-- websocket 依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
配置
新建一个Java配置类,注入ServerEndpointExporter 配置,如果是使用springboot内置的tomcat此配置必须,如果是使用的是外部tomcat容器此步骤请忽略。看spring源码中这样描述,使用此配置可以关闭servlet容器对websocket端点的扫描,这个暂时没有深入研究。
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
核心代码
接下来最核心的类其实就是提供一个前后端交互的类实现消息的接收推送。
- @ServerEndpoint(value = "/wsdemo") 前端通过此URI 和后端交互,建立连接
- @Component 不用说将此类交给spring管理
- @OnOpen websocket建立连接的注解,前端触发上面URI时会进入此注解标注的方法,和之前关于DWR文章中的onpage方法类似
- @OnClose 顾名思义关闭连接,销毁session
- @OnMessage 收到前端传来的消息后执行的方法
@ServerEndpoint(value = "/wsdemo")
@Component
public class MyWebSocket {
/**静态变量,用来记录当前在线连接数。应该把它设计成线程安全的。*/
private static int onlineCount = 0;
/** concurrent包的线程安全Set,用来存放每个客户端对应的MyWebSocket对象。
在外部可以获取此连接的所有websocket对象,并能对其触发消息发送功能,我们的定时发送核心功能的实现在与此变量 */
private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
/**与某个客户端的连接会话,需要通过它来给客户端发送数据*/
private Session session;
/**
* 连接建立成功调用的方法
*
* 类似dwr的onpage方法,参考之前文章中demo有
* */
@OnOpen
public void onOpen(Session session) {
this.session = session;
webSocketSet.add(this); //加入set中
addOnlineCount(); //在线数加1
System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
try {
sendMessage("连接已建立成功.");
} catch (Exception e) {
System.out.println("IO异常");
}
}
/**
* 连接关闭调用的方法
*
* 参考dwrsession摧毁方法
*/
@OnClose
public void onClose() {
webSocketSet.remove(this); //连接关闭后,将此websocket从set中删除
subOnlineCount(); //在线数减1
System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
}
/**
* 收到客户端消息后调用的方法
*
* @param message 客户端发送过来的消息*/
@OnMessage
public void onMessage(String message, Session session) {
System.out.println("来自客户端的消息:" + message);
}
// 错误提示
@OnError
public void onError(Session session, Throwable error) {
System.out.println("发生错误");
error.printStackTrace();
}
// 发送消息,在定时任务中会调用此方法
public void sendMessage(String message) throws IOException {
this.session.getBasicRemote().sendText(message);
}
public static synchronized int getOnlineCount() {
return onlineCount;
}
public static synchronized void addOnlineCount() {
MyWebSocket.onlineCount++;
}
public static synchronized void subOnlineCount() {
MyWebSocket.onlineCount--;
}
public Session getSession() {
return session;
}
public void setSession(Session session)
