websocket 协议是在 http 协议上的一种补充协议,是 html5 的新特性,是一种持久化的协议。其实 websocket 和 http 关系并不是很大,不过都是属于应用层的协议,接下来我们就开始实战。

websocket 定时推送

本教程基于 springboot 为脚手架,没使用过 springboot 同学可以看往期文章,或者直接去 spring 官网拉一个 springboot 基础项目下来。

加入依赖

在 springboot 的项目中添加一下 webSocket 依赖,一般一项新技术的引入在 springboot 中也只是引用一个此技术 starter 的依赖,其他配置基本 springboot 帮我们解决了。

<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;
    private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();
    private Session session;
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this);     
        addOnlineCount();           
        System.out.println("有新连接加入!当前在线人数为" + getOnlineCount());
        try {
            sendMessage("连接已建立成功.");
        } catch (Exception e) {
            System.out.println("IO异常");
        }
    }
    @OnClose
    public void onClose() {
        webSocketSet.remove(this);  
        subOnlineCount();           
        System.out.println("有一连接关闭!当前在线人数为" + getOnlineCount());
    }
    @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) {
        this.session = session;
    }
    public static CopyOnWriteArraySet<MyWebSocket> getWebSocketSet() {
        return webSocketSet;
    }
    public static void setWebSocketSet(CopyOnWriteArraySet<MyWebSocket> webSocketSet) {
        MyWebSocket.webSocketSet = webSocketSet;
    }
}

定时任务

使用 spring 的 Schedule 建立定时任务

  • @EnableScheduling 开启 spring 定时任务功能
  • @Scheduled(cron = "0/10 * * * * ?") 用于标识定时执行的方法,此处主要方法返回值一定是 void,没有入参。对应定时时间配置可以百度 cron 语法,根据自己的业务选择合适的周期
    在这类中,我们通过上面 MyWebSocket 提供的静态方法获取其中的 webSocketSet ,来获取所有此业务相关的所有 websocketsession,可以在定时任务中对 session 内容进行验证判断(权限验证等),进行发送消息
@Component
@EnableScheduling
public class TimeTask
{
    private static Logger logger = LoggerFactory.getLogger(TimeTask.class);
    @Scheduled(cron = "0/1 * * * * ?")   
    public void test(){
        System.err.println("*********   定时任务执行   **************");
        CopyOnWriteArraySet<MyWebSocket> webSocketSet = 
     MyWebSocket.getWebSocketSet();
            int i = 0 ;
        webSocketSet.forEach(c->{
            try {
                c.sendMessage("  定时发送  " + new Date().toLocaleString());
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        System.err.println("/n 定时任务完成.......");
    }
}

前端页面

前端页面可以参考使用,主要要更改调用的 url 为自己项目 URL

<!DOCTYPE HTML>
<html>
<head>
    <title>My WebSocket</title>
</head>
<body>
Welcome<br/>
<input type="text" /><button onclick="send()">Send</button>    <button onclick="closeWebSocket()">Close</button>
<div>
</div>
</body>
<script type="text/javascript">
    var websocket = null;
    //判断当前浏览器是否支持WebSocket  ,主要此处要更换为自己的地址
    if('WebSocket' in window){
        websocket = new WebSocket("ws://localhost:8886/wsdemo");
    }
    else{
        alert('Not support websocket')
    }
    //连接发生错误的回调方法
    websocket.onerror = function(){
        setMessageInnerHTML("error");
    };
    //连接成功建立的回调方法
    websocket.onopen = function(event){
        setMessageInnerHTML("open");
    }
    //接收到消息的回调方法
    websocket.onmessage = function(event){
        setMessageInnerHTML(event.data);
    }
    //连接关闭的回调方法
    websocket.onclose = function(){
        setMessageInnerHTML("close");
    }
    //监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,server端会抛异常。
    window.onbeforeunload = function(){
        websocket.close();
    }
    //将消息显示在网页上
    function setMessageInnerHTML(innerHTML){
        document.getElementById('message').innerHTML += innerHTML + '<br/>';
    }
    //关闭连接
    function closeWebSocket(){
        websocket.close();
    }
    //发送消息
    function send(){
        var message = document.getElementById('text').value;
        websocket.send(message);
    }
</script>
</html>

效果演示

Spring Boot + WebSocket 实时消息推送系统,写得太好了!_定时任务

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!