使用Spring Boot輕松實(shí)現(xiàn)流式AI輸出的步驟
1、背景
隨著AI的快速發(fā)展,越來(lái)越多的AI應(yīng)用誕生了,但是AI也有響應(yīng)慢的問(wèn)題,一般不能夠即時(shí)響應(yīng),為了優(yōu)化用戶體驗(yàn),現(xiàn)在大部分AI應(yīng)用都是實(shí)現(xiàn)了打字機(jī)的效果,那么這種效果是如何實(shí)現(xiàn)的呢?今天我們先看一下后端的實(shí)現(xiàn)邏輯。
代碼流程是后端發(fā)出請(qǐng)求,請(qǐng)求智能體或AI模型暴露的流式接口,然后返回一個(gè)流式接口。
為什么不直接前端請(qǐng)求AI接口,因?yàn)橛械腁I接口在前端直接請(qǐng)求,可能會(huì)出現(xiàn)跨域問(wèn)題。因?yàn)锳I接口返回的響應(yīng)中沒(méi)有包含跨域的Access-Control-Allow-Origin。
2、實(shí)現(xiàn)步驟
1、引入依賴
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency>
2、使用webClient發(fā)起對(duì)AI接口請(qǐng)求
代碼中的URL,請(qǐng)求頭、請(qǐng)求體或者請(qǐng)求方法都可以按照對(duì)應(yīng)的AI接口文檔進(jìn)行替換。
WebClient webClient = WebClient.create(); Flux<String> resultFlux = webClient.post() .uri(URL) //請(qǐng)求url .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.AUTHORIZATION, "Bearer "+ API_KEY) // 添加認(rèn)證頭部 .bodyValue(requestBody)//請(qǐng)求體 .retrieve() .bodyToFlux(String.class); //返回流式結(jié)果
3、啟動(dòng)類需要添加@EnableAsync注解
4、如果工程中有過(guò)濾器,需要進(jìn)行配置
我的工程啟動(dòng)后報(bào)錯(cuò)
Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.
大概意思是異步支持必須在servlet和所有的過(guò)濾器中被標(biāo)注成是enabled
Servlet和Filter聲明:如果您使用的是基于XML的配置,可以在web.xml
文件中的servlet和filter聲明中添加<async-supported>true</async-supported>
元素來(lái)啟用異步支持
<servlet> <servlet-name>myServlet</servlet-name> <servlet-class>com.example.MyAsyncServlet</servlet-class> <async-supported>true</async-supported> </servlet> <filter> <filter-name>myFilter</filter-name> <filter-class>com.example.MyAsyncFilter</filter-class> <async-supported>true</async-supported> </filter>
如果您使用的是基于注解的配置或Java配置類,可以通過(guò)實(shí)現(xiàn)javax.servlet.Servlet接口并覆蓋isAsyncSupported()方法返回true,或者通過(guò)@WebServlet和@WebFilter注解的asyncSupported屬性來(lái)設(shè)置。我使用的是這種方式
@WebServlet(urlPatterns = "/async", asyncSupported = true) public class MyAsyncServlet extends HttpServlet { // ... } @WebFilter(urlPatterns = "/*", asyncSupported = true) public class MyAsyncFilter implements Filter { // ... }
5、postman發(fā)送請(qǐng)求后結(jié)果
controller類接口
@RequestMapping(method = RequestMethod.POST, value = "/getAIResult",produces = MediaType.TEXT_EVENT_STREAM_VALUE) Flux<String> getAIResult(@RequestBody String content){ // 構(gòu)建請(qǐng)求體 HashMap<String, Object> requestBody = new HashMap<>(); requestBody.put("model", "6bbdf08d55244bd9be24052ded2a58ef"); requestBody.put("context",0); requestBody.put("stream", true); List<HashMap<String, String>> messages = new ArrayList<>(); HashMap<String, String> message = new HashMap<>(); message.put("role", "user"); message.put("content", content); messages.add(message); requestBody.put("messages", messages); WebClient webClient = WebClient.create(); Flux<String> resultFlux = webClient.post() .uri(URL) .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) .header(HttpHeaders.AUTHORIZATION, "Bearer "+ API_KEY) // 添加認(rèn)證頭部 .bodyValue(requestBody) .retrieve() .bodyToFlux(String.class); // 輸出響應(yīng)結(jié)果 // // 訂閱響應(yīng)以觸發(fā)實(shí)際的 HTTP 請(qǐng)求 // resultFlux.subscribe( // response -> System.out.println("Response received: " + response), // error -> System.err.println("Error occurred: " + error.getMessage()) // ); return resultFlux; }
3、使用技術(shù)介紹
WebFlux
WebFlux模塊是Spring 5引入的一部分,旨在提供一種新的方式來(lái)構(gòu)建響應(yīng)式的Web應(yīng)用程序。它允許你以異步和非阻塞的方式處理HTTP請(qǐng)求,這在處理高并發(fā)場(chǎng)景時(shí)可以顯著提高性能。
WebFlux的特點(diǎn)
- 非阻塞I/O:與傳統(tǒng)的Servlet API不同,WebFlux使用的是非阻塞I/O模型,這意味著它可以更有效地利用線程資源。
- 反應(yīng)式編程:WebFlux內(nèi)置了對(duì)反應(yīng)式編程的支持,主要通過(guò)Reactor庫(kù)實(shí)現(xiàn),使得編寫和處理異步代碼更加容易。
- 函數(shù)式路由:除了注解驅(qū)動(dòng)的控制器,WebFlux還提供了函數(shù)式路由API,讓你能夠以聲明性的方式定義路由規(guī)則。
以上來(lái)自AI內(nèi)容生成,看完一頭霧水,下方給出介紹
非阻塞I/O
含義:非阻塞I/O(Non-blocking I/O)是一種編程模型,它允許應(yīng)用程序在等待某些操作完成時(shí)不會(huì)被阻塞。與傳統(tǒng)的Servlet API(如Spring MVC)相比,WebFlux采用了基于事件和回調(diào)的非阻塞I/O模型。
- 傳統(tǒng)Servlet API (阻塞I/O):在傳統(tǒng)的Servlet環(huán)境中,每個(gè)HTTP請(qǐng)求都會(huì)分配一個(gè)線程來(lái)處理。如果這個(gè)處理過(guò)程包含了一個(gè)長(zhǎng)時(shí)間運(yùn)行的操作(例如數(shù)據(jù)庫(kù)查詢或網(wǎng)絡(luò)調(diào)用),那么該線程會(huì)被阻塞直到操作完成。這意味著線程不能用來(lái)處理其他請(qǐng)求,從而降低了服務(wù)器的效率。
- WebFlux (非阻塞I/O):WebFlux使用了Netty這樣的異步網(wǎng)絡(luò)框架,它們可以在不阻塞線程的情況下執(zhí)行I/O操作。當(dāng)一個(gè)請(qǐng)求涉及到耗時(shí)的任務(wù)時(shí),它不會(huì)阻塞當(dāng)前的線程;相反,任務(wù)完成后會(huì)觸發(fā)相應(yīng)的回調(diào)函數(shù)繼續(xù)處理。這種方式使得單個(gè)線程可以處理多個(gè)并發(fā)請(qǐng)求,極大地提高了資源利用率和服務(wù)的吞吐量。
反應(yīng)式編程
含義:反應(yīng)式編程(Reactive Programming)是一種面向數(shù)據(jù)流和變化傳播的編程范式。它強(qiáng)調(diào)的是通過(guò)聲明式的代碼來(lái)描述數(shù)據(jù)流的變化,并能夠?qū)@些變化做出響應(yīng)。WebFlux內(nèi)置了對(duì)反應(yīng)式編程的支持,主要通過(guò)Project Reactor庫(kù)實(shí)現(xiàn),這是Spring 5引入的一個(gè)核心特性。
- Reactor庫(kù):Reactor提供了兩個(gè)核心類型——
Mono
和Flux
,分別表示0到1個(gè)元素的異步序列和0到N個(gè)元素的異步序列。開發(fā)者可以使用這些類型來(lái)構(gòu)建復(fù)雜的異步邏輯,而不需要顯式地管理線程或同步問(wèn)題。 - 好處:
- 簡(jiǎn)化異步編程:通過(guò)組合操作符(如
map
,flatMap
,filter
等),你可以輕松地創(chuàng)建復(fù)雜的異步工作流,同時(shí)保持代碼的簡(jiǎn)潔性和可讀性。 - 錯(cuò)誤處理:Reactor還提供了一套強(qiáng)大的錯(cuò)誤處理機(jī)制,比如
onErrorResume
、retry
等,使得處理異常情況更加直觀。 - 背壓支持:對(duì)于生產(chǎn)者-消費(fèi)者模式中的流量控制,Reactor實(shí)現(xiàn)了背壓(Backpressure),確保系統(tǒng)不會(huì)因?yàn)檫^(guò)載而崩潰。
- 簡(jiǎn)化異步編程:通過(guò)組合操作符(如
函數(shù)式路由
含義:函數(shù)式路由是WebFlux提供的另一種定義HTTP端點(diǎn)的方式,除了傳統(tǒng)的注解驅(qū)動(dòng)控制器之外。它允許你以一種聲明性的、函數(shù)式的方式來(lái)配置路由規(guī)則,這在某些情況下可能比注解更靈活、更具表達(dá)力。
- RouterFunction和HandlerFunction:在函數(shù)式路由中,
RouterFunction
用于定義路由匹配邏輯,而HandlerFunction
則負(fù)責(zé)處理實(shí)際的請(qǐng)求。兩者結(jié)合在一起,可以非常清晰地表達(dá)出“如果路徑匹配,則執(zhí)行某個(gè)處理器”的意圖。
到此這篇關(guān)于用Spring Boot輕松實(shí)現(xiàn)流式AI輸出的文章就介紹到這了,更多相關(guān)Spring Boot流式AI輸出內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringBoot或SpringAI對(duì)接DeepSeek大模型的詳細(xì)步驟
- SpringBoot整合DeepSeek實(shí)現(xiàn)AI對(duì)話功能
- 在 Spring Boot 3 中接入生成式 AI的操作方法
- 解決創(chuàng)建springboot后啟動(dòng)報(bào)錯(cuò):Failed?to?bind?properties?under‘spring.datasource‘
- Springboot項(xiàng)目打包如何將依賴的jar包輸出到指定目錄
- Springboot Logback日志多文件輸出方式(按日期和大小分割)
- Java調(diào)用ChatGPT(基于SpringBoot和Vue)實(shí)現(xiàn)可連續(xù)對(duì)話和流式輸出的ChatGPT API
- 在Spring Boot中使用Spark Streaming進(jìn)行實(shí)時(shí)數(shù)據(jù)處理和流式計(jì)算的步驟
- SpringBoot項(xiàng)目實(shí)現(xiàn)MyBatis流式查詢的教程詳解
相關(guān)文章
struts2+spring+ibatis框架整合實(shí)現(xiàn)增刪改查
這篇文章主要為大家詳細(xì)介紹了struts2+spring+ibatis框架整合實(shí)現(xiàn)增刪改查操作,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-07-07mybatis-plus實(shí)現(xiàn)自定義SQL、多表查詢與多表分頁(yè)查詢語(yǔ)句實(shí)例
mybatisplus是個(gè)很好用的插件,相信小伙伴們都知道,下面這篇文章主要給大家介紹了關(guān)于mybatis-plus實(shí)現(xiàn)自定義SQL、多表查詢與多表分頁(yè)查詢語(yǔ)句的相關(guān)資料,需要的朋友可以參考下2022-09-09Spring-boot的debug調(diào)試代碼實(shí)例
這篇文章主要介紹了Spring-boot的debug調(diào)試代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12Java中內(nèi)部類使用方法實(shí)戰(zhàn)案例分析
這篇文章主要介紹了Java中內(nèi)部類使用方法,結(jié)合具體案例形式分析了Java內(nèi)部類原理、調(diào)用方法及相關(guān)使用注意事項(xiàng),需要的朋友可以參考下2019-09-09Java try-with-resource語(yǔ)法使用解析
這篇文章主要介紹了Java try-with-resource語(yǔ)法使用解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-03-03深入了解Java語(yǔ)言中的并發(fā)性選項(xiàng)有何不同
這篇文章主要介紹了深入了解Java語(yǔ)言中的并發(fā)性選項(xiàng)有何不同,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,,需要的朋友可以參考下2019-06-06