最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中使用Flux實現(xiàn)流式返回的方法小結(jié)

 更新時間:2025年06月18日 12:05:21   作者:sd2131512  
文章介紹流式返回(StreamingResponse)在SpringBoot中通過Flux實現(xiàn),優(yōu)勢包括提升用戶體驗、降低內(nèi)存消耗、支持長連接,適用于實時對話等場景,并給出調(diào)用阿里云DeepSeek模型的流式接口示例代碼,感興趣的朋友跟隨小編一起看看吧

背景

近期在使用deepseek/openai等網(wǎng)頁和APP時,發(fā)現(xiàn)大模型在思考和回復時,內(nèi)容是一點點的顯示出來的,于是好奇他們的實現(xiàn)方式。經(jīng)調(diào)研和使用開發(fā)者工具抓取請求,每次聊天會向后臺發(fā)送一個http請求,而這個接口跟普通接口一次性返回不一樣,而是以流式的返回。

流式返回的核心概念與優(yōu)勢

在傳統(tǒng)的 Web 開發(fā)中,接口通常以「一次性返回完整響應(yīng)體」的形式工作。而 ** 流式返回(Streaming Response)** 指的是服務(wù)器在處理請求時,將響應(yīng)結(jié)果分段逐步返回給客戶端,而非等待所有數(shù)據(jù)生成完成后再一次性返回。這種模式具有以下核心優(yōu)勢:

1. 提升用戶體驗

  • 對于大數(shù)據(jù)量響應(yīng)(如文件下載、長文本流)或?qū)崟r交互場景(如聊天機器人對話),客戶端可邊接收數(shù)據(jù)邊處理,減少「空白等待時間」,提升實時性感知。

2. 降低內(nèi)存消耗

  • 服務(wù)器無需在內(nèi)存中緩存完整響應(yīng)數(shù)據(jù),尤其適合處理高并發(fā)、大流量場景,降低 OOM(內(nèi)存溢出)風險。

3. 支持長連接與實時通信

  • 天然適配實時數(shù)據(jù)推送場景(如日志監(jiān)控、股票行情更新),可與 SSE(Server-Sent Events)、WebSocket 等技術(shù)結(jié)合使用。

大模型的接口,尤其是那些帶推理的模型接口返回,數(shù)據(jù)就是一點點的返回的,因此如果要提升用戶體驗,最好的方式就是采用流式接口返回。

在SpringBoot中基于Flux的流式接口實現(xiàn)

1. 依賴配置

在 pom.xml 中引入 WebFlux 依賴:

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

2. 流式接口實現(xiàn)(以模擬大模型對話為例)

import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
@RestController
@RequestMapping("/api/chat")
public class ChatController {
    @PostMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestBody ChatRequest request) {
        // 調(diào)用大模型 API 并返回 Flux 流
        return callLargeModelApi(request.message())
            .doOnNext(chunk -> log.info("發(fā)送響應(yīng)片段: {}", chunk))
            .doOnError(error -> log.error("流式處理出錯", error));
    }
    // 模擬調(diào)用大模型 API,返回 Flux 流
    private Flux<String> callLargeModelApi(String prompt) {
        // 實際項目中需替換為真實的大模型調(diào)用邏輯
        return Flux.just(
            "您好!", 
            "我是您的AI助手。", 
            "您的問題是:" + prompt, 
            "我將為您提供詳細解答..."
        )
        .delayElements(Duration.ofMillis(300)); // 模擬實時響應(yīng)延遲
    }
}

3. 關(guān)鍵配置說明

  • 響應(yīng)格式:設(shè)置 produces = MediaType.TEXT_EVENT_STREAM_VALUE,符合 SSE 協(xié)議。
  • 異步處理:Flux 流中的元素會被自動轉(zhuǎn)換為 SSE 格式(data: <內(nèi)容>\n\n)并推送至客戶端。
  • 背壓控制:通過 onBackpressureBuffer() 或 onBackpressureDrop() 處理客戶端消費速率問題。

瀏覽器端 JS 調(diào)用方案

1. 使用 EventSource(簡化版)

function connectWithEventSource() {
    const source = new EventSource("/api/chat");
    const chatWindow = document.getElementById("chat-window");
    source.onmessage = (event) => {
        chatWindow.innerHTML += `<div>${event.data}</div>`;
        chatWindow.scrollTop = chatWindow.scrollHeight;
    };
    source.onerror = (error) => {
        console.error("EventSource failed:", error);
        source.close();
    };
}

