日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

解密 SSE,像 ChatGPT 一樣返回流式響應(yīng)

來源: 責(zé)編: 時間:2023-11-20 08:57:07 269觀看
導(dǎo)讀我們知道目前的 HTTP/1.1 采用的是標準的請求-響應(yīng)模型,客戶端主動發(fā)請求,服務(wù)端被動地返回響應(yīng)。這種模型在客戶端需要實時獲取結(jié)果的場景下是不合適的,因為這意味著客戶端需要不斷地輪詢,所以最好的做法是服務(wù)端生成結(jié)

我們知道目前的 HTTP/1.1 采用的是標準的請求-響應(yīng)模型,客戶端主動發(fā)請求,服務(wù)端被動地返回響應(yīng)。這種模型在客戶端需要實時獲取結(jié)果的場景下是不合適的,因為這意味著客戶端需要不斷地輪詢,所以最好的做法是服務(wù)端生成結(jié)果之后,主動推送給客戶端。AAJ28資訊網(wǎng)——每日最新資訊28at.com

比如 ChatGPT,它在生成內(nèi)容時,也是生成一部分,就主動向客戶端推送一部分。而在這個過程中,客戶端不需要做任何事情,只需等待 ChatGPT 服務(wù)端返回內(nèi)容即可。AAJ28資訊網(wǎng)——每日最新資訊28at.com

說到這兒,你肯定想到了 WebSocket,沒錯這是一種解決方案。但 WebSocket 太重了,它和 HTTP 都是基于 TCP 的應(yīng)用層傳輸協(xié)議,只不過在握手的時候搭了 HTTP 的便車,利用 HTTP 本身的協(xié)議升級特性,偽裝成 HTTP,這樣就能繞過瀏覽器沙箱、網(wǎng)絡(luò)防火墻等限制。AAJ28資訊網(wǎng)——每日最新資訊28at.com

當(dāng)完成握手之后,后續(xù)傳輸?shù)臄?shù)據(jù)就不再是 HTTP 報文,而是 WebSocket 格式的二進制幀。所以這兩者完全是不同的協(xié)議,那有沒有一種辦法,我們?nèi)匀皇褂?HTTP 協(xié)議,同時還能讓服務(wù)端主動推送數(shù)據(jù)呢?AAJ28資訊網(wǎng)——每日最新資訊28at.com

答案是有的,也就是本文將要介紹的 SSE 技術(shù),它的英文全稱是 Server-Sent Events(服務(wù)端推送事件)。通過 SSE 可以讓服務(wù)端即時推送數(shù)據(jù)到客戶端,而不需要客戶端輪詢服務(wù)端以獲取更新。AAJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片AAJ28資訊網(wǎng)——每日最新資訊28at.com

到這你可能會問,那 WebSocket 和 SSE 有什么區(qū)別呢?AAJ28資訊網(wǎng)——每日最新資訊28at.com

1)通信方式

WebSocket 提供全雙工通信,服務(wù)端和客戶端都可以在同一個連接上同時發(fā)送和接收數(shù)據(jù)。最重要的是,WebSocket 獨立于 HTTP 協(xié)議,盡管它開始于一個 HTTP 握手。AAJ28資訊網(wǎng)——每日最新資訊28at.com

SSE 僅提供服務(wù)端到客戶端的單向通信,客戶端不能通過 SSE 給服務(wù)端發(fā)信息。AAJ28資訊網(wǎng)——每日最新資訊28at.com

2)協(xié)議和實現(xiàn)

WebSocket 使用自己的協(xié)議(ws:// 或 wss://),需要服務(wù)端和客戶端都支持,并且協(xié)議比較復(fù)雜。AAJ28資訊網(wǎng)——每日最新資訊28at.com

SSE 則是使用標準的 HTTP 協(xié)議,實現(xiàn)起來更簡單,尤其是在服務(wù)端。AAJ28資訊網(wǎng)——每日最新資訊28at.com

3)適用場景

WebSocket 適用于服務(wù)端和客戶端之間雙向?qū)崟r通信的場景,如在線游戲、聊天應(yīng)用等。AAJ28資訊網(wǎng)——每日最新資訊28at.com

