首页 > 其他分享 >深入浅出SseEmitter

深入浅出SseEmitter

时间:2023-08-14 16:04:18浏览次数:42  
标签:深入浅出 send sseEmitter sse SseEmitter data 客户端


SseEmitter 详解

在现代的Web应用程序中,实时数据是非常重要的。为了实现这一点,许多框架和技术被开发出来,其中包括SseEmitter。SseEmitter是一个Spring框架中的类,它允许服务器向客户端发送实时数据。

SseEmitter是Spring框架中的一个类,用于实现服务器向客户端推送消息的功能。SSE代表Server-Sent Events,是一种基于HTTP协议的服务器推送技术。SseEmitter可以在服务器端创建一个SSE连接,然后通过该连接向客户端发送消息。

优缺点

  • 优点:开发简单,无需引入其他包,可以相当于流式输出
  • 缺点: 单向,只能服务端往客户端发,且占用一个连接,连接数有限制,传输只支持utf-8,不支持二进制;

SseEmitter正常测试,就是在一个方法里面开启,在另一个方法里面关闭

SseEmitter算作异步返回,在springmvc中有个设置超时参数: spring.mvc.async.request-timeout ,默认是10s,所以如果你返回的SseEmitter 10s内没关闭可能会自动关闭;

/**
		 * Amount of time before asynchronous request handling times out. If this value is
		 * not set, the default timeout of the underlying implementation is used, e.g. 10
		 * seconds on Tomcat with Servlet 3.
		 * 设定async请求的超时时间,以毫秒为单位,如果没有设置的话,以具体实现的超时时间为准,比如tomcat的servlet3的话是10秒. 
		 */
		private Duration requestTimeout;

SseEmitter的用法非常简单。首先,我们需要在服务器端创建一个controller对象。有两个方法,一个创建SseEmitter对象,一个往SseEmitter对象发送数据: 代码如下:

@Api(tags = "sse", description = "sse")
@Controller
@Slf4j
public class SseController {

    private final Map<String, SseEmitter> sseCache = new HashMap<>();


    @GetMapping(value = "/sse")
    @ApiOperation("/sse")
    @ResponseBody
    public SseEmitter sse(String sseKey) {
        SseEmitter emitter = new SseEmitter();
        emitter.onCompletion(() -> {
            log.error("SseEmitter结束:" + sseKey);
        });
        sseCache.put(sseKey, emitter);
        return emitter;
    }

    @GetMapping(value = "/send")
    @ApiOperation("send")
    @ResponseBody
    public void send(String sseKey, String data) throws IOException {
        SseEmitter sseEmitter = sseCache.get(sseKey);
        if (sseEmitter != null) {
            sseEmitter.send(data);
        }
        sseEmitter.complete();
    }

}

然后,我们可以先调用/sse返回SseEmitter,然后调用/send使用SseEmitter对象的send方法向客户端发送消息。 自己测试一下:

http://localhost:8080/sse?sseKey=111
http://localhost:8080/send?data=aaa&sseKey=111

这会向客户端发送一条消息,消息内容为"aaa"。

除了文本消息,我们还可以向客户端发送事件。事件是一个JSON对象,包含一个名称和一些数据。客户端可以根据事件名称来处理不同类型的事件。代码如下:

Map<String, Object> data = new HashMap<>();
data.put("name", "John Doe");
data.put("age", 30);
emitter.send(SseEmitter.event().name("user").data(data));

这会向客户端发送一个名为"user"的事件,数据为一个包含"name"和"age"两个属性的JSON对象。 在第一个浏览器返回data:aaa 控制台打印

2023-08-14 15:20:31.449  INFO 19916 [io-8080-exec-13] c.s.s.controller.LoginInterceptor        [16] : request uri : /sse
2023-08-14 15:20:38.230  INFO 19916 [io-8080-exec-14] c.s.s.controller.LoginInterceptor        [16] : request uri : /send
2023-08-14 15:20:38.233  INFO 19916 [io-8080-exec-15] c.s.s.controller.LoginInterceptor        [16] : request uri : /sse
2023-08-14 15:20:38.234 ERROR 19916 [io-8080-exec-15] c.s.s.controller.sse.SseController       [34] : SseEmitter结束:111

在一个方法里面返回并关闭

场景: 这一种是在一个方法里面生成,然后异步处理,发送数据,然后进行关闭.举个栗子,对接chatgpt流式返回数据: 然后在完成里面将SseEmitter 关闭;

https://github.com/PlexPt/chatgpt-java

@GetMapping("/chat/sse")
    @CrossOrigin
    public SseEmitter sseEmitter(String prompt) {
       //国内需要代理 国外不需要
       Proxy proxy = Proxys.http("127.0.0.1", 1080);

       ChatGPTStream chatGPTStream = ChatGPTStream.builder()
                .timeout(600)
                .apiKey("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")
                .proxy(proxy)
                .apiHost("https://api.openai.com/")
                .build()
                .init();
        
        SseEmitter sseEmitter = new SseEmitter(-1L);

        SseStreamListener listener = new SseStreamListener(sseEmitter);
        Message message = Message.of(prompt);

        listener.setOnComplate(msg -> {
            //回答完成,可以做一些事情
        });
        chatGPTStream.streamChatCompletion(Arrays.asList(message), listener);


        return sseEmitter;
    }

