细心的朋友会敏锐地发现ChatGPT网页给出的问题答案是句子。同样,ChatGPT后台Api接口请求中,如果将Stream参数设置为True,Api接口也可以实现和ChatGPT网页一样的流式返回,然后更快的给前端用户反馈,并且在同时,也可以缓解连接超时的问题。服务器发送事件(SSE)是一种用于启用从服务器到客户端的单向通信的协议。使用SSE,服务器可以在客户端不发出请求的情况下向客户端推送实时数据。SSE建立在HTTP协议之上,使用基于文本的数据格式(通常是JSON)进行通信。客户端通过创建一个EventSource对象与服务器建立连接,然后可以监听服务器发送的事件。服务端可以随时向客户端推送事件,客户端通过监听事件接收数据。ChatGPT的Server-sentevents应用首先打开ChatGPT网页,随机提问,然后进入网络菜单,清除历史请求记录,然后进行网络抓包和监控:可以看到触发回答按钮后,页面会向后向后端的backend-api/conversation对话接口发起请求,但是这个接口的通信方式不是传统的http接口或者Websocket持久链接协议,而是返回ChatGPT后端模型段的返回数据基于EventSteam的事件流进行细分。为什么ChatGPT会选择这种方式与后端Server通信?ChatGPT网页使用Server-sentevents通信,因为这种通信方式允许服务器向客户端推送数据,而无需客户端不断向服务器发送请求。这种推送模式提高了应用程序性能和响应能力,减少了不必要的网络流量。与WebSocket等其他实时通信协议相比,Server-sentevents通信是一种轻量级协议,易于实现和部署。此外,它还具有广泛的浏览器兼容性,无需特殊网络配置即可使用。在ChatGPT中,服务器会将新的聊天消息推送到网页,实时显示新的聊天内容。使用Server-sentevents通信,可以轻松实现这种实时更新的功能,保证网页与服务器之间的通信效率和稳定性。说白了,为了降低成本和提高效率,ChatGPT是一种基于深度学习的大规模语言模型,处理自然语言文本需要大量的计算资源和时间。所以返回响应的速度肯定比普通的数据库读取要慢很多。Http接口显然不适合,因为Http是一次性返回,等待时间太长,Websocket太重,因为全双工通信不适合这种单项对话的场景,so-称为单项对话场景,是指对话中的双方不会有并发对话,而是一个串行的问答逻辑。同时,持久链接也会占用服务器资源。要知道,ChatGPT几乎可以说是天天活跃。它是世界上用户数量最多的网络应用程序。在效率上,大型语言模型无法一次返回所有计算的数据,但是可以通过Server-sent事件将之前计算的数据“推送”到前端,这样用户就不会因为过于频繁而关闭页面等待时间长,所以ChatGPT的前端观感就像一台打字机,一块一块地返回答案。这种“边计算边返回”的生成器模式也提高了ChatGPT的答题效率。Python3.10实现Server-sentevents应用这里我们使用基于Python3.10的Tornado异步非阻塞框架来实现Server-sentevents通信。先安装Tornado框架pip3installtornado==6.1然后写sse\_server.py:importtornado.ioloopimporttornado.webpush_flag=TruefromasyncioimportsleepclassServerSentEvent(tornado.web.RequestHandler):def__init__(self,*args,**kwargs):super(ServerSentEvent,self).__init__(*args,**kwargs)self.set_header('Content-Type','text/event-stream')self.set_header('访问控制-Allow-Origin',"*")self.set_header("Access-Control-Allow-Headers","*")#请求方法self.set_header("Access-Control-Allow-Methods","*")#断开连接defon_finish(self):print("disconnect")returnsuper().on_finish()asyncdefget(self):print("establishlink")whileTrue:ifpush_flag:print("start")self。write("事件:消息\n");lf.write("数据:"+"推送数据"+"\n\n");self.flush()awaitsleep(2)创建一个推送路由类ServerSentEvent,它继承了Tornado内置的视图类tornado.web。RequestHandler,先使用super方法调用父类的初始化方法,设置跨域,如果不使用super,会重写父类的同名方法,然后异步get方法建立链接和推送消息,这里使用Python原生异步的写法,每两秒向前端推送一条事件消息,内容为推送数据。请注意,这只是一个简单的推送演示。实际场景中如果涉及到IO操作,比如数据库读写或者网络请求,需要单独封装异步包的方法。另外,这里假设前端onmessagehandler的事件名称是message。如果想使用其他事件名称,可以使用前端的addEventListener订阅事件,最后一条消息必须以两行换行结束。然后编写路由和服务实例:defmake_app():returntornado.web.Application([(r"/sse/data/",ServerSentEvent),])if__name__=="__main__":app=make_app()app.listen(8000)print("sseservicestart")tornado.ioloop.IOLoop.current().start()然后在后台运行命令:python3sse_server.py程序返回:PSC:\Users\liuyue\www\videosite>python.\sse_server.pysse服务启动至此,基于Tornado的Server-sentevents服务搭建完成。前端Vue.js3linkServer-senteventsserviceclient我们使用最流行的Vue.js3框架:sse_init:function(){varpush_data=newEventSource("http://localhost:8000/sse/data/")push_data.onopen=function(event){//打开事件console.log("EventSource连接成功");};push_data.onmessage=function(event){try{console.log(event);}catch(error){console.log('EventSource结束消息异常',error);}};push_data.onerror=function(error){console.log('EventSource连接异常',error);};}这是在前端的初始化方法中建立的EventSource实例,通过onmessage方法监听后端的主动推送:可以看到可以每两秒订阅一次后端的message事件推送的消息。同时SSE默认支持断开和重连,而全双工的WebSocket协议需要自己在前端实现,判断会判断。结论不仅可以实现ChatGPT的流式返回功能,SSE广泛应用于Web应用,如实时新闻推送、实时股票行情、在线游戏等。相比轮询和长轮询,SSE更高效,因为它只会在新数据到达时发送;同时SSE支持自定义事件和数据,具有更高的灵活性和复用性,为流式数据的回归保驾护航,ChatGPT的最爱,谁不爱?最后放上项目地址和大家一起欣赏:github.com/zcxey2911/sse\_tornado6\_vuejs3