SSE 適用于服務(wù)端向客戶端單向推送數(shù)據(jù)的場景,如消息通知、數(shù)據(jù)更新。并且 SSE 自動支持斷線重連,而 WebSocket 則需要額外部署。AAJ28資訊網(wǎng)——每日最新資訊28at.com

4)復(fù)雜性和資源使用

WebSocket 由于其雙向通信的能力,通常比 SSE 更復(fù)雜,可能需要更多的資源來維護和管理連接。AAJ28資訊網(wǎng)——每日最新資訊28at.com

SSE 因為其單向性和基于 HTTP 的特性,它可以利用現(xiàn)有的網(wǎng)絡(luò)基礎(chǔ)設(shè)施,如代理服務(wù)器、負載均衡器和防火墻等等,通常更容易實現(xiàn)和維護。AAJ28資訊網(wǎng)——每日最新資訊28at.com


AAJ28資訊網(wǎng)——每日最新資訊28at.com

相信現(xiàn)在你已經(jīng)明白 SSE 是做什么的了,它的目的就是讓服務(wù)端能夠主動推送數(shù)據(jù)給客戶端。如果不需要和服務(wù)端動態(tài)交互,只是希望服務(wù)端在有數(shù)據(jù)的時候推過來,那么 WebSocket 就有些太重了,因為這意味著要替換 HTTP 協(xié)議,而使用 SSE 無疑是更好的選擇。AAJ28資訊網(wǎng)——每日最新資訊28at.com

SSE 是什么我們已經(jīng)知道了,那它是怎么實現(xiàn)的呢?原理是什么呢?AAJ28資訊網(wǎng)——每日最新資訊28at.com

1)建立連接

客戶端發(fā)起一個標準的 HTTP 請求來開啟 SSE 會話,這個請求的特殊之處在于它包含一個頭字段。AAJ28資訊網(wǎng)——每日最新資訊28at.com

Accept: text/event-stream

相當(dāng)于客戶端告訴服務(wù)端,期望接收 SSE 消息流。而服務(wù)端在看到該字段時,也知道這是一個 SSE 請求,于是立即向客戶端返回響應(yīng)頭,注意:返回的只有響應(yīng)頭,里面會包含如下頭字段。AAJ28資訊網(wǎng)——每日最新資訊28at.com

Content-Type: text/event-stream

響應(yīng)頭返回之后標志著 SSE 連接成功建立,并且連接會保持開放狀態(tài),服務(wù)端后續(xù)可以隨時通過此連接向客戶端發(fā)送數(shù)據(jù)。此外當(dāng)連接不小心斷開時,客戶端也會自動進行重連。AAJ28資訊網(wǎng)——每日最新資訊28at.com

所以在普通的 HTTP 請求中,一旦服務(wù)端返回,那么請求結(jié)束了。雖然可以將 Connection 頭字段設(shè)置為 keep-alive 保證連接不斷開,但每次訪問都包含了 HTTP 請求/響應(yīng)的完整過程。AAJ28資訊網(wǎng)——每日最新資訊28at.com

而在 SSE 中,服務(wù)端會保持一個開放的連接,只要有新數(shù)據(jù)可用,就會直接發(fā)送給客戶端。所以服務(wù)端會將響應(yīng)以流的形式發(fā)送給客戶端,每次發(fā)送的消息都是響應(yīng)流的一部分,而不是獨立的 HTTP 響應(yīng)。AAJ28資訊網(wǎng)——每日最新資訊28at.com

因此 SSE 的服務(wù)端在發(fā)送數(shù)據(jù)時,并不遵循傳統(tǒng)的一次請求,一次響應(yīng)模式。它在建立連接之后會保持連接開放,并通過這個持續(xù)的連接流式地發(fā)送數(shù)據(jù),這種方式就使得 SSE 非常適合實時數(shù)據(jù)推送的場景。AAJ28資訊網(wǎng)——每日最新資訊28at.com

2)發(fā)送消息