2. 使用 Fetch API(支持 POST 請求)

async function connectWithFetch() {
    const response = await fetch("/api/chat", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ message: "你好" })
    });
    const reader = response.body.getReader();
    const decoder = new TextDecoder();
    const chatWindow = document.getElementById("chat-window");
    while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        // 解碼并處理數(shù)據(jù)塊
        const chunk = decoder.decode(value, { stream: true });
        const messages = chunk.split('\n\n')
            .filter(line => line.trim().startsWith('data:'))
            .map(line => line.replace('data:', '').trim());
        messages.forEach(msg => {
            chatWindow.innerHTML += `<div>${msg}</div>`;
            chatWindow.scrollTop = chatWindow.scrollHeight;
        });
    }
}

調(diào)用Deepseek模型實戰(zhàn)

寫一個接口,通過Spring AI Alibaba ,調(diào)用阿里云百煉的deepseek模型,返回Flux流數(shù)據(jù)

基礎(chǔ)使用詳見:快速開始-阿里云Spring AI Alibaba官網(wǎng)官網(wǎng)

這里只給出轉(zhuǎn)Flux的示例,即通過client/model的stream方法來轉(zhuǎn),并通過map方法將每個流轉(zhuǎn)成前端需要的數(shù)據(jù)(我這里是區(qū)分了thinking思考和content的數(shù)據(jù),便于前端顯示):                                

    public Flux<ChatMessageResponse> processRealMessage(ChatMessageRequest request) throws ChatBaseException {
        // 獲取會話的歷史消息
        List<Message> messages = new ArrayList<>();
        List<ChatMessage> chatMessages = this.chatMessageService.getConversationMessage(request.getSessionId(), 1, 20);
        for (ChatMessage chatMessage : chatMessages) {
            if (Constants.MESSAGE_ROLE_USER.equals(chatMessage.getRole())) {
                messages.add(new UserMessage(chatMessage.getContent()));
            } else {
                messages.add(new AssistantMessage(chatMessage.getContent()));
            }
        }
        // 記錄用戶的輸入
        ChatMessage message = new ChatMessage();
        message.setContent(request.getContent());
        message.setType("text");
        message.setRole(Constants.MESSAGE_ROLE_USER);
        chatMessageService.insertMessage(request.getSessionId(), message);
        StringBuilder sb = new StringBuilder();
        // 模擬流式響應(yīng)
        return this.chatClient.prompt().messages(messages).user(request.getContent()).stream().chatResponse().doOnNext(response -> {
                    String content = response.getResult().getOutput().getText();
                    if (StringUtils.isNotBlank(content)) {
                        // 記錄完整的響應(yīng)對象
                        sb.append(content);
                    }
                })
                // 在流結(jié)束時記錄完整的會話內(nèi)容
                .doOnComplete(() -> {
                    // 這里記錄消息到數(shù)據(jù)庫
                    String content = sb.toString();
                    LOGGER.info("收到模型原始響應(yīng)結(jié)束: " + content);
                    ChatMessage assistantMessage = new ChatMessage();
                    assistantMessage.setContent(content);
                    assistantMessage.setType("text");
                    assistantMessage.setRole(Constants.MESSAGE_ROLE_ASSISTENT);
                    try {
                        chatMessageService.insertMessage(request.getSessionId(), assistantMessage);
                    } catch (ChatBaseException e) {
                        LOGGER.error("processMessage2 doOnComplete insertMessage error");
                    }
                }).map(response -> {
                    String content = response.getResult().getOutput().getText();
                    String thinking = response.getResults().get(0).getOutput().getMetadata().get("reasoningContent").toString();
                    if (StringUtils.isNotEmpty(content)) {
                        LOGGER.info("content" + content);
                        return new ChatMessageResponse("content", content);
                    } else if (StringUtils.isNotEmpty(thinking)) {
                        LOGGER.info("thinking" + thinking);
                        return new ChatMessageResponse("thinking", thinking);
                    } else {
                        LOGGER.info("done~~~~");
                        return new ChatMessageResponse("done", "");
                    }
                });
    }

完整代碼:MaDiXin/madichat 

到此這篇關(guān)于SpringBoot中使用Flux實現(xiàn)流式返回的技術(shù)總結(jié)的文章就介紹到這了,更多相關(guān)SpringBoot Flux流式返回內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java 9中如何對IntegerCache進行修改詳解

    Java 9中如何對IntegerCache進行修改詳解

    這篇文章主要給大家介紹了關(guān)于Java 9中如何對IntegerCache進行修改的相關(guān)資料,文中通過示例代碼介紹的非常詳細,對大家學習或使用java9具有一定的參考學習價值,需要的朋友們下面隨著小編來一起看看吧。
    2017-12-12
  • 基于spring+springmvc+hibernate 整合深入剖析

    基于spring+springmvc+hibernate 整合深入剖析

    這篇文章主要介紹了于spring+springmvc+hibernate整合實例,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-10-10
  • kafka生產(chǎn)者和消費者的javaAPI的示例代碼

    kafka生產(chǎn)者和消費者的javaAPI的示例代碼

    這篇文章主要介紹了kafka生產(chǎn)者和消費者的javaAPI的示例代碼,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-06-06
  • Java 關(guān)于時間復雜度和空間復雜度的深度刨析

    Java 關(guān)于時間復雜度和空間復雜度的深度刨析

    算法復雜度分為時間復雜度和空間復雜度。其作用: 時間復雜度是度量算法執(zhí)行的時間長短;而空間復雜度是度量算法所需存儲空間的大小
    2021-11-11
  • Java實現(xiàn)MySQL數(shù)據(jù)實時同步至Elasticsearch的方法詳解

    Java實現(xiàn)MySQL數(shù)據(jù)實時同步至Elasticsearch的方法詳解

    MySQL擅長事務(wù)處理,而Elasticsearch(ES)則專注于搜索與分析,將MySQL數(shù)據(jù)實時同步到ES,可以充分發(fā)揮兩者的優(yōu)勢,下面我們就來看看如何使用Java實現(xiàn)這一功能吧
    2025-03-03
  • 解決HashMap多線程操作導致死循環(huán)問題

    解決HashMap多線程操作導致死循環(huán)問題

    文章主要講述了在多線程環(huán)境下,HashMap的并發(fā)操作可能導致的死循環(huán)問題,包括鏈表/紅黑樹結(jié)構(gòu)破壞、擴容過程中的混亂以及讀寫不一致等,為了解決這些問題,文章建議使用線程安全的ConcurrentHashMap替代HashMap,并介紹了其分段鎖機制和優(yōu)化方案
    2025-01-01
  • SpringBoot 代碼風格推薦

    SpringBoot 代碼風格推薦

    本文主要介紹了Spring Boot 項目定義了統(tǒng)一的包結(jié)構(gòu)與命名規(guī)范,涵蓋分層架構(gòu)、實體對象、工具類的命名與放置規(guī)則,強調(diào)各層對象隔離與技術(shù)層分,下面就來詳細的介紹一下
    2026-05-05
  • SpringBoot集成validation校驗參數(shù)遇到的坑

    SpringBoot集成validation校驗參數(shù)遇到的坑

    這篇文章主要介紹了SpringBoot集成validation校驗參數(shù)遇到的坑,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-12-12
  • Mockito+PowerMock+Junit單元測試用途解析

    Mockito+PowerMock+Junit單元測試用途解析

    本文介紹單元測試在開發(fā)和DevOps中的規(guī)范要求,詳解Mockito和PowerMock的使用,包括解耦依賴、模擬行為、驗證調(diào)用及參數(shù)匹配,同時說明SpringBoot測試注解(如@MockBean)的用法,并提及IDEA插件Squaretest的自動化測試生成功能,感興趣的朋友一起看看吧
    2025-06-06
  • Java基礎(chǔ)語法之對象的比較方式

    Java基礎(chǔ)語法之對象的比較方式

    文章介紹了Java中自定義類類型的比較大小的三種方式:覆寫基類的equals方法、基于Comparable接口的比較、基于Comparator接口自定義比較器的比較,每種方式都有其適用場景和局限性
    2026-02-02

最新評論

邓州市| 贵溪市| 铁岭市| 泾源县| 九龙县| 沈丘县| 皮山县| 南宫市| 靖宇县| 来安县| 酉阳| 永平县| 金门县| 仪陇县| 婺源县| 左权县| 罗平县| 沙湾县| 资源县| 汾西县| 奉节县| 西吉县| 连山| 惠来县| 阜城县| 施甸县| 洛扎县| 油尖旺区| 墨脱县| 甘谷县| 辽中县| 宜州市| 邢台县| 城步| 睢宁县| 清镇市| 天津市| 金门县| 蚌埠市| 莱芜市| 志丹县|