SSE长连接( SpringBoot整合SSE(Server-Sent Events)可以实现后端主动向前端推送数据)

Demo代码分享

依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.7</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>com.example</groupId>
    <artifactId>demo</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>demo</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <mybatis-plus.version>3.5.2</mybatis-plus.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!-- test -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>



后端接收sse连接

@Controller
@RequestMapping("/sse")
public class IndexController {
    /**
     * 创建SSE连接
     *
     * @return
     */
    @GetMapping(path = "/connect")
    public SseEmitter sse() {
        SseEmitter sseEmitter = new SseEmitter();

        // 发送一个注释,响应前端请求
        sseEmitter.send(SseEmitter.event().comment("welcome"));
        return sseEmitter;
    }
}


前端浏览器代码

// 连接服务器
var sseSource = new EventSource("http://localhost:8080/sse/connect");

// 连接打开
sseSource.onopen = function () {
    console.log("连接打开");
}

// 连接错误
sseSource.onerror = function (err) {
    console.log("连接错误:", err);
}

// 接收到数据
sseSource.onmessage = function (event) {
    console.log("接收到数据:", event);
    handleReceiveData(JSON.parse(event.data))
}

定义一个返回数据 Message.java

package com.example.demo.entity;

import lombok.Data;

@Data
public class Message {
    private String data;
    private Integer total;
}


定义sse接口 SseService.java

package com.example.demo.service;

import com.example.demo.entity.Message;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

public interface SseService {
    SseEmitter connect(String uuid);

    void sendMessage(Message message);
}


实现sse接口 SseServiceImpl.java

package com.example.demo.service.impl;

import com.example.demo.entity.Message;
import com.example.demo.service.SseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Slf4j
@Service
public class SseServiceImpl implements SseService {
    /**
     * messageId的 SseEmitter对象映射集
     */
    private static Map<String, SseEmitter> sseEmitterMap = new ConcurrentHashMap<>();