客戶端發(fā)送請求,服務(wù)端返回響應(yīng)頭之后,SSE 連接就建立成功了。此時客戶端只需要躺平,安靜地等待服務(wù)端的輸出即可。所以現(xiàn)在的關(guān)鍵就在于服務(wù)端要返回什么格式的數(shù)據(jù)呢?很簡單,一個基本的消息由以下幾部分組成:AAJ28資訊網(wǎng)——每日最新資訊28at.com

  • data:實際的消息數(shù)據(jù);
  • id:可選,消息的唯一標識符,用于在連接重新建立時同步消息;
  • event:可選,定義事件類型,用于客戶端區(qū)分消息的類型;
  • retry:可選,自動重連的時間(毫秒),如果連接中斷,客戶端在自動重新連接之前,需要等待多長時間;

注意:每個消息要以兩個換行符(/n/n)結(jié)束,舉個例子,我們發(fā)送一個 Hello World。AAJ28資訊網(wǎng)——每日最新資訊28at.com

data: Hello World/n/n

也可以發(fā)送帶有事件類型的消息:AAJ28資訊網(wǎng)——每日最新資訊28at.com

event: userUpdatedata: {"username": "Serpen", "age": 18}/n/n

還是比較簡單的,服務(wù)端可以保持連接并隨時發(fā)送更多數(shù)據(jù)。然后客戶端在收到時會進行處理,但不需要(也不能)對服務(wù)端作出任何回應(yīng),它只需要被動地接收來自服務(wù)端的數(shù)據(jù)即可。當(dāng)服務(wù)端認為數(shù)據(jù)已經(jīng)全部發(fā)送完畢、無需再發(fā)時,那么便可以主動斷開連接。AAJ28資訊網(wǎng)——每日最新資訊28at.com

關(guān)于 SSE 的原理我們就解釋清楚了,下面來實際編程實現(xiàn)它,這里我們先使用原生的 asyncio 實現(xiàn) SSE。AAJ28資訊網(wǎng)——每日最新資訊28at.com

import asynciofrom asyncio import StreamReader, StreamWriterclass SSE:    def __init__(self, host="0.0.0.0", port=9999):        self.host = host        self.port = port    @staticmethod    def parse_request_headers(data: bytes) -> dict:        """        此函數(shù)負責(zé)從原始字節(jié)流中解析出請求頭        """        headers = data.split(b"/r/n/r/n")[0].split(b"/r/n")        header_dict = {}        for header in headers[1:]:            key, val = header.decode("utf-8").split(":", 1)            header_dict[key.lower()] = val.strip()        return header_dict    async def handler_requests(self,                               reader: StreamReader,                               writer: StreamWriter):        """        負責(zé)處理來自客戶端的請求        每來一個客戶端連接,就會基于此函數(shù)創(chuàng)建一個協(xié)程        并且自動傳遞兩個參數(shù):reader 和 writer        reader.read  負責(zé)讀取數(shù)據(jù),等價于 socket.recv        writer.write 負責(zé)發(fā)送數(shù)據(jù),等價于 socket.send        """        # 獲取客戶端的請求報文,這里對請求方法、請求地址不做限制        data = await reader.readuntil(b"/r/n/r/n")        # 解析出請求頭        request_headers = self.parse_request_headers(data)        # 簡單檢測一下 accept 字段,如果不是建立 SSE,那么直接關(guān)閉連接        if request_headers.get("accept") != "text/event-stream":            writer.close()            return await writer.wait_closed()        # 如果是 SSE 連接,那么返回響應(yīng)頭        response_header = (            b"HTTP/1.1 200 OK/r/n"            b"Content-Type: text/event-stream/r/n"            b"Cache-Control: no-cache/r/n"            b"Connection: keep-alive/r/n"            b'Access-Control-Allow-Origin: */r/n'            b"/r/n"        )        writer.write(response_header)        await writer.drain()        # 然后便可以不斷地向客戶端返回數(shù)據(jù)了        for _ in range(5):            # 每隔 1 秒返回數(shù)據(jù)            data = "data: 高老師總能分享出好東西/r/n/r/n".encode("utf-8")            writer.write(data)            await writer.drain()            await asyncio.sleep(1)        # 數(shù)據(jù)傳輸完畢        writer.close()        await writer.wait_closed()    async def __create_server(self):        # 創(chuàng)建服務(wù),第一個參數(shù)是一個回調(diào)函數(shù)        # 當(dāng)連接過來的時候就會根據(jù)此函數(shù)創(chuàng)建一個協(xié)程        # 后面是綁定的 ip 和 端口        server = await asyncio.start_server(self.handler_requests,                                            self.host,                                            self.port)        # 然后開啟無限循環(huán)        async with server:            await server.serve_forever()    def run_server(self):        loop = asyncio.get_event_loop()        loop.run_until_complete(self.__create_server())if __name__ == '__main__':    sse = SSE()    sse.run_server()