注意事项

在使用SseEmitter时,我们需要注意以下几点:

  1. SseEmitter对象在使用完毕后必须关闭,否则会导致资源泄漏。可以在finally块中关闭SseEmitter对象。
  2. 如果客户端断开了连接,服务器应该捕获到异常并关闭SseEmitter对象。
  3. SseEmitter对象有一个默认的超时时间,如果在指定时间内没有发送任何消息,连接会自动关闭。可以通过setTimeout方法修改超时时间。

总之,SseEmitter是一个非常有用的类,可以方便地实现服务器向客户端推送消息的功能。在实际应用中,我们可以将其用于实时通知、实时数据展示等场景。

标签:深入浅出,send,sseEmitter,sse,SseEmitter,data,客户端
From: https://blog.51cto.com/yida/7077901

相关文章

  • 【Freertos基础入门】深入浅出freertos互斥量
    @TOC前言FreeRTOS是一款开源的实时操作系统,提供了许多基本的内核对象,其中包括互斥锁(Mutex)。互斥锁是一种常用的同步机制,用于确保在同一时间内只有一个任务可以访问共享资源,防止竞态条件等并发问题。本文将介绍FreeRTOS中的互斥锁的使用方法和注意事项。一、互斥量是什么?当多个任务......
  • 深入浅出关于微服务架构实战指南:分布式系统的设计与部署技巧
    深入浅出关于微服务架构实战指南:分布式系统的设计与部署技巧微服务架构实战指南:分布式系统的设计与部署技巧摘要:随着互联网应用的快速发展,传统的单体应用架构已经无法满足高可用、可扩展、快速迭代等需求。微服务架构的出现为解决这些问题提供了一种新的思路。本文将深入探讨微......
  • 深入浅出 Typescript
    TypeScript是JavaScript的一个超集,支持ECMAScript6标准(ES6教程)。TypeScript由微软开发的自由和开源的编程语言。TypeScript设计目标是开发大型应用,它可以编译成纯JavaScript,编译出来的JavaScript可以运行在任何浏览器上。目录1TypeScriptVSJavaScript2TypeScri......
  • 【网工玩转自动化】深入浅出TextFSM 2022终极版
    《从零开始NetDevOps》是本人8年多的NetDevOps实战总结的一本书(且称之为书,通过公众号连载的方式,集结成册,希望有天能以实体书的方式和大家相见)。NetDevOps是指以网络工程师为主体,针对网络运维场景进行自动化开发的工作思路与模式,是2014年左右从国外刮起来的一股“网工学Python"的风......
  • 深入浅出,五次课程,带您进入数据分析的世界
    导读:程序员4大出路:业务专家,全栈开发,技术专家,技术管理。最近,了解了下“数据分析”,觉得也可以作为参考。有兴趣的朋友们,可以看下本篇文章和对应的视频。 近些年,对于分布在各个行业的企业来说,「数据」已经逐渐开始扮演越来越重要的角色,成为企业长远发展不可忽视的力量。在数据分析大......
  • 深入浅出RxJava (四:在Android中使用响应式编程)
    [url=http://blog.danlew.net/2014/10/08/grokking-rxjava-part-4/]原文链接[/url]在第1,2,3篇中,我大概介绍了RxJava是怎么使用的。下面我会介绍如何在Android中使用RxJava.RxAndroidRxAndroid是RxJava的一个针对Android平台的扩展。它包含了一些能够简化And......
  • 深入浅出大型网站架构设计:设计流程
    假如没有使用独立的数据库系统或者云数据库,而是将数据作为文件保存在服务器上活着直接暂存在内存中,那么当这个网站流量增长时,增加再多的服务器也没有用,因为关键数据只能来源于这一台本来的服务器,其他增加的服务器无法提供这个网站所需的服务。网站的扩展性:添加这个新功能,是否......
  • 深入浅出Zookeeper之二Session建立
    上一篇,小编给大家介绍了zookeeperserver端的启动。这一篇将来说一下client和server端是如何建立session的。通过官网的DataMonitor例子来说明。通过Session建立这个例子,可以大概知道client端和server端是如何处理请求的,之间是如何通信的。官网Datamonitor的代码:Executor 1.pu......
  • 【深入浅出】你必须知道的 InnoDB 锁(二)
    ......
  • 【深入浅出Spring原理及实战】「夯实基础系列」360全方位透析和探究SpringEL的开发实
    Spring表达式语言(SpEL)本文将介绍SpEL的功能、API和语言语法。概念介绍Spring表达式语言(SpEL)是一种功能强大的表达式语言,用于在运行时查询和操作对象图。它的语法与UnifiedEL相似,但提供了更多功能,其中最主要的是方法调用和基本的字符串模板功能。技术无关性虽然还有其他几......