SSE使用详解及浏览器连接限制问题解决

一、SSE简介

        SSE是一种在网页开发中使用的、基于HTTP长连接技术,允许服务器向客户端浏览器实时推送更新。客户端通过创建一个EventSource对象并指向服务器上的一个URL来发起请求,这个请求保持打开状态,服务器可以在这个单一的TCP连接上不断发送新的数据块。这些数据块被称为“事件”,每个事件包含类型(可选)、数据和一些元数据(如事件ID,重新连接时间间隔等)。服务器端以简单的文本格式(通常为UTF-8编码的纯文本)发送数据。

二、SSE有什么用

        理论上, SSE WebSocket 做的是同一件事情。当你需要用新数据局部实时更新网络应用时,SSE 可以做到不需要用户执行任何操作,便可以完成。

  如统计数据的实时情况。类似这种更新频繁、 低延迟的场景,SSE 可以完全满足。

        SSE 是单向通道,只能服务器向客户端发送消息,当客户端发送一个 HTTP 请求,和服务器进行了一次握手,SSE便可以一直向客户端发送消息。相对于 WebSocket 的双工通道来说,开销会更小一些。

三、SSE发送的数据类型

在服务器端,需要使用text/event-stream作为响应的Content-Type。发送的数据中:

1.  event字段是可选的,用于指定事件的名称;

2. data字段是必须的,用于指定数据的内容;

3. id字段是可选的,用于指定事件的标识符;

4. retry字段是可选的,用于指定客户端在连接断开后重新连接的时间间隔(以毫秒为单位)。

每个字段都必须以换行符(\n)结尾,并且每个消息都必须以两个换行符(\n\n)结尾。

四、SSE的使用

本文使用的是springboot集成的SSE,使用SseEmitter来实现sse的消息推送

1. pom文件引入依赖

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

springboot中集成了SSE,引入springboot的依赖即可。

2. 编写创建长链接接口

这里展示创建SseEmitter的service的代码,controller中仅调用该接口即可

    public SseEmitter createSseConnect() {
        // 设置超时时间,0表示不过期。默认30秒,超过时间未完成会抛出异常:AsyncRequestTimeoutException
        SseEmitter sseEmitter = new SseEmitter(0L);
        String clientId = String.valueOf(UUID.randomUUID());
        sseCache.put(clientId, sseEmitter);
        // 连接断开回调
        sseEmitter.onCompletion(() -> {
            sseCache.get(clientId).complete();
            sseCache.remove(clientId);
        });
        // 连接超时
        sseEmitter.onTimeout(()-> {
            sseCache.get(clientId).complete();
            sseCache.remove(clientId);
        });
        // 连接报错
        sseEmitter.onError((throwable) ->  {
            sseCache.get(clientId).complete();
            sseCache.remove(clientId);
        });

        return sseEmitter;
    }

其中,创建sseEmitter对象,并创建UUID用于唯一标识该sseEmitter。

然后将sseEmitter存入缓存中(sseCache为全局缓存),以便发送的时候使用。

然后再配置回调函数,包括:

(1) onCompletion:连接断开的回调
(2) onTimeout: 连接超时回调(目前配置为不超时)
 (3) onError: 连接报错回调。

在回调函数中,调用

complete(): 表示执行完毕,会断开连接

再移除缓存中的连接信息。

3. 发送数据

在SseEmitter中,重载了三个send方法,归根结底都是调用同一个send方法,这里给大家介绍一个最全面的。

//先调用event()方法,返回SseEventBuilder
SseEmitter.SseEventBuilder event = event();

其中builder可以配置多个参数(以下为SSE源码)

    public interface SseEventBuilder {
        SseEventBuilder id(String id);

        SseEventBuilder name(String eventName);

        SseEventBuilder reconnectTime(long reconnectTimeMillis);

        SseEventBuilder comment(String comment);

        SseEventBuilder data(Object object);

        SseEventBuilder data(Object object, @Nullable MediaType mediaType);

        Set<ResponseBodyEmitter.DataWithMediaType> build();
    }

以下为SSE发送消息代码示例:

    public void ssePushMsg() {

        if (CollectionUtils.isEmpty(sseCache)) {
            return;
        }

        for (Map.Entry<String, SseEmitter> entry : sseCache.entrySet()) {
            try {
                SseEmitter.SseEventBuilder event = event();
                event.id("11");
                event.name("这是名字");
                event.data("这是一条sse数据");
                //发送消息
                entry.getValue().send(event);

            } catch (IOException e) {
                // 处理异常
                log.error("发送失败");
            }
        }

    }

 到这里发送消息就结束了,前端可以通过EventSource获取数据。还可以通过请求上述的“创建长链接接口”的连接获取数据。具体效果如下:

 五、浏览器限制HTTP1.1连接上限问题

在主流的edge、chrome、火狐浏览器中,都限制HTTP1.1的连接数上线为6。

此限制在HTTP规范(RFC2616)中定义。大多数现代浏览器每个域允许六个连接。

意思就是一个浏览器访问相同的域(ip+端口 为一个域)只能开6个,数据浏览器同域并发请求限制,到第7个时开始排队

在这些浏览器中,接收的sse数最多6个。注意,这里是说的是浏览器不是标签页,也就是说,一个浏览器如果开多个标签页,这些标签页的连接总数不能超过6个,第七个就无法连接了。

在一个系统中,如果有多种类型的数据,六个连接数是远不够用的,所以这里提出两个解决办法。

1. 升级HTTP协议为HTTP2

HTTP/1.x只能在一个TCP连接上发送一个HTTP请求,HTTP/2理论上可以在一个TCP连接上发送100个HTTP请求,而这些HTTP请求在浏览器看来,只是一个连接,所以避免了同源并发个数限制的问题。(具体操作方式可以参考其他文章)

        问题:如果时使用jdk生成的证书,浏览器认为自签证书是不安全

前端获取SSE连接时也会认为时不安全的,需要检查中选择信任该连接才行。(如果公司SSL证书可以采用此方案,但是SSL证书需要绑定域名,所以该方案得看情况选择)。

2. springboot开启多端口监听

        给springBoot开启多端口号监听,既然一个端口号只能6个,那么我给他配置10个端口号就能使用单个服务满足60个浏览器连接,还不够甚至可以继续绑定端口号。

        前端直接定义个公共的变量来记录当前连接的使用的端口情况,在请求路径后免拼接不同端口号,当同一个端口号已经有6 个连接时换下一个端口

  • 23
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值