網(wǎng)站開發(fā)模式acca少女網(wǎng)課視頻
在探索ChatGPT的使用過程中,我們發(fā)現(xiàn)GPT采用了流式數(shù)據(jù)返回的方式。理論上,這種情況可以通過全雙工通信協(xié)議實(shí)現(xiàn)持久化連接,或者依賴于基于EventStream的事件流。然而,ChatGPT選擇了后者,也就是本文即將深入探討的SSE(Server-Sent Events)技術(shù)。
要理解這個(gè)選擇,我們需要關(guān)注ChatGPT的使用場景。作為一個(gè)基于深度學(xué)習(xí)的大型語言模型,ChatGPT需要處理大量的自然語言數(shù)據(jù),這無疑需要大量的計(jì)算資源和時(shí)間。相較于普通的讀取數(shù)據(jù)庫操作,其響應(yīng)速度自然會(huì)慢許多。
對(duì)于這種可能需要長時(shí)間等待響應(yīng)的對(duì)話場景,ChatGPT采用了一種巧妙的策略:它會(huì)將已經(jīng)計(jì)算出的數(shù)據(jù)“推送”給用戶,并利用SSE技術(shù)在計(jì)算過程中持續(xù)返回?cái)?shù)據(jù)。這樣做可以避免用戶因等待時(shí)間過長而選擇關(guān)閉頁面。
什么是 SSE?
SSE(Server-Sent Events)是一種Web技術(shù),它允許服務(wù)器實(shí)時(shí)向客戶端推送數(shù)據(jù)。相比于傳統(tǒng)的輪詢和長輪詢機(jī)制,SSE提供了一種更高效且實(shí)時(shí)的數(shù)據(jù)推送方式。這種技術(shù)主要應(yīng)用于構(gòu)建實(shí)時(shí)應(yīng)用,例如實(shí)時(shí)消息推送、股票行情更新等。
SSE是HTML5規(guī)范中的一個(gè)通信相關(guān)API,它主要包含兩個(gè)部分:服務(wù)端與瀏覽器端的通信協(xié)議(基于HTTP協(xié)議),以及瀏覽器端JavaScript可使用的EventSource對(duì)象。
SSE運(yùn)行在HTTP協(xié)議之上,它允許服務(wù)器以事件流(Event Stream)的形式將數(shù)據(jù)發(fā)送給客戶端。客戶端通過建立持久化的HTTP連接,并監(jiān)聽這個(gè)事件流,從而可以實(shí)時(shí)接收到服務(wù)器推送的數(shù)據(jù)。
SSE具有以下幾個(gè)主要特點(diǎn):
- 簡單易用:SSE使用基于文本的數(shù)據(jù)格式,如純文本、JSON等,這使得數(shù)據(jù)發(fā)送和解析都相對(duì)簡單直接。
- 單向通信:SSE僅支持從服務(wù)器到客戶端的單向通信。這意味著服務(wù)器可以主動(dòng)推送數(shù)據(jù)給客戶端,但客戶端只能被動(dòng)接收數(shù)據(jù)。
- 實(shí)時(shí)性:由于SSE能夠建立持久化連接,服務(wù)器因此可以實(shí)時(shí)地將數(shù)據(jù)推送給客戶端,無需客戶端頻繁地發(fā)起請(qǐng)求。這大大提高了數(shù)據(jù)傳輸?shù)男屎蛯?shí)時(shí)性。
SSE與WebSocket的比較
WebSocket是一種Web技術(shù),用于實(shí)現(xiàn)實(shí)時(shí)雙向通信,它與SSE(Server-Sent Events)在某些方面存在差異。以下是對(duì)兩者的比較:
- 數(shù)據(jù)推送方向:SSE主要支持從服務(wù)器到客戶端的單向通信,這意味著服務(wù)器可以主動(dòng)地向客戶端推送數(shù)據(jù)。而WebSocket則支持雙向通信,允許服務(wù)器和客戶端之間進(jìn)行實(shí)時(shí)的數(shù)據(jù)交換。
- 連接建立:SSE利用基于HTTP的長連接,通過常規(guī)的HTTP請(qǐng)求和響應(yīng)來建立連接,進(jìn)而實(shí)現(xiàn)數(shù)據(jù)的實(shí)時(shí)推送。相反,WebSocket采用自定義的協(xié)議,通過創(chuàng)建WebSocket連接來實(shí)現(xiàn)雙向通信。
- 兼容性:由于SSE基于HTTP協(xié)議,因此它可以在大多數(shù)現(xiàn)代瀏覽器中使用,并且無需進(jìn)行額外的協(xié)議升級(jí)。雖然WebSocket在絕大多數(shù)現(xiàn)代瀏覽器中也得到了支持,但在某些特定的網(wǎng)絡(luò)環(huán)境下可能會(huì)遇到問題。
- 適用場景:SSE適合于需要服務(wù)器向客戶端實(shí)時(shí)推送數(shù)據(jù)的場景,例如股票價(jià)格更新、新聞實(shí)時(shí)推送等。而WebSocket則適合于需要實(shí)時(shí)雙向通信的場景,如聊天應(yīng)用、多人在線協(xié)作編輯等。
選擇使用SSE還是WebSocket主要取決于具體的業(yè)務(wù)需求和場景。如果你只需要實(shí)現(xiàn)從服務(wù)器向客戶端的單向數(shù)據(jù)推送,并且希望保持操作簡便且兼容性好,那么SSE是一個(gè)理想的選擇。然而,如果你需要實(shí)現(xiàn)雙向通信,或者需要更高級(jí)的功能和控制,那么WebSocket可能會(huì)更適合你的需求。
SSE的實(shí)現(xiàn)原理
以下是SSE(Server-Sent Events)的實(shí)現(xiàn)原理:
- 連接建立:通常情況下,客戶端(如瀏覽器)通過發(fā)送HTTP GET請(qǐng)求到服務(wù)器來請(qǐng)求建立一個(gè)SSE連接。
- 服務(wù)器響應(yīng):一旦服務(wù)器接收到請(qǐng)求,它將返回一個(gè)HTTP響應(yīng),該響應(yīng)的狀態(tài)碼為200,內(nèi)容類型(Content-Type)設(shè)置為"text/event-stream"。
- 數(shù)據(jù)推送:服務(wù)器可以通過已經(jīng)建立的連接向客戶端推送數(shù)據(jù)。每次推送的數(shù)據(jù)被稱作一個(gè)事件(Event)。每個(gè)事件由一個(gè)或多個(gè)以"\n\n"分隔的數(shù)據(jù)塊組成。每個(gè)數(shù)據(jù)塊都是一行文本,可能包含一個(gè)以":"開頭的注釋行、以"data:"開頭的數(shù)據(jù)行,或者以"id:"和"event:"開頭的行來指定事件ID和事件類型。
- 客戶端處理:當(dāng)客戶端接收到服務(wù)器推送的事件后,它會(huì)觸發(fā)相應(yīng)的JavaScript事件處理器來處理這些事件。
- 重連:如果連接斷開,客戶端會(huì)自動(dòng)嘗試重新連接。如果服務(wù)器在事件中指定了ID,那么在重新連接時(shí),客戶端會(huì)發(fā)送一個(gè)"Last-Event-ID"的HTTP頭部信息到服務(wù)器,告訴服務(wù)器客戶端接收到的最后一個(gè)事件的ID。根據(jù)這個(gè)信息,服務(wù)器可以決定從哪個(gè)事件開始重新發(fā)送數(shù)據(jù)。
總結(jié)起來,SSE使用了基于文本和HTTP協(xié)議的簡單機(jī)制,使得服務(wù)器能夠?qū)崟r(shí)地將數(shù)據(jù)推送到客戶端,而無需客戶端頻繁地發(fā)起新的請(qǐng)求。
使用SSE的注意事項(xiàng)
以下是在使用SSE(Server-Sent Events)技術(shù)進(jìn)行實(shí)時(shí)數(shù)據(jù)推送時(shí)需要注意的幾個(gè)關(guān)鍵點(diǎn):
- 異步處理:由于SSE基于長連接的機(jī)制,因此數(shù)據(jù)推送過程可能會(huì)持續(xù)較長時(shí)間。為了防止服務(wù)器線程被阻塞,建議采用異步方式處理SSE請(qǐng)求。例如,可以在控制器方法中使用@Async注解或利用CompletableFuture等異步編程方式。
- 超時(shí)處理:SSE連接可能會(huì)因網(wǎng)絡(luò)中斷、客戶端關(guān)閉等原因而超時(shí)。為了避免無效連接占據(jù)服務(wù)器資源,建議設(shè)置超時(shí)時(shí)間并處理超時(shí)情況。例如,可以利用SseEmitter對(duì)象的setTimeout()方法設(shè)定超時(shí)時(shí)間,并通過onTimeout()方法處理超時(shí)邏輯。
- 異常處理:在實(shí)際應(yīng)用中,可能會(huì)遇到網(wǎng)絡(luò)異常、數(shù)據(jù)推送失敗等問題。這種情況下,可以使用SseEmitter對(duì)象的completeWithError()方法將異常信息發(fā)送給客戶端,并在客戶端通過eventSource.onerror事件進(jìn)行處理。
- 內(nèi)存管理:在使用SseEmitter時(shí),需要特別注意內(nèi)存管理問題,尤其是在大量并發(fā)連接的場景下。當(dāng)客戶端斷開連接后,務(wù)必及時(shí)釋放SseEmitter對(duì)象,以避免資源泄漏和內(nèi)存溢出。
- 并發(fā)性能:SSE的并發(fā)連接數(shù)可能對(duì)服務(wù)器性能產(chǎn)生影響。如果需要處理大量并發(fā)連接,可以考慮使用線程池或其他異步處理方式,以最大化服務(wù)器資源利用。
- 客戶端兼容性:雖然大多數(shù)現(xiàn)代瀏覽器都支持SSE,但一些舊版本的瀏覽器可能不支持。因此,在使用SSE時(shí),需要確保目標(biāo)客戶端對(duì)其有良好的支持,或者提供備選的實(shí)時(shí)數(shù)據(jù)推送機(jī)制。
以上這些注意事項(xiàng)可以根據(jù)具體應(yīng)用需求進(jìn)行調(diào)整和優(yōu)化。在實(shí)際應(yīng)用中,確保服務(wù)器的穩(wěn)定性、安全性和性能是非常重要的。同時(shí),在處理SSE連接時(shí),可以考慮適當(dāng)?shù)南蘖骱桶踩刂拼胧?#xff0c;以防止濫用和惡意連接的出現(xiàn)。總的來說,使用SSE技術(shù)時(shí)需要全面考慮各個(gè)方面的因素,才能實(shí)現(xiàn)高效、穩(wěn)定、安全的實(shí)時(shí)數(shù)據(jù)推送服務(wù)。
SpringBoot集成SSE案例
假設(shè)正在開發(fā)一個(gè)實(shí)時(shí)股票價(jià)格監(jiān)控應(yīng)用,需要將股票價(jià)格實(shí)時(shí)推送給客戶端。以下為Spring Boot中集成SSE技術(shù)實(shí)現(xiàn)的場景示例代碼。
首先,定義一個(gè)控制器來處理SSE請(qǐng)求和發(fā)送實(shí)時(shí)股票價(jià)格:
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.util.Random;@RestController
public class StockController {@GetMapping(value = "/stock-price", produces = MediaType.TEXT_EVENT_STREAM_VALUE)public SseEmitter streamStockPrice() {SseEmitter emitter = new SseEmitter();// 模擬生成實(shí)時(shí)股票價(jià)格并推送給客戶端Random random = new Random();new Thread(() -> {try {while (true) {// 生成隨機(jī)的股票價(jià)格double price = 100 + random.nextDouble() * 10;// 構(gòu)造股票價(jià)格的消息String message = String.format("%.2f", price);// 發(fā)送消息給客戶端emitter.send(SseEmitter.event().data(message));// 休眠1秒鐘Thread.sleep(1000);}} catch (Exception e) {emitter.completeWithError(e);}}).start();return emitter;}
}
在上述代碼中,定義了一個(gè)streamStockPrice()
方法,該方法使用@GetMapping
注解將/stock-price
路徑映射到該方法上,并指定produces = MediaType.TEXT_EVENT_STREAM_VALUE
以表明該方法將產(chǎn)生SSE事件流。
在方法內(nèi)部創(chuàng)建了一個(gè)SseEmitter
對(duì)象作為事件發(fā)射器,并在一個(gè)單獨(dú)的線程中不斷生成隨機(jī)的股票價(jià)格,并將價(jià)格轉(zhuǎn)換為字符串形式發(fā)送給客戶端。
通過emitter.send()
方法發(fā)送的數(shù)據(jù)會(huì)被封裝為SSE事件流的形式,客戶端可以通過監(jiān)聽該事件流來實(shí)時(shí)接收股票價(jià)格。
在前端頁面中,創(chuàng)建一個(gè)簡單的HTML頁面來展示實(shí)時(shí)股票價(jià)格:
<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><title>實(shí)時(shí)股票價(jià)格監(jiān)控</title></head><body><h1>實(shí)時(shí)股票價(jià)格</h1><div id="stock-price"></div><script>const eventSource = new EventSource('/stock-price');eventSource.onmessage = function (event) {document.getElementById('stock-price').innerHTML = event.data;};</script></body>
</html>
上述代碼中,通過new EventSource('/stock-price')
創(chuàng)建了一個(gè)EventSource
對(duì)象,它與/stock-price
路徑建立SSE連接。然后,通過eventSource.onmessage
定義了接收消息的回調(diào)函數(shù),在收到新消息時(shí)更新頁面上的股票價(jià)格。
通過以上代碼,可以在瀏覽器中打開該HTML頁面,它會(huì)建立與服務(wù)器的SSE連接,并實(shí)時(shí)接收并展示股票價(jià)格。這只是使用SSE實(shí)現(xiàn)實(shí)時(shí)數(shù)據(jù)推送的一個(gè)簡單示例。在實(shí)踐中,可以根據(jù)具體的業(yè)務(wù)需求和場景,進(jìn)行更復(fù)雜和豐富的實(shí)現(xiàn)。
小結(jié)
SSE(Server-Sent Events)是一種基于HTTP協(xié)議的輕量級(jí)實(shí)時(shí)通信技術(shù),具備服務(wù)端推送、斷線重連和簡單輕量等優(yōu)點(diǎn)。然而,它也存在一些限制,例如無法進(jìn)行雙向通信、連接數(shù)受限以及僅支持GET請(qǐng)求等。
在Web應(yīng)用程序中,SSE可以實(shí)現(xiàn)各種即時(shí)數(shù)據(jù)推送功能,如股票在線數(shù)據(jù)更新、日志推送、實(shí)時(shí)顯示聊天室人數(shù)等。
然而,需要注意的是,并非所有的實(shí)時(shí)推送場景都適合使用SSE。在需要處理高并發(fā)、高吞吐量和低延遲的場景下,WebSocket可能是更好的選擇。而對(duì)于那些需要輕量級(jí)推送解決方案的場景,SSE可能會(huì)更加適合。
因此,在選擇實(shí)時(shí)更新方案時(shí),我們需要根據(jù)具體的需求和應(yīng)用場景來做出決策。只有這樣,我們才能確保選擇的技術(shù)能夠最大程度地滿足我們的需求。