    /**
     * 连接sse
     * @param uuid
     * @return
     */
    @Override
    public SseEmitter connect(String uuid) {
        SseEmitter sseEmitter = new SseEmitter();

        // 连接成功需要返回数据,否则会出现待处理状态
        try {
            sseEmitter.send(SseEmitter.event().comment("welcome"));
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 连接断开
        sseEmitter.onCompletion(() -> {
            sseEmitterMap.remove(uuid);
        });

        // 连接超时
        sseEmitter.onTimeout(() -> {
            sseEmitterMap.remove(uuid);
        });

        // 连接报错
        sseEmitter.onError((throwable) -> {
            sseEmitterMap.remove(uuid);
        });

        sseEmitterMap.put(uuid, sseEmitter);

        return sseEmitter;
    }

    /**
     * 发送消息
     * @param message
     */
    @Override
    public void sendMessage(Message message) {
        message.setTotal(sseEmitterMap.size());

        sseEmitterMap.forEach((uuid, sseEmitter) -> {
            try {
                sseEmitter.send(message, MediaType.APPLICATION_JSON);
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }
}


定时任务 SendMessageTask.java

package com.example.demo.task;


import com.example.demo.entity.Message;
import com.example.demo.service.SseService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Scheduled;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;

@Configuration
public class SendMessageTask {
    @Autowired
    private SseService sseService;

    /**
     * 定时执行 秒 分 时 日 月 周
     */
    @Scheduled(cron = "*/5 * * * * *")  // 间隔5秒
    public void sendMessageTask() {
        Message message = new Message();
        DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        message.setData(LocalDateTime.now().format(format));
        sseService.sendMessage(message);
    }
}


前端路由 IndexController.java

package com.example.demo.controller;

import com.example.demo.entity.Message;
import com.example.demo.service.SseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.util.UUID;

@Slf4j
@Controller
@RequestMapping("/sse")
public class IndexController {

    @Autowired
    private SseService sseService;

    /**
     * 首页
     *
     * @return
     */
    @GetMapping("/")
    public String index() {
        return "index";
    }

    /**
     * 创建SSE连接
     *
     * @return
     */
    @GetMapping(path = "/connect", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter sse() {
        String uuid = UUID.randomUUID().toString();
        log.info("新用户连接:{}", uuid);
        return sseService.connect(uuid);
    }

    /**
     * 广播消息
     *
     * @param message
     */
    @PostMapping("/sendMessage")
    @ResponseBody
    public void sendMessage(@RequestBody Message message) {
        sseService.sendMessage(message);
    }
}


这里简单是用Apifox测试了一下的效果
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/364128.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Java编程练习之类的封装2

1.封装一个股票&#xff08;Stock&#xff09;类&#xff0c;大盘名称为上证A股&#xff0c;前一日的收盘点是2844.70点&#xff0c;设置新的当前值如2910.02点&#xff0c;控制台既要显示以上信息&#xff0c;又要显示涨跌幅度以及点数变化的百分比。运行效果如下&#xff1a;…

【2024美国大学生数学建模竞赛】2024美赛C题 问题分析、数学模型、实现代码、完整论文

【2023美国大学生数学建模竞赛】2024美赛C题 问题分析、数学模型、实现代码、完整论文 引言 题目将于2024年2月2日6:00发布。我们团队将会在8点前准时更新问题分析&#xff0c;逐步更新数学模型和实现代码&#xff0c;最后发布完整的论文。 更新进展&#xff1a; &#xff08;…

朋友,我在项目挺好的

2024年&#xff0c;是优橙教育成立第7年。过去6年&#xff0c;我们迎来送往&#xff0c;曾与一届届网优人并肩作战。 今天&#xff0c;我们不问所在何处&#xff1f;只想遥问曾在优橙的每一个你 嘿&#xff01;朋友&#xff0c;最近过得怎么样&#xff1f;现在的你和曾经的自…

IEC 104电力规约详细解读(二) - 总召唤

1功能简述 总召唤功能是在初始化以后进行&#xff0c;或者是定期进行总召唤&#xff0c;以刷新主站的数据库。总召唤时请求子站传送所有的过程的变量实际值。定期进行总召唤的周期的是一个系统参数&#xff0c;可以是15分钟或者更长的时间。 总召唤的内容包括子站的遥信、遥测…

【软件设计师笔记】计算机系统基础知识考点

&#x1f413; 计算机系统组成 计算机系统是由硬件和软件组成的&#xff0c;它们协同工作来运行程序。计算机的基本硬件系统由 运算器、控制器、存储器、输入设备和输出设备5大部件组成。运算器、控制器等部件被集成 在一起统称为中央处理单元&#xff08;Central Processing …

springboot+AOP+自定义注解+RBAC自定义操作权限管理02

springbootAOP自定义注解RBAC自定义操作权限管理02!经过上一次的凑话部署&#xff0c;我们这一次&#xff0c;增加了一个后端管理系统菜单栏的访问权限的数据表。用角色表&#xff0c;和这张菜单栏的数据表进行映射。不同的角色&#xff0c;可以看见不同的菜单栏目。 这个就是菜…

Maya------创建多边形工具

配合导入图像使用 Tab键可以删除一个点&#xff01; 模型不能超过4边面&#xff01;多切割工具进行连接&#xff01; 15.maya常用命令5.创建多边形工具 反转 双显 挤出_哔哩哔哩_bilibili

不做烧钱买卖,2025年实现双百万量产!奇瑞和大卓智能「不客气」了

随着造车从技术思维向用户思维转变&#xff0c;车企的自研边界逐渐清晰。 一方面&#xff0c;前两年各车企围绕高阶智驾制定的目标&#xff0c;基本都没有达到预期。尤其是疯狂烧钱堆料&#xff0c;并没能换来销量暴涨和C端消费者的满意度&#xff0c;也让部分车企也开始反思&…

电子电器架构——车载网关转发buffer心得汇总

电子电器架构——车载网关转发buffer心得汇总 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力…

leetcode-704.二分查找

题目 给定一个 n 个元素有序的&#xff08;升序&#xff09;整型数组 nums 和一个目标值 target &#xff0c;写一个函数搜索 nums 中的 target&#xff0c;如果目标值存在返回下标&#xff0c;否则返回 -1。 示例 1: 输入: nums [-1,0,3,5,9,12], target 9输出: 4 解释: 9 …

Unity使用反向遮罩实现镂空shader

实现步骤&#xff1a; 1&#xff0c;创建两个材质球&#xff0c;遮罩层的属性如下&#xff1a; 被遮罩层的属性如下&#xff1a; 2&#xff0c;使用两张image&#xff0c;遮罩层在父节点&#xff0c;被遮罩层在子节点&#xff0c;然后分别添加材质球与镂空图片 实现效果如下&a…

使用AnimeGAN2将照片动漫化--入门

使用AnimeGAN2将照片动漫化--入门 1. 环境准备2. 使用3. 总结 1. 环境准备 首先下载AnimeGAN2:https://github.com/TachibanaYoshino/AnimeGANv2.git 然后使用conda 创建一个python3.6的环境conda create -n pyt36 python3.6&#xff1a; 创建一个requirements.txt文件&am…

Linux文本三剑客-awk

一、awk的介绍&#xff1a; 1.awk的简介&#xff1a; AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具 可以在无交互的模式下实现复杂的文本操作 相较于sed常作用于一整个行的处理&#xff0c;awk则比较倾向于一行当中分成数个字段来处理&#xff0c;因…

骨传导耳机是什么?使用骨传导耳机可以保护听力吗?

骨传导耳机是一种特殊的蓝牙耳机&#xff0c;通过人体骨骼来传递声音&#xff0c;可以绕过耳道和耳膜直接传达音频到听者的内耳&#xff0c;开放双耳的佩戴方式可以在享受音乐或通话的同时保持对周围环境的感知&#xff0c;这种设计在户外活动或运动等场景下的使用尤为实用&…

Qt关于qss文件的添加使用

把ui设计得更加的养眼&#xff0c;肯定需要对控件的属性进行设置&#xff0c;qt中就是关于qss文件的使用。 那么如何创建和添加qss文件呢 1.新建一个文本文件的txt 2.将文本文件的后缀改为qss&#xff08;类比html&#xff09; 3.放置到项目的资源文件夹下 4.添加资源文件 5.在…

分治 (地毯填补问题)

地毯填补问题 题目描述 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地…

DevOps落地笔记-05|非功能需求:如何有效关注非功能需求

上一讲主要介绍了看板方法以及如何使用看板方法来解决软件研发过程中出现的团队过载、工作不均、任务延期等问题。通过学习前面几个课时介绍的知识&#xff0c;你的团队开始源源不断地交付用户价值。用户对交付的功能非常满意&#xff0c;但等到系统上线后经常出现服务不可用的…

【正点原子STM32】STM32时钟系统(时钟树、时钟源、分频器和倍频系数、锁相环、STM32CubeMX时钟树、系统时钟配置步骤)

一、认识时钟树 1.1、什么是时钟&#xff1f;1.2、认识时钟树&#xff08;F1&#xff09;1.3、认识时钟树&#xff08;F4&#xff09;1.4、认识时钟树&#xff08;F7&#xff09;1.5、认识时钟树&#xff08;H7&#xff09; 二、配置系统时钟 2.1、系统时钟配置步骤2.2、外设…

基于鲸鱼优化的knn分类特征选择算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 鲸鱼优化算法&#xff08;WOA&#xff09; 4.1.1 包围猎物 4.1.2 螺旋式搜索 4.1.3 更新策略 4.2 K近邻&#xff08;KNN&#xff09;分类器 4.3 基于WOA的KNN分类特征选择算法 5.完…

[C#][opencvsharp]winform实现自定义卷积核锐化和USM锐化

【锐化介绍】 图像锐化(image sharpening)是补偿图像的轮廓&#xff0c;增强图像的边缘及灰度跳变的部分&#xff0c;使图像变得清晰&#xff0c;分为空间域处理和频域处理两类。图像锐化是为了突出图像上地物的边缘、轮廓&#xff0c;或某些线性目标要素的特征。这种滤波方法…