Server-Sent Events(SSE整合SpringBoot)服务端向客户端消息推送轻量化方案(详细)

哈喽,大家好,最近在工作中需要用到后端实时跟前端实时推送数据,目前常用的有两种实现方式 websocket和sse 。 废话不多说 上干货!!!

1. SSE和 Websocket 的区别

SSE说明:

SSE是一种基于HTTP的单向通信机制,用于服务器向客户端推送数据。它的工作原理如下:

建立连接:客户端通过发送HTTP请求与服务器建立连接。在请求中,客户端指定了接收事件的终点(Endpoint)。
保持连接:服务器接收到连接请求后,保持连接打开,并定期发送事件数据给客户端。
事件流:服务器使用 “Content-Type: text/event-stream” 头部标识SSE连接,并使用特定格式的数据(事件流)发送给客户端。
客户端处理事件:客户端通过JavaScript的 EventSource 接口监听SSE连接,一旦接收到事件,就可以处理数据并更新页面。
SSE的特点和适用场景:

单向通信:SSE是从服务器到客户端的单向通信模型,只能由服务器推送数据给客户端。
实时更新:SSE适用于需要实时更新数据的应用场景,如股票行情、新闻推送等。
简单易用:使用SSE相对简单,无需额外的库或框架支持,可以直接使用浏览器的原生API进行开发。
 

Websocket 说明:

WebSocket是一种全双工的通信协议,它通过在客户端和服务器之间建立持久连接,实现双向通信。WebSocket的工作原理如下:

握手阶段:客户端向服务器发送WebSocket握手请求,服务器返回握手响应。在这个阶段,客户端和服务器协商选择协议和版本。
建立连接:握手成功后,客户端和服务器之间建立持久连接,可以进行双向数据传输。
双向通信:一旦连接建立,客户端和服务器都可以主动发送消息给对方。数据可以以文本或二进制格式进行传输。
断开连接:当任一方决定关闭连接时,可以发送关闭帧来终止连接。
WebSocket的特点和适用场景:

双向通信:WebSocket支持双向通信,客户端和服务器可以互相发送消息。
实时互动:WebSocket适用于实时互动的应用场景,如聊天应用、协作编辑等。
复杂性和灵活性:相对于SSE,WebSocket更为灵活,可以处理更复杂的通信需求。它允许自定义消息格式、心跳检测、连接状态管理等。

SSE与WebSocket的比较
现在我们来比较一下SSE和WebSocket的异同点:

通信模型:SSE是单向通信模型,只能由服务器向客户端推送数据,而WebSocket是双向通信模型,客户端和服务器可以互相发送消息。

连接性:SSE使用长轮询或HTTP流技术,而WebSocket使用持久连接。SSE需要频繁地发起HTTP请求来获取数据,而WebSocket只需在握手阶段建立一次连接,然后保持连接打开。

实时性:WebSocket提供了更低的延迟和更高的实时性,因为它支持双向通信,可以立即将数据推送给客户端。SSE虽然也可以实现实时性,但由于其单向通信模型,需要服务器定期发送数据。

浏览器支持:WebSocket在现代浏览器中得到广泛支持,包括Chrome、Firefox、Safari等。SSE在大多数现代浏览器中也有支持,但在某些旧版本浏览器中可能存在兼容性问题。

API复杂性:WebSocket提供了更灵活和复杂的API,可以处理更高级的通信需求。SSE相对简单,使用浏览器的原生 EventSource 接口即可。

2.下面是我搭建的一个简单的SSE整合SpringBoot的项目,来吧兄弟们 开整!!

    后端Controller代码:
@Slf4j
@RequiredArgsConstructor
@RestController
@RequestMapping("/sse/test")
public class SseController {

       private  final SseService sseService;

    /**
     * 创建新连接
     * @return
     */
    @GetMapping("/start")
     public Object createSession() throws IOException {
         return sseService.createSession();
     }

    /**
     * 断开连接
     * @return
     */
    @GetMapping("/close")
    public void  cloneSession(@RequestParam String  clientId){
        sseService.cloneSession(clientId);
    }
}

后端service层代码

public interface SseService {


    Object createSession() throws IOException;


    void cloneSession(String clientId);

}

后端service的ipl层代码

import com.xjww.ss.service.SseService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
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.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

@Service
@Slf4j
public class SseServiceImpl implements SseService {



    private final static Map SSE_CACHE= new ConcurrentHashMap<>();





    /**
     *  创建连接
     * @return
     */
    public  synchronized  SseEmitter createSession() throws IOException{
        // 过期时间设置为0,表示永不过期
        SseEmitter sseEmitter = new SseEmitter(0L);
        String id = UUID.randomUUID().toString().replace("-","");
        if (!SSE_CACHE.containsKey(id)){
            SSE_CACHE.put(id,sseEmitter);
            log.info("客户端:{}  新建连接成功,当前客户端总数为【{}】",id,SSE_CACHE.size() );
        }
       return  sseEmitter;
    }




    @Override
    public void cloneSession(String clientId) {
    if (SSE_CACHE.containsKey(clientId)){
        SSE_CACHE.get(clientId).complete();
        SSE_CACHE.remove(clientId);
         log.info("客户端:【{}】 断开成功,当前剩余客户端总数为【{}】",clientId,SSE_CACHE.size());
        }
    }




    /**
     *  定时任务 用于测试后端推送的数据
     */
    @Scheduled(fixedDelay = 3, initialDelay = 1,timeUnit = TimeUnit.SECONDS)
    public void job(){
         if (SSE_CACHE.size()>0){
             String msg ="消息"+UUID.randomUUID().toString();
             System.out.println("msg = " + msg);
             for (Map.Entry entry : SSE_CACHE.entrySet()) {
                 SseEmitter sseEmitter =SSE_CACHE.get(entry.getKey());
                 try {
                     sseEmitter.send(SseEmitter.event().reconnectTime(1000).id(entry.getKey()).data(msg));
                 }catch (IOException e){
                     SSE_CACHE.remove(entry.getKey());
                 }
             }
         }

    }
}

前端html代码:




    
    菠萝吹雪 -SSE轻量级化服务器端消息推送方案


sse测试

  至此是测试demo的所有代码,后面我会着重的强调一下测试过程。

 测试结果:

1.启动后端项目,如图所示

 Server-Sent Events(SSE整合SpringBoot)服务端向客户端消息推送轻量化方案(详细)_第1张图片

2.打开前端的demo,首次建立长连接,如图所示

 Server-Sent Events(SSE整合SpringBoot)服务端向客户端消息推送轻量化方案(详细)_第2张图片

  3.观察后端这时候已经建立连接,并且已经接收到后端推送的数据。

Server-Sent Events(SSE整合SpringBoot)服务端向客户端消息推送轻量化方案(详细)_第3张图片

总结 : 后端是在实时推送是一条一条的推送数据,所以前端也是一条条的数据在接受,当然这是轻量级的解决方案,后面如果大家需要,我也会出websocker的推送数据方案,希望能帮到大家,ok~    

你可能感兴趣的:(java,spring,java-ee,spring,boot,html5)