使用Server-Sent Events实现后端主动向前端进行通信

目录

概述

[使用 Server-Sent Events (SSE)](#使用 Server-Sent Events (SSE))

示例

1.创建SpringBoot项目添加web依赖,并编写以下代码

2.创建Vue项目并在项目的.vue文件中编写以下代码

效果说明

使用触发的方式向前端传递数据

总结


概述

在典型的前后端分离架构中,前端通过发起 HTTP 请求(例如使用 Axios 或 Fetch API)向后端发送请求,后端处理这些请求并返回响应。这种模式是常见的,并符合常规的网络通信方式。

然而,有时候确实存在一些特殊情况或需求,需要后端通过主动推送的方式与前端进行通信。这通常使用 WebSocket 或 Server-Sent Events (SSE) 技术来实现。WebSocket 通常更适合实时双向通信,而 SSE 更适合单向通知。本文主要使用 Server-Sent Events 实现。

使用 Server-Sent Events (SSE)

Server-Sent Events (SSE) 允许服务器单向推送事件到浏览器。在 Spring Boot 中,使用 SseEmitterResponseBodyEmitter 类来实现 SSE。

示例

1.创建SpringBoot项目添加web依赖,并编写以下代码

创建SseController类,用于传递数据

java 复制代码
import com.hgkx.pojo.MyData;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;

@RestController
@RequestMapping("/sse")
public class SseController {

    private SseEmitter emitter;

    @GetMapping("/events")
    public SseEmitter streamEvents() throws IOException {
        SseEmitter emitter = new SseEmitter();

        emitter.send("向前端传递的数据");

        return emitter;
    }
}

创建GlobalCorsConfig配置类用于解决前后端跨域问题

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

@Configuration
public class GlobalCorsConfig {

    @Bean
    public CorsFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        //允许所有域名进行跨域调用
        config.addAllowedOriginPattern("*");
        // 设置你要允许的网站域名
        //config.addAllowedOrigin("http://localhost:8081");
        //允许跨域发送cookie
        config.setAllowCredentials(true);
        //放行全部原始头信息
        config.addAllowedHeader("*");
        //允许所有请求方法跨域调用
        config.addAllowedMethod("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}

代码流程:

1.创建SseEmitter实例

2.准备向前端发送数据

3.建立与前端的连接

4.解决跨域问题

注意:return emitter; 是将这个 SseEmitter 对象返回给客户端,以维持连接。客户端可以通过这个连接持续接收来自服务器的实时消息。调用 emitter.send("向前端传递的数据"); 时,它才会将指定的数据发送给客户端。也就是说 emitter.send()才是用来发送数据的。

你可以理解为 emitter.send() 是用来主动推送消息给客户端的行为,而 return emitter; 是为了确保 SSE 连接的持续性。客户端可以在整个连接生命周期中通过这个连接接收服务器端发送的实时消息。

2.创建Vue项目并在项目的.vue文件中编写以下代码

使用JavaScript 中的 EventSource API 来接收 SSE 事件

html 复制代码
<template>
  <div>
    <h1>测试页</h1>
    <h3>接收的消息:{{message}}</h3>
  </div>
</template>

<script>
    export default {
        name: "Test",
        data(){
            return{
                eventSource: null,
                message: '',
            }
        },
        methods:{
            //接收后台消息
            receiveMessage(){
                this.eventSource = new EventSource('http://localhost:8080/sse/events');
                //接收成功
                this.eventSource.onmessage = (event) => {
                    this.message=event.data;
                };
                //接收失败
                this.eventSource.onerror = (error) => {
                    console.error('SSE error:', error);
                };
            }
        },
        mounted() {
            this.receiveMessage();
        },
        beforeDestroy() {
            // 关闭 EventSource 连接
            this.eventSource.close();
        }

    }
</script>

<style scoped>

</style>

代码流程:

1.创建了EventSource实例,连接到指定的SSE服务端端点(http://localhost:8080/sse/events

2.处理消息事件,通过 onmessage 事件处理程序,组件监听从服务端推送过来的消息。当服务端发送一条消息时,onmessage 事件处理程序将被调用,其中的 event.data 包含了服务端发送的实际数据。

3.处理错误事件,SSE 连接可能因为一些原因中断,例如网络故障。为了处理这种情况,组件还监听了 onerror 事件,并在发生错误时打印错误信息到控制台。

4.关闭连接,在组件销毁之前(beforeDestroy 钩子中),通过 this.eventSource.close() 关闭 SSE 连接,以确保资源被释放。这是为了防止在组件销毁后持续接收不必要的 SSE 事件。

效果说明

此时前后端就会建立连接,如果在控制台输出 this.message会发现每隔一段时间就会输出一次"向前端传递的数据"。

使用触发的方式向前端传递数据

修改Java代码:

java 复制代码
import com.hgkx.pojo.MyData;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;

@RestController
@RequestMapping("/sse")
public class SseController {

    private SseEmitter emitter;

    @GetMapping("/events")
    public SseEmitter streamEvents() throws IOException {
        emitter = new SseEmitter();

        // 设置超时处理
        emitter.onTimeout(() -> {
            emitter.complete();
            System.out.println("SSE 连接超时");
        });

        // 设置关闭处理
        emitter.onCompletion(() -> {
            System.out.println("SSE 连接已完成");
        });

        return emitter;
    }

    @RequestMapping("/aaa")
    public void aaa() {
        if (emitter != null) {
            try {
                // 向前端发送消息
                emitter.send("aaa");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    @RequestMapping("/bbb")
    public void bbb(){
        if (emitter != null) {
            try {
                // 向前端发送消息
                emitter.send("bbb");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

因为刚刚我们说到 emitter.send(); 才是用来发送数据的,那么我们只需要将他拿出来在单独设置数据即可,这样当我们访问aaa与bbb的地址时就会向前端发送数据。emitter也可以设置超时处理和关闭处理。当SpringBoot项目启动后用于连接的代码不会执行,只有接收的Vue前端也启动后才会执行。

总结

Server-Sent Events使用起来还是比较方便的,不需要加入额外的库或包,只需要单纯的编写代码就可以了,但是他它只支持单向通信,也就只能用于后端向前端发送消息,SSE在跨域通信时可能遇到一些限制,需要进行额外的配置。最后,由于SSE依赖于HTTP长连接,如果连接数量过多,可能会导致服务器资源不足。

相关推荐
HaiFan.19 分钟前
SpringBoot 事务
java·数据库·spring boot·sql·mysql
2401_8827275725 分钟前
低代码配置式组态软件-BY组态
前端·后端·物联网·低代码·前端框架
我要学编程(ಥ_ಥ)27 分钟前
一文详解“二叉树中的深搜“在算法中的应用
java·数据结构·算法·leetcode·深度优先
NoneCoder28 分钟前
CSS系列(36)-- Containment详解
前端·css
music0ant30 分钟前
Idea 添加tomcat 并发布到tomcat
java·tomcat·intellij-idea
anyup_前端梦工厂39 分钟前
初始 ShellJS:一个 Node.js 命令行工具集合
前端·javascript·node.js
5hand43 分钟前
Element-ui的使用教程 基于HBuilder X
前端·javascript·vue.js·elementui
计算机徐师兄1 小时前
Java基于SSM框架的无中介租房系统小程序【附源码、文档】
java·微信小程序·小程序·无中介租房系统小程序·java无中介租房系统小程序·无中介租房微信小程序
源码哥_博纳软云1 小时前
JAVA智慧养老养老护理帮忙代办陪诊陪护小程序APP源码
java·开发语言·微信小程序·小程序·微信公众平台
GDAL1 小时前
vue3入门教程:ref能否完全替代reactive?
前端·javascript·vue.js