使用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 帮我们解决了。

            <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>

效果演示

(完)

推荐阅读

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

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

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

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

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

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

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


好文!必须点赞
  • 2
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
好的,您的第一个问题是如何利用RabbitMQ发送消息通知,使用Spring BootWebSocket实现实时推送在线用户列表和实现实时推送通知信息。 首先,您需要在Spring Boot项目中添加RabbitMQ和WebSocket的依赖。 在使用RabbitMQ发送消息通知的过程中,您可以使用RabbitTemplate类来发送消息。您可以将RabbitTemplate配置为使用WebSocket连接来发送消息。下面是一个简单的示例: ```java @Autowired private RabbitTemplate rabbitTemplate; public void sendNotification(Notification notification) { rabbitTemplate.convertAndSend("notification-exchange", "notification-routing-key", notification); } ``` 这里我们假设您已经创建了名为“notification-exchange”的交换机和名为“notification-routing-key”的路由键。 接下来,您需要使用Spring BootWebSocket实现实时推送在线用户列表和实时推送通知信息。 您可以使用@WebSocketMapping注解来创建WebSocket处理程序,如下所示: ```java @WebSocketMapping("/websocket") public class WebSocketHandler { private final List<WebSocketSession> sessions = new CopyOnWriteArrayList<>(); @Autowired private RabbitTemplate rabbitTemplate; @MessageMapping("/sendNotification") public void sendNotification(Notification notification) { rabbitTemplate.convertAndSend("notification-exchange", "notification-routing-key", notification); } @SubscribeMapping("/onlineUsers") public List<String> getOnlineUsers() { return sessions.stream() .map(WebSocketSession::getId) .collect(Collectors.toList()); } @WebSocketSessionAdded public void onSessionAdded(WebSocketSession session) { sessions.add(session); } @WebSocketSessionRemoved public void onSessionRemoved(WebSocketSession session) { sessions.remove(session); } } ``` 在这个处理程序中,我们使用CopyOnWriteArrayList来存储WebSocket会话,并使用@Autowired注解注入了RabbitTemplate,实现消息推送使用@MessageMapping注解和“/sendNotification”路径的集成使用,来发送消息通知到客户端。 使用@SubscribeMapping注解和“/onlineUsers”路径的集成使用,来实时推送在线用户列表到客户端。 使用@WebSocketSessionAdded注解和@WebSocketSessionRemoved注解,监听用户连接和断开连接事件,并把WebSocket会话添加到或从sessions列表中删除。 最后,您需要在应用程序中启用WebSocket。在Spring Boot应用程序中,您可以使用@EnableWebSocket注解实现这一点,如下所示: ```java @SpringBootApplication @EnableWebSocket public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } } ``` 如此一来,您就可以使用RabbitMQ和WebSocket实现实时推送通知和在线用户列表了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值