欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

java后端http接口流式輸出到前端的方法示例

 更新時(shí)間:2025年09月19日 10:37:39   作者:abcnull  
這篇文章主要介紹了java后端http接口流式輸出到前端的相關(guān)資料,對(duì)比SseEmitter與WebFlux兩種實(shí)現(xiàn)方式,通過(guò)實(shí)例代碼詳細(xì)分析了其適用場(chǎng)景及優(yōu)劣,需要的朋友可以參考下

前置

解釋:

Server-Sent Events:服務(wù)器發(fā)送事件,是一種基于 HTTP 的輕量級(jí)協(xié)議,允許服務(wù)器主動(dòng)向客戶端推送文本數(shù)據(jù)(如 JSON、純文本等)

特點(diǎn):

  • 單向通信:僅服務(wù)器 → 客戶端方向
  • 基于 HTTP/HTTPS:無(wú)需特殊協(xié)議
  • 自動(dòng)重連:瀏覽器內(nèi)置支持?jǐn)嗑€重連
  • 簡(jiǎn)單易用:前端直接使用 EventSource API

流程:

前端 (Vue)       → 發(fā)起SSE請(qǐng)求 →   Spring Boot (Controller)
  ↓                                   ↓
(EventSource)    ← 流式數(shù)據(jù) ←   WebClient → 大模型API (流式HTTP)

后端流式輸出

使用 springboot SseEmitter

傳統(tǒng)項(xiàng)目小范圍流式推送 → SseEmitter(改動(dòng)成本低)

輕量級(jí) SSE 推送、兼容舊 MVC 項(xiàng)目

每個(gè)請(qǐng)求占用一個(gè)線程,高并發(fā)時(shí)資源消耗大

@RestController
public class StreamingController {
    @GetMapping("/stream")
    public SseEmitter streamText() {
        SseEmitter emitter = new SseEmitter(60_000L); // 設(shè)置超時(shí)時(shí)間(毫秒)

        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(() -> {
            try {
                for (int i = 1; i <= 10; i++) {
                    String data = "這是第 " + i + " 段內(nèi)容\n";
                    emitter.send(SseEmitter.event().name("message").data(data));
                    Thread.sleep(1000); // 模擬延遲
                }
                emitter.complete(); // 結(jié)束流
            } catch (IOException | InterruptedException e) {
                emitter.completeWithError(e);
            } finally {
                executor.shutdown();
            }
        });

        return emitter;
    }
}

代碼內(nèi)部直接承接大模型 api 調(diào)用,然后流式輸出到前端

@RestController
public class ModelStreamController {
    // 創(chuàng)建線程池
    private final ExecutorService executor = Executors.newCachedThreadPool();
    @GetMapping("/model-stream")
    public SseEmitter streamModel() {
        // 創(chuàng)建 SSE 發(fā)射器(60秒超時(shí))
        SseEmitter emitter = new SseEmitter(60_000L);
        
        // 提交任務(wù)到線程池
        executor.execute(() -> {
            try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
                // 1. 創(chuàng)建到大模型API的請(qǐng)求
                HttpGet request = new HttpGet("https://api.open-model.com/stream");
                // 2. 執(zhí)行請(qǐng)求
                HttpResponse response = httpClient.execute(request);
                // 3. 檢查響應(yīng)狀態(tài)
                if (response.getStatusLine().getStatusCode() != 200) {
                    throw new RuntimeException("API error: " + 
                            response.getStatusLine().getStatusCode());
                }
                
                // 4. 獲取響應(yīng)流
                try (InputStream is = response.getEntity().getContent();
                     BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
                    String line;
                    // 5. 逐行讀取并轉(zhuǎn)發(fā)
                    while ((line = reader.readLine()) != null) {
                        // 6. 發(fā)送給前端
                        emitter.send(line);
                    }
                    
                    // 7. 完成后關(guān)閉SSE
                    emitter.complete();
                }
            } catch (Exception e) {
                // 8. 錯(cuò)誤處理
                emitter.completeWithError(e);
            }
        });
        return emitter;
    }
}

使用 springboot WebFlux

新建高并發(fā)/代理外部流式 API → Flux + WebFlux(性能與擴(kuò)展性更優(yōu))

響應(yīng)式編程(Reactive Streams)

每個(gè)請(qǐng)求占用一個(gè)線程,高并發(fā)時(shí)資源消耗大

全鏈路非阻塞,適合代理外部流式 API

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-webflux</artifactId> <!-- 非阻塞IO -->
</dependency>

下面例子是轉(zhuǎn)發(fā)大模型的響應(yīng)結(jié)果

@RestController
public class WebFluxController {
    @GetMapping(value = "/flux-stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> stream() {
        // 直接轉(zhuǎn)發(fā)大模型的流式響應(yīng)(非阻塞)
        WebClient webClient = WebClient.create("https://api.open-model.com/stream");
        return webClient.get()
            .retrieve()
            .bodyToFlux(String.class)
            .map(data -> "data: " + data + "\n\n") // 封裝為 SSE 格式
            .delayElements(Duration.ofMillis(100)); // 非阻塞延遲
    }
}

使用 servlet

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
    // 設(shè)置響應(yīng)頭,聲明內(nèi)容類型為 SSE
    response.setContentType("text/event-stream");
    response.setCharacterEncoding("UTF-8");
    response.setHeader("Cache-Control", "no-cache");
    response.setHeader("Connection", "keep-alive");

    OutputStream outputStream = response.getOutputStream();

    try {
        // 模擬流式傳輸數(shù)據(jù)
        for (int i = 0; i < 5; i++) {
            String data = "event: message\ndata: {" + i + "}\n\n";
            outputStream.write(data.getBytes()); // 寫(xiě)入
            outputStream.flush(); // 不斷的發(fā)送 flush
            Thread.sleep(1000); // 模擬延遲
        }

        // 發(fā)送自定義結(jié)束事件
        String endEvent = "data: [DONE]\n\n";
        outputStream.write(endEvent.getBytes());
        outputStream.flush();

        // 關(guān)閉流,通知前端結(jié)束
        outputStream.close();
    } catch (Exception e) {
        e.printStackTrace();
        outputStream.close();
    }
}

如果是你的服務(wù)接入了大模型 api,大模型本身做的就是一個(gè)流式輸出呢?承接后直接輸出到前端

@WebServlet("/proxy-stream")
public class StreamingProxyServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // 設(shè)置響應(yīng)頭
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("UTF-8");
        response.setHeader("Cache-Control", "no-cache");
        response.setHeader("Connection", "keep-alive");

        // 構(gòu)造 JSON 請(qǐng)求體
        String jsonBody = "{\"param1\": \"value1\", \"param2\": \"value2\"}";

        // 創(chuàng)建 HttpClient 實(shí)例
        try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
            HttpPost httpPost = new HttpPost("https://third-party.com/stream"); // 替換為你的流式接口地址
            // 設(shè)置請(qǐng)求頭
            httpPost.setHeader("Content-Type", "application/json; charset=UTF-8");
            // 設(shè)置請(qǐng)求體
            httpPost.setEntity(new StringEntity(jsonBody, StandardCharsets.UTF_8));
            // 執(zhí)行請(qǐng)求
            try (CloseableHttpResponse backendResponse = httpClient.execute(httpPost)) {
                // 檢查后端響應(yīng)狀態(tài)碼
                int statusCode = backendResponse.getStatusLine().getStatusCode();
                if (statusCode != 200) {
                    response.sendError(HttpServletResponse.SC_BAD_GATEWAY, "后端接口返回狀態(tài)碼: " + statusCode);
                    return;
                }

                // 獲取后端流式接口的輸入流
                InputStream backendStream = backendResponse.getEntity().getContent();
                // 獲取前端響應(yīng)的輸出流
                OutputStream frontendStream = response.getOutputStream();
                byte[] buffer = new byte[4096];
                int bytesRead;

                // 邊讀邊寫(xiě),實(shí)時(shí)透?jìng)?
                while ((bytesRead = backendStream.read(buffer)) != -1) {
                    frontendStream.write(buffer, 0, bytesRead);
                    frontendStream.flush(); // 必須立即刷新,確保數(shù)據(jù)實(shí)時(shí)到達(dá)前端
                }

                // 結(jié)束流式連接
                frontendStream.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "流式傳輸失敗: " + e.getMessage());
        }
    }
}

擴(kuò)展

自定義事件類型:

后端發(fā)送:event: update\ndata: {…}\n\n

前端監(jiān)聽(tīng):.addEventListener(“update”, handler)

總結(jié)

到此這篇關(guān)于java后端http接口流式輸出到前端的文章就介紹到這了,更多相關(guān)java后端http接口流式輸出到前端內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java中List add添加不同類型元素的講解

    Java中List add添加不同類型元素的講解

    今天小編就為大家分享一篇關(guān)于java的List add不同類型的對(duì)象,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • Mybatis的特點(diǎn)及優(yōu)點(diǎn)

    Mybatis的特點(diǎn)及優(yōu)點(diǎn)

    Mybatis 本是apache的一個(gè)開(kāi)源項(xiàng)目iBatis, 2010年這個(gè)項(xiàng)目由apache software foundation 遷移到了google code,并且改名為MyBatis。mybatis有哪些特點(diǎn)和優(yōu)點(diǎn)呢?通過(guò)本文一起學(xué)習(xí)吧
    2016-12-12
  • SpringBoot自定義RestTemplate的攔截器鏈的實(shí)戰(zhàn)指南

    SpringBoot自定義RestTemplate的攔截器鏈的實(shí)戰(zhàn)指南

    在項(xiàng)目開(kāi)發(fā)中,RestTemplate作為Spring提供的HTTP客戶端工具,經(jīng)常用于訪問(wèn)內(nèi)部或三方服務(wù),但在實(shí)際項(xiàng)目中,我們往往需要對(duì)請(qǐng)求進(jìn)行統(tǒng)一處理,所以本文給大家介紹了SpringBoot自定義RestTemplate的攔截器鏈的實(shí)戰(zhàn)指南,需要的朋友可以參考下
    2025-07-07
  • Java容器類的深入理解

    Java容器類的深入理解

    本篇文章是對(duì)Java容器類進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • IDEA?服務(wù)器熱部署圖文詳解(On?Update?action/On?frame?deactivation)

    IDEA?服務(wù)器熱部署圖文詳解(On?Update?action/On?frame?deactivation)

    這篇文章主要介紹了IDEA?服務(wù)器熱部署詳解(On?Update?action/On?frame?deactivation),本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-03-03
  • Java多線程的用法詳解

    Java多線程的用法詳解

    本篇文章介紹了,在Java中多線程的用法詳解。需要的朋友參考下
    2013-04-04
  • 如何解決Project SDK is not defined問(wèn)題

    如何解決Project SDK is not defined問(wèn)題

    這篇文章主要介紹了如何解決Project SDK is not defined問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-09-09
  • Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤的解決方法

    Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤的解決方法

    今天給大家?guī)?lái)的是關(guān)于Java的相關(guān)知識(shí),文章圍繞著Hadoop運(yùn)行時(shí)遇到j(luò)ava.io.FileNotFoundException錯(cuò)誤展開(kāi),文中有非常詳細(xì)的解決方法,需要的朋友可以參考下
    2021-06-06
  • java實(shí)現(xiàn)死鎖的示例代碼

    java實(shí)現(xiàn)死鎖的示例代碼

    本篇文章主要介紹了java實(shí)現(xiàn)死鎖的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-01-01
  • Spring中利用配置文件和@value注入屬性值代碼詳解

    Spring中利用配置文件和@value注入屬性值代碼詳解

    這篇文章主要介紹了Spring中利用配置文件和@value注入屬性值代碼詳解,代碼中注釋比較詳細(xì),具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-11-11

最新評(píng)論