使用spring boot+WebSocket 实现定时消息推送(基于注解)

  • 作者:清风徐来水波不清

  • https://www.jianshu.com/p/62132f605669

前言

前面文章也有 websocket 相关的文章,为什么这次又要重新写一篇呢?第一这篇文章需求业务场景有些不同,第二这篇文章 websocket 基本上完全基于注解操作简单。
其实能实现定时消息推送的技术有很多,Dwr、goeasy、comer4j 、netPush 等技术也可以完全实现这个功能.

  • DWR 之前文档的消息推送也有使用到,但是在实际项目中表现的并不是很好,毕竟技术相对较老,对于一些浏览器版本兼容性不是很好,而且容易出现消息丢失的情况,研究半天源码改动很多无法解决这个问题。

  • GoEasy 其实在消息推送方面表现还是比较良好,但是其是收费的,我们项目基本使用开源产品对应收费产品合规性检测肯定无法通过,因此没有考虑。

  • 后面两个技术没有具体研究过,目前觉得 webSocket 可以完美解决我现在业务需求。

websocket 简介

websocket 协议是在 http 协议上的一种补充协议,是 html5 的新特性,是一种持久化的协议。其实 websocket 和 http 关系并不是很大,不过都是属于应用层的协议。关于更多概念大家可以参考下面文章讲述的很详细,接下来我们就开始实战。

https://www.cnblogs.com/fuqiang88/p/5956363.html

websocket 定时推送

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

加入依赖

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

            
            org.springframework.boot
            spring-boot-starter-websocket
        

配置

新建一个 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 webSocketSet = new CopyOnWriteArraySet();
    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 getWebSocketSet() {
        return webSocketSet;
    }
    public static void setWebSocketSet(CopyOnWriteArraySet 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 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




    My WebSocket


Welcome

效果演示

使用spring boot+WebSocket 实现定时消息推送(基于注解)_第1张图片

(完)

推荐阅读

IDEA新特性:提前知道代码怎么走!

还在担心写的一手烂SQL,送你4款工

瞬间几千次的重复提交,我用 SpringBoot+Redis 扛住了

爬了知乎“神回复”,笑得根本停不下来

这 17 个 JVM 参数,高级 Java 必须掌握!

HTTP客户端连接,选择HttpClient还是OkHttp?

为什么建议使用你 LocalDateTime ,而不是 Date?


好文!必须点赞

你可能感兴趣的:(使用spring boot+WebSocket 实现定时消息推送(基于注解))