开发者社区 > 博文 > 长连接:chatgpt流式响应背后的逻辑
分享
  • 打开微信扫码分享

  • 点击前往QQ分享

  • 点击前往微博分享

  • 点击复制链接

长连接:chatgpt流式响应背后的逻辑

  • jd_482c4d7916506
  • 2023-07-24
  • IP归属:北京
  • 19920浏览

    一、前言:

    提起长连接,我们并不陌生,最常见的长连接非websocket莫属了。即使没有在项目中实际用过,至少也应该有所接触。长连接指在一次网络通信中,客户端与服务器之间建立一条持久的连接,可以在多次请求和响应中重复使用该连接。这种方式的优点是减少了连接建立和关闭的开销,提高了通信效率,但需要注意控制连接的数量,避免资源浪费。短连接则是每次请求和响应都建立一个新的连接,完成后立即关闭,需要频繁进行连接建立和关闭,效率相对较低。但是这种方式更加灵活,适用于请求量较小、请求频率不高的场景。


    二、背景:

    最近项目在引用chatgpt智能小助手,最开始采用的是当chatgpt回答完成后一次性返回答案。但这种方式受限于网络及服务较慢的原因导致用户需要等待较长时间,极大的降低了用户的使用体验。经过项目组成员商议决定采取答案逐字返回的形式,以便于用户能更快的得到反馈。


    关于长连接技术,主要考虑两种方案websocket和sse

    三、原理:

    1.websocket概念:WebSocket是HTML5定义的新协议,实现了服务器与客户端之间的全双工通信。WebSocket连接一旦建立,客户端和服务器端处于平等地位,可以相互发送数据,不存在请求和响应的区别。

    2、websocket优劣势:优势在于实现了双向通信,劣势在于服务器端的逻辑非常复杂。现在针对不同的后台语言有不同的插件可以使用。

    3、sse概念:SSE(Server-Sent Events)是HTML5新增的功能,允许服务器将数据推送到客户端。与长轮询和短轮询不同,SSE不需要客户端先发送请求,而是在服务器端数据有更新时立即发送到客户端


    4、sse优劣势:优势在于节约资源,提升应用性能。SSE可以实现只要服务器端数据有更新,就可以马上发送到客户端,不需要建立或保持大量的客户端发往服务器端的请求。另外,SSE的实现非常简单,并且不需要依赖其他插件。劣势在于不是双向通信,只能后台向前台推送。

    5、相同点:都是基于tcp,都是可靠的传输协议

    6、不同点

    • WebSocket是双向通信协议,模拟Socket协议,可以双向发送或接受信息
    • HTTP是单向的
    • WebSocket是需要浏览器和服务器握手进行建立连接的
    • 而http是浏览器发起向服务器的连接,服务器预先并不知道这个连接

    四、应用:

    1、sse在chatgpt中的应用

    前端代码

    import { fetchEventSource } from '@microsoft/fetch-event-source'
    let answerContent = ''
    fetchEventSource('/chatgptApi/chatgpt_qa_stream', {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json'
            },
            body: JSON.stringify({ messages }),
            async onopen(response) {
              if (response.ok && response.status === 200) {
                console.log('连接成功')
              } else {
                console.log('连接异常')
              }
            },
            async onmessage(event) {
              // 表示整体结束
              if (event.data === '[DONE]') {
                console.log('结束')
                return
              }
              if (event.data) {
                const data = JSON.parse(event.data)
                answerContent += data.content
              }
            },
            async onerror(error) {
              console.error('Error:', error)
            },
            async onclose() {
              console.log('关闭连接')
            }
          })
    

    后端代码

    const http = require('http');
    const yun = express();
    const eventServer = http.createServer((req, res) => {
        res.writeHead(200, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-cache',
            'Connection': 'keep-alive',
            'Access-Control-Allow-Origin': "*",
            'Access-Control-Allow-Headers': 'Content-Type,Content-Length,Authorization,Accept,X-Requested-With',
            'Access-Control-Allow-Methods': 'PUT,POST,GET,DELETE,OPTIONS'
        });
        setInterval(() => {
        	// 事件要用两个\n结束
            res.write('data: The server time is: ' + new Date() + '\n\n');
        }, 1000);
        req.connection.addListener('close', () => {
            console.log('SSE connection closed!');
        }, false);
    }).listen(4001);
    

    2、websockt在即时聊天中的应用

    前端代码

    // 创建WebSocket对象
    let ws = new WebSocket('ws://localhost:8888')
    
    // 连接成功后的回调函数
    ws.onopen = function (params) {
      console.log('客户端连接成功')
      // 向服务器发送消息
      ws.send('hello')
    };
    
    // 从服务器接受到信息时的回调函数
    ws.onmessage = function (e) {
      console.log('收到服务器响应', e.data)
    };
    
    // 连接关闭后的回调函数
    ws.onclose = function(evt) {
      console.log("关闭客户端连接");
    };
    
    // 连接失败后的回调函数
    ws.onerror = function (evt) {
      console.log("连接失败了");
    };
    // 监听窗口关闭事件,当窗口关闭时,主动去关闭websocket连接,防止连接还没断开就关闭窗口,这样服务端会抛异常。
    window.onbeforeunload = function() {
        ws.close();
    }
    

    后端代码

    / 引入插件
    const ws = require('nodejs-websocket')
    // 只要有用户链接,函数就会执行,会给当前链接的用户创建一个connect对象
    const server = ws.createServer((connect)=>{
        console.log('连接成功')
        // console.log(connect)
        // 注册text事件 ,接收用户传递过来的数据
        connect.on('text',data=>{
            console.log('接收客户端数据---->', data)
            // 给所有用户发送消息
            broadcast(server,data+"--返回数据")
        })
        // 连接断开,触发事件close
        connect.on('close',()=>{
            console.log('用户链接断开--close')
        })
        // 用户链接断开
        connect.on('error',err=>{
            console.log('err', err)
        })
    
    }).listen(3001,()=>{
        console.log('websocket服务启动成功了')
    })
    
    // 给所有人发消息
    function broadcast(server,msg){
        server.connections.forEach(element => {
            element.send(msg)
        });
    }
    

    五、效果:

    sse在chatgpt案例中的应用