服務(wù)端代碼編寫完畢,下面編寫前端代碼。AAJ28資訊網(wǎng)——每日最新資訊28at.com

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>Title</title>    <style>        #data {            font-weight: bold;            color: cadetblue;            font-size: large;        }    </style></head><body>    <h1>SSE Test</h1>    <div id="data"></div>    <script>        document.addEventListener("DOMContentLoaded", function () {            // 和服務(wù)端建立 SSE 連接            var eventSource = new EventSource("http://localhost:9999");            eventSource.onmessage = function (e) {                // 將數(shù)據(jù)渲染在 <div id="data"></div> 的內(nèi)部                var data = e.data + "/n";                document.getElementById('data').innerText += data;            };            eventSource.onerror = function (e) {                console.error('Error occurred:', e);                eventSource.close();            };        });    </script></body></html>

代碼編寫完畢,我們用瀏覽器打開 HTML 文件,便可看到如下效果。AAJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片AAJ28資訊網(wǎng)——每日最新資訊28at.com

以上我們就簡單實現(xiàn)了 SSE,當(dāng)然為了加深印象,這里的后端是使用原生的 asyncio 編寫的,但在工作中,我們會使用現(xiàn)成的 Web 框架,比如 FastAPI,Blacksheep 等等。AAJ28資訊網(wǎng)——每日最新資訊28at.com

需要說明的是,雖然通過 SSE 技術(shù)可以實現(xiàn)類似 ChatGPT 的效果,但 ChatGPT 內(nèi)部并沒有用到 SSE,它內(nèi)部是基于 HTTP 的分塊傳輸實現(xiàn)的。因為 SSE 只能通過 GET 請求發(fā)出,并且無法自定義請求頭。AAJ28資訊網(wǎng)——每日最新資訊28at.com

如果想實現(xiàn) ChatGPT 的效果,需要使用 HTTP 的分塊傳輸。而像 FastAPI、BlackSheep 等框架提供的流式響應(yīng),便是基于 HTTP 的分塊傳輸實現(xiàn)的,比如 FastAPI:AAJ28資訊網(wǎng)——每日最新資訊28at.com

import asynciofrom fastapi import FastAPIfrom fastapi.responses import StreamingResponsefrom fastapi.middleware.cors import CORSMiddlewareimport uvicornapp = FastAPI()app.add_middleware(    CORSMiddleware,    allow_origins=["*"],    allow_methods=["*"],    allow_headers=["*"],)async def event_generator():    for _ in range(5):        # 每隔 1 秒返回數(shù)據(jù)        data = "data: 高老師總能分享出好東西/r/n/r/n".encode("utf-8")        yield data        await asyncio.sleep(1)@app.get("/")async def sse():    return StreamingResponse(event_generator(),                              media_type="text/event-stream")if __name__ == '__main__':    uvicorn.run(app, host="0.0.0.0", port=9999)

首先之前的前端代碼依舊可以正常訪問,通過修改數(shù)據(jù)格式和 Content-Type 可以讓其支持 SSE。但最正確的做法是直接訪問 localhost:9999,效果如下:AAJ28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片AAJ28資訊網(wǎng)——每日最新資訊28at.com

所以基于 StreamingResponse 可以實現(xiàn) SSE,也可以直接訪問。而直接訪問的話,此時里面的 data: 和 /r/n 就是實體數(shù)據(jù)的一部分。并且這種方式和 ChatGPT 的工作機制是相似的,都使用了 HTTP 的分塊傳輸,支持所有的請求方法,而 SSE 只支持 GET 請求。AAJ28資訊網(wǎng)——每日最新資訊28at.com

BlackSheep 也是類似的,它同樣也支持流式響應(yīng)。AAJ28資訊網(wǎng)——每日最新資訊28at.com

import asynciofrom blacksheep import Application, Response, StreamedContentimport uvicornapp = Application()app.use_cors(    allow_origins=["*"],    allow_methods=["*"],    allow_headers=["*"],)async def event_generator():    for _ in range(5):        # 每隔 1 秒返回數(shù)據(jù)        data = "data: 高老師總能分享出好東西/r/n/r/n".encode("utf-8")        yield data        await asyncio.sleep(1)@app.router.get("/")async def sse():    return Response(        200,        cnotallow=StreamedContent(b"text/event-stream", event_generator),    )if __name__ == '__main__':    uvicorn.run(app, host="0.0.0.0", port=9999)

可以測試一下,效果是一樣的。如果你不想實現(xiàn) SSE,只是希望固定的數(shù)據(jù)以流的形式一點一點返回,那么記得將數(shù)據(jù)中多余的 data: 和 /r/n 給去掉,并最好修改 Content-Type 為合適的類型。AAJ28資訊網(wǎng)——每日最新資訊28at.com

所以 SSE 一般用于需要服務(wù)端推數(shù)據(jù),但數(shù)據(jù)不知道什么時候會過來,于是通過 SSE 保持連接開放。后續(xù)當(dāng)服務(wù)端有數(shù)據(jù)了,直接通過連接發(fā)送給客戶端即可。AAJ28資訊網(wǎng)——每日最新資訊28at.com

而 FastAPI 和 BlackSheep 提供的流式響應(yīng)更像是,返回的數(shù)據(jù)比較龐大,如果全部準備好再一次性返回,會讓用戶陷入長時間的等待,造成不好的體驗。于是通過分塊傳輸,準備好一部分就返回一部分。雖然整體時間沒變,但可以讓用戶立刻獲取到數(shù)據(jù),從而提升用戶體驗。AAJ28資訊網(wǎng)——每日最新資訊28at.com

比如 ChatGPT,當(dāng)它回答的內(nèi)容比較多的時候,那么整個過程耗費幾十秒鐘是常有的事情,假設(shè) 30 秒。相比讓用戶等待 30 秒,然后內(nèi)容一下子刷出來,顯然生成一部分返回一部分這種方式更讓人喜歡。AAJ28資訊網(wǎng)——每日最新資訊28at.com

因此使用 SSE 還是流式響應(yīng),則取決于你當(dāng)前的業(yè)務(wù)。如果你返回的數(shù)據(jù)是確定的,只是準備的時間比較長,或者數(shù)據(jù)量比較大,那么推薦使用流式響應(yīng)。AAJ28資訊網(wǎng)——每日最新資訊28at.com

至于 SSE,在這些現(xiàn)成的 Web 框架里面,也可以通過流式響應(yīng)來實現(xiàn),只需要將 Content-Type 設(shè)置為 text/event-stream,并將數(shù)據(jù)加上前綴 data: 和后綴 /r/n/r/n。AAJ28資訊網(wǎng)——每日最新資訊28at.com

但說實話,如果想實現(xiàn) SSE,不建議通過流式響應(yīng)來實現(xiàn),而是使用專門的庫。以 FastAPI 為例:AAJ28資訊網(wǎng)——每日最新資訊28at.com

from sse_starlette.sse import EventSourceResponse

FastAPI 其實就是在 starlette 的基礎(chǔ)上套了一層殼,通過安裝 sse_starlette 可以讓 FastAPI 更好地支持 SSE。AAJ28資訊網(wǎng)——每日最新資訊28at.com

以上就是本文的內(nèi)容,如果對你有幫助,就點個贊吧。AAJ28資訊網(wǎng)——每日最新資訊28at.com

本文鏈接:http://www.www897cc.com/showinfo-26-30985-0.html解密 SSE,像 ChatGPT 一樣返回流式響應(yīng)

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: Spring Boot中實現(xiàn)訂單30分鐘自動取消的策略思路及源代碼

下一篇: Vue3問題:如何實現(xiàn)微信掃碼授權(quán)登錄?

標簽:
  • 熱門焦點
  • 鴻蒙OS 4.0公測機型公布:甚至連nova6都支持

    華為全新的HarmonyOS 4.0操作系統(tǒng)將于今天下午正式登場,官方在發(fā)布會之前也已經(jīng)正式給出了可升級的機型產(chǎn)品,這意味著這些機型會率先支持升級享用。這次的HarmonyOS 4.0支持
  • 7月安卓手機性能榜:紅魔8S Pro再奪榜首

    7月份的手機市場風(fēng)平浪靜,除了紅魔和努比亞帶來了兩款搭載驍龍8Gen2領(lǐng)先版處理器的新機之外,別的也想不到有什么新品了,這也正常,通常6月7月都是手機廠商修整的時間,進入8月份之
  • 在線圖片編輯器,支持PSD解析、AI摳圖等

    自從我上次分享一個人開發(fā)仿造稿定設(shè)計的圖片編輯器到現(xiàn)在,不知不覺已過去一年時間了,期間我經(jīng)歷了裁員失業(yè)、面試找工作碰壁,寒冬下一直沒有很好地履行計劃.....這些就放在日
  • 梁柱接棒兩年,騰訊音樂闖出新路子

    文丨田靜 出品丨牛刀財經(jīng)(niudaocaijing)7月5日,企鵝FM發(fā)布官方公告稱由于業(yè)務(wù)調(diào)整,將于9月6日正式停止運營,這意味著騰訊音樂長音頻業(yè)務(wù)走向消亡。騰訊在長音頻領(lǐng)域還在摸索。為
  • 認真聊聊東方甄選:如何告別低垂的果實

    來源:山核桃作者:財經(jīng)無忌爆火一年后,俞敏洪和他的東方甄選依舊是頗受外界關(guān)心的&ldquo;網(wǎng)紅&rdquo;。7月5日至9日,為期5天的東方甄選&ldquo;甘肅行&rdquo;首次在自有App內(nèi)直播,
  • 微博大門常打開,迎接海外畫師漂洋東渡

    作者:互聯(lián)網(wǎng)那些事&ldquo;起猛了,我能看得懂日語了&rdquo;。&ldquo;為什么日本人說話我能聽懂?&rdquo;&ldquo;中文不像中文,日語不像日語,但是我竟然看懂了&rdquo;&hellip;&hell
  • 信通院:小米、華為等11家應(yīng)用商店基本完成APP簽名及驗簽工作

    中國信通院表示,目前,小米、華為、OPPO、vivo、360手機助手、百度手機助手、應(yīng)用寶、豌豆莢和努比亞等9家應(yīng)用商店,以及抖音和快手2家新型應(yīng)用分發(fā)平
  • 首發(fā)天璣9200+ iQOO Neo8系列發(fā)布首銷售價2299元起

    2023年5月23日晚,iQOO Neo8系列正式發(fā)布。其中,Neo系列首款Pro之作——iQOO Neo8 Pro強悍登場,限時售價3099元起;價位段最強性能手機iQOO Neo8同期上市
  • iQOO Neo8系列今日官宣:首發(fā)天璣9200+ 全球安卓最強芯!

    在昨日舉行的的聯(lián)發(fā)科新一代旗艦芯片天璣9200+的發(fā)布會上,iQOO官方也正式宣布,全新的iQOO Neo8系列新品將全球首發(fā)搭載這款當(dāng)前性能最強大的移動平臺
Top 主站蜘蛛池模板: 萝北县| 黎城县| 玉门市| 桦甸市| 汝南县| 商水县| 丘北县| 莱芜市| 荣成市| 泸溪县| 偏关县| 龙井市| 龙口市| 萝北县| 寿宁县| 德格县| 正定县| 灵川县| 元江| 枣强县| 七台河市| 东光县| 清新县| 鹤庆县| 江口县| 射洪县| 卢龙县| 中宁县| 越西县| 江门市| 吉木萨尔县| 石楼县| 资中县| 无锡市| 南京市| 莫力| 武汉市| 泸州市| 栖霞市| 汽车| 邳州市|