SSE介绍(实现流式响应)

news2025/1/11 13:58:02

写在前面

本文一起来看下SSE相关内容。

1:SSE是什么

全称,server-send events,基于http协议,一次http请求,server端可以分批推送数据, 不同于websocket的全双工通信,SSM单向通信,一般应用于需要返回的内容较多场景中,比如大模型问答场景,答案可能比较长,就可以使用SSM来实现流式的响应。

2:例子

  • pom
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>sse-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.6.0</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
<!--            <version>4.10.0</version>-->
            <version>3.14.9</version>
        </dependency>
        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp-sse</artifactId>
<!--            <version>4.10.0</version>-->
            <version>3.14.9</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>
  • SSM server
@RestController
@RequestMapping("/sse")
public class SseController {

    @RequestMapping("/emitter")
    public SseEmitter sse(@RequestBody String inputParameter, HttpServletResponse response) {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");
        SseEmitter emitter = new SseEmitter();


        // Simulate asynchronous data retrieval from the database
        new Thread(() -> {
            try {
                // Query the database based on the input parameter and send data in batches
                for (int i = 0; i < 10; i++) {
                    String data = "Data batch " + i + " for parameter: " + inputParameter;
                    emitter.send(data);
                    Thread.sleep(1000); // Simulate delay between batches
                }

                emitter.complete(); // Complete the SSE connection
            } catch (Exception e) {
                emitter.completeWithError(e); // Handle errors
            }
        }).start();

        return emitter;
    }
}
  • SSM client
package sse;

import okhttp3.*;
import okhttp3.sse.EventSource;
import okhttp3.sse.EventSourceListener;
import okhttp3.sse.EventSources;
import org.junit.Test;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class TTT {

    CountDownLatch c = new CountDownLatch(1);
    @Test
    public void aaa() throws InterruptedException {
        String json = "{\"inputParameter\": \"1234\"}";

        stream("http://localhost:8080/sse/emitter", new HashMap<>(), json, new EventSourceListener() {
            /*@Override
            public void onClosed(@NotNull EventSource eventSource) {
                System.out.println("closed");
            }

            @Override
            public void onOpen(@NotNull EventSource eventSource, @NotNull Response response) {
                System.out.println("open");
            }

            @Override
            public void onEvent(@NotNull EventSource eventSource, @Nullable String id, @Nullable String type, @NotNull String data) {
                System.out.println(data);
            }*/

            @Override
            public void onClosed(EventSource eventSource) {
                System.out.println("closed");
            }

            @Override
            public void onOpen(EventSource eventSource, Response response) {
                System.out.println("open");
            }

            @Override
            public void onEvent(EventSource eventSource, String id, String type, String data) {
                System.out.println("receive data: [ " + data + " ], time:" + new Date().getTime() / 1000);
            }
        });

        c.await();
    }
    public static final MediaType MEDIA_TYPE_JSON = MediaType.parse("application/json; charset=utf-8");

    public final static int MAX_IDLE_CONNECTIONS = 20;
    public final static long KEEP_ALIVE_DURATION = 30L;

    public final static int CONNECT_TIME_OUT = 6;

    public final static int WRITE_TIME_OUT = 10;

    public final static int READ_TIME_OUT = 40;

    private final static OkHttpClient HTTP_CLIENT = new OkHttpClient.Builder()
            .connectTimeout(CONNECT_TIME_OUT, TimeUnit.SECONDS)
            .writeTimeout(WRITE_TIME_OUT, TimeUnit.SECONDS)
            .readTimeout(READ_TIME_OUT, TimeUnit.SECONDS)
            .connectionPool(new ConnectionPool(MAX_IDLE_CONNECTIONS, KEEP_ALIVE_DURATION, TimeUnit.MINUTES))
            .build();

    public static boolean stream(String url, Map<String, String> headers, String json, EventSourceListener eventSourceListener) {
        try {
            RequestBody body = RequestBody.create(MEDIA_TYPE_JSON, json);
            Request.Builder builder = new Request.Builder();
//            buildHeader(builder, headers);
            Request request = builder.url(url).post(body).build();
            EventSource.Factory factory = EventSources.createFactory(HTTP_CLIENT);
            //创建事件
//            log.info("http stream请求,url: {},参数: {}", url, json);
            factory.newEventSource(request, eventSourceListener);
            return true;
        } catch (Exception e) {
//            log.error("http stream请求,url: {} 失败 ,参数: {}", url, json, e);
        }
        return false;
    }

}
  • 测试
    启动server后运行client:
    在这里插入图片描述
    可以看到一点一点往外蹦。

写在后面

参考文章列表

springboot整合sse 。

springboot搭建流式响应服务,SSE服务端实现 。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

代码生成工具1 ——项目简介和基础开发

1 项目简介 需要提前在数据库建好表&#xff0c;然后执行代码生成工具&#xff0c;会生成简单的Java文件&#xff0c;避免重复编写增删改查代码。类似的工具网上有很多&#xff0c;本人开发这个工具属于自娱自乐。这个专栏会记录开发的过程。 2 项目搭建 数据库使用MySQL &…

激光跟踪仪在石油化工领域高效应用

管板式换热器是一种实现物料之间热量传递的节能设备&#xff0c;在石油化工行业生产过程中扮演着重要的角色。无论是在提高生产效率&#xff0c;保证产品质量还是节约能源方面&#xff0c;都发挥着重要作用。 测量需求 管板式热交换器内部有多个管板和折流板&#xff0c;每一…

BigInteger和BigDecimal类

BigInteger 和 BigDecimal 介绍 应用场景 BigInteger适合保存比较大的整型BigDecimal适合保存精度更高的浮点型&#xff08;小数&#xff09; BigInteger 和 BigDecimal 常见方法 1&#xff0c;add 加2&#xff0c;subtract 减3&#xff0c;multiply 乘4&#xff0c;divide…

小程序开发之tdesignUI组件的简易使用教程

文章目录 TDesign简介小程序端使用TDesign一、安装二、使用可能的问题 附&#xff1a;如何使用weui开发参考链接 TDesign简介 TDesign 是腾讯各业务团队在服务业务过程中沉淀的一套企业级设计体系。 该UI框架支持桌面端、移动端、小程序端等全端。 小程序端使用TDesign 开发…

深沪300etf期权如果放弃行权了会怎么样?

今天期权懂带你了解深沪300etf期权如果放弃行权了会怎么样&#xff1f;期权行权是指期权持有人根据合约规定&#xff0c;在合约有效期内以约定的行权价格购买或卖出标的资产的权利&#xff0c;投资者可以选择行权&#xff0c;当然也有个别的选择放弃行权。 深沪300etf期权如果放…

不要被git的记录误导了,git也会犯错

Android studio中有个很方便的功能&#xff0c;可以查看单个文件的修改记录 显示这条记录把一行代码给删除了 我们找到这条完整的提交记录看看 在这次提交中我们确实没有删除那行代码。 那这行代码到底是怎么删除的&#xff1f;为什么又会被认为是我们删除的呢&#xff1f; …

OpenAI推出DALL·E 3识别器、媒体管理器

5月8日&#xff0c;OpenAI在官网宣布&#xff0c;将推出面向其文生图模型DALLE 3 的内容识别器&#xff0c;以及一个媒体管理器。 随着ChatGPT、DALLE 3等生成式AI产品被大量应用在实际业务中&#xff0c;人们越来越难分辨AI和人类创建内容的区别&#xff0c;这个识别器可以帮…

MongoDB Atlas Vector Search与Amazon Bedrock集成已全面可用

亮点前瞻 ●MongoDB Atlas Vector Search知识库与Amazon Bedrock的最新集成&#xff0c;将极大加速生成式AI应用的开发。 ●诺和诺德利用MongoDB Atlas Vector Search与Amazon Bedrock集成&#xff0c;加速构建AI应用程序。 MongoDB&#xff08;纳斯达克股票代码&#xff1a…

小白必看:数据防泄密软件介绍|安在云和Ping32对比?

在当今数字化时代&#xff0c;数据防泄密软件已经成为企业和组织不可或缺的重要工具。随着信息技术的发展&#xff0c;企业面临着越来越多的网络安全威胁&#xff0c;数据泄露事件也屡见不鲜。数据防泄密软件的出现&#xff0c;为企业提供了有效的解决方案。 一、数据防泄密软…

每日OJ题_记忆化搜索⑤_力扣329. 矩阵中的最长递增路径

目录 力扣329. 矩阵中的最长递增路径 解析代码1_爆搜递归&#xff08;超时&#xff09; 解析代码2_记忆化搜索 力扣329. 矩阵中的最长递增路径 329. 矩阵中的最长递增路径 难度 困难 给定一个 m x n 整数矩阵 matrix &#xff0c;找出其中 最长递增路径 的长度。 对于每…

vscode 使用正则搜索

ctrl c 复制&#xff0c;内容如下&#xff1a; Vue3简介创建Vue3工程Vue3核心语法路由pinia组件通信其它 APIVue3新组件

腐烂的橘子

题目链接 腐烂的橘子 题目描述 注意点 grid[i][j] 仅为 0、1 或 2每分钟&#xff0c;腐烂的橘子周围4个方向上相邻的新鲜橘子都会腐烂 解答思路 广度优先遍历找到每分钟腐烂的橘子&#xff0c;将第i - 1分腐烂的橘子都添加到队列中&#xff0c;在第i分钟&#xff0c;第i -…

重学java 30.API 1.String字符串

于是&#xff0c;虚度的光阴换来了模糊 —— 24.5.8 一、String基础知识以及创建 1.String介绍 1.概述 String类代表字符串 2.特点 a.Java程序中的所有字符串字面值(如“abc”)都作为此类的实例(对象)实现 凡是带双引号的&#xff0c;都是String的对象 String s "abc&q…

Linux基础服务NFS入门篇

文章目录 Linux基础服务NFS入门篇0.前言1.NFS1.1NFS简介1.2NFS配置 Linux基础服务NFS入门篇 0.前言 本文根据大佬们的资料整理了NFS的基础知识&#xff0c; 加深对linux运维基础服务工具的理解&#xff0c;以便个人查询复习使用。 1.NFS 资料来自B站阿铭linux的印象笔记&#…

基于Springboot的校园疫情防控系统(有报告)。Javaee项目,springboot项目。

演示视频&#xff1a; 基于Springboot的校园疫情防控系统&#xff08;有报告&#xff09;。Javaee项目&#xff0c;springboot项目。 项目介绍&#xff1a; 采用M&#xff08;model&#xff09;V&#xff08;view&#xff09;C&#xff08;controller&#xff09;三层体系结构…

17 空闲空间管理

目录 假设 底层机制 分割与合并 追踪已分配空间的大小 嵌入空闲列表 让堆增长 基本策略 最优匹配 首次匹配 下次匹配 其他方式 分离空闲列表 伙伴系统 小结 分页是将内存成大小相等的内存块&#xff0c;这样的机制下面&#xff0c;很容易去管理这些内存&#xff0c…

内外网文件传输摆渡工具大全|企业跨网文件交换解决方案

有许多文件传输工具可以用于内外网之间的安全文件传输。以下是一些常用的文件传输工具&#xff1a; 1、FileLink FileLink跨网文件传输系统提供一系列功能&#xff0c;包括文件传输审批、审计、敏感文件检查以及文件操作管控等。这些功能旨在确保文件传输的安全性和合规性&am…

Relaxed MemoryConsistency

SC和TSO都被称之为强&#xff08;strong&#xff09;保序模型&#xff1b; because the global memory order of each model usually respects (preserves) per-thread program order&#xff1b;回想一下&#xff0c;对于load和store的所有四种组合&#xff08;Load -> Lo…

python-dict序列化的数据为啥前后不一致

前情提要及背景:流式数据的二次处理终结篇-CSDN博客 假如直接将dict进行str,那么编码数据都是一致的,但是在postman上就表现不那么好看,如下: 而之前的显示如下: 其中的差别就是单引号与双引号的差别了。 采用如下方案无疑是最笨的方法了: 在Python中,如果你想将处理…

CMakeLists.txt语法规则:数学运算 math

一. 简介 前面几篇文章学习了 CMakeLists.txt语法中的一些常用变量&#xff0c;常用命令&#xff0c;双引号的作用。条件判断语句&#xff0c;循环语句等等。 本文简单学习一下 CMakeLists.txt语法中数学运算 match。 二. CMakeLists.txt语法规则&#xff1a;数学运算 math 在…