首页 > 其他分享 >SSE post 实践

SSE post 实践

时间:2024-09-25 14:12:52浏览次数:10  
标签:const 实践 response sse done SSE post true event

SSE post 实践

头像 assassin_cike   5 月 17 日 新加坡 阅读 5 分钟 1  

需求:对接大模型的聊天功能
疑惑:但是接口是post方法,需要传一些复杂的数据,而EventSource不支持post,那我们应该怎么办呢?
思路:SSE (Server-Sent Events) Using A POST Request Without EventSource
办法:用fetch的post
实验:sse-demo

客户端

async function fetchData() {
  const response = await fetch("/sse", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      user_id: 123,
    }),
  });
  const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;
    console.log("Received:", value);
  }
}

webpack.config.js 配置代理

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  entry: "./index.js",
  output: {
    path: path.resolve(__dirname, "dist"),
    filename: "[name].bundle.js",
    clean: true,
  },
  optimization: {
    runtimeChunk: "single",
  },
  plugins: [
    new HtmlWebpackPlugin({
      title: "Development",
      template: "index.html",
    }),
  ],
  devtool: "inline-source-map",
  devServer: {
    port: 8345,
    static: "./dist",
    proxy: [
      {
        context: ["/sse"],
        target: "http://127.0.0.1:3333",
        changeOrigin: true,
      },
    ],
  },
};

node.js服务

// server.js
const http = require("http");
// Create a HTTP server
const server = http.createServer((req, res) => {
  // Check if the request path is /stream
  if (req.url === "/sse") {
    // Set the response headers
    res.writeHead(200, {
      "Content-Type": "text/event-stream",
      "Cache-Control": "no-cache",
      "Access-Control-Allow-Origin": "*",
      "Access-Control-Allow-Headers": "Content-type",
      "Access-Control-Allow-Methods": "DELETE,PUT,POST,GET,OPTIONS",
      Connection: "keep-alive",
    });
    // Create a counter variable
    let counter = 0;
    // Create an interval function that sends an event every second
    const interval = setInterval(() => {
      // Increment the counter
      counter++;
      // Create an event object with name, data, and id properties
      const event = {
        name: "message",
        data: `Hello, this is message number ${counter}`,
        id: counter,
      };
      // Convert the event object to a string
      const eventString = `event: ${event.name}\ndata: ${event.data}\nid: ${event.id}\n\n`;
      // Write the event string to the response stream
      res.write(eventString);
      // End the response stream after 10 events
      if (counter === 10) {
        clearInterval(interval);
        res.end();
      }
    }, 1000);
  } else {
    // Handle other requests
    res.writeHead(404);
    res.end("Not found");
  }
});

server.listen(3333, () => {
  console.log("Server listening on port 3333");
});

启动服务,点击button触发fetchData函数,发现服务端的数据并不是流式输出到客户端的,而是等所有数据准备好后一次性返回给了客户端,这不是我想要的,排查,SSE doen't work with vue-cli devServer proxy,于是改了webpack配置

  devServer: {
    port: 8345,
    static: "./dist",
    compress: false,
    proxy: [
      {
        context: ["/sse"],
        target: "http://127.0.0.1:3333",
        changeOrigin: true,
        ws: true,
      },
    ],
  },

新增了compress: falsews: true,,再次发送请求,数据一个个被吐出来了,但是到底是哪个参数起了作用,经过测试证明是compress: false的作用。但是公司的项目使用的umijs@4没有这个配置项,搜,umijs4由于无法配置decServer中的compress属性 导致无法实时输出sse请求的数据,将UMI_DEV_SERVER_COMPRESS=none umi dev配置好还是无法输出,原来我的umijs版本不够新,只好升级到最新npm update umi,但是还是不能流式输出,问题到底在哪?还有一点后端返回的数据并没有出现在EventSream面板,而我的demo的数据跟竞品的数据都会出现在该面板,如下
image.png
从而推断是接口返回的数据不对,原来返回的数据要有固定的格式,Server-Sent Events 教程告诉后端数据要被包裹在data: json数据 \n\n之中,从而数据流式的出现在了EventSream面板,但是我的控制台打印出来的数据的个数跟EventSream面板的数据不一致且有很多重复的数据格式出现在一个输出字符串里,又开始怀疑后端的数据有问题,仔细推敲,已经正确的流式的输出在了EventSream面板,应该不是接口的问题,而是我解析的问题,尝试eventsource-parser

export const useSendMessageWithSse = (
  url: string = api.completeConversation,
) => {
  const [answer, setAnswer] = useState<IAnswer>({} as IAnswer);
  const [done, setDone] = useState(true);

  const send = useCallback(
    async (body: any) => {
      try {
        setDone(false);
        const response = await fetch(url, {
          method: 'POST',
          headers: {
            [Authorization]: getAuthorization(),
            'Content-Type': 'application/json',
          },
          body: JSON.stringify(body),
        });

        const reader = response?.body
          ?.pipeThrough(new TextDecoderStream())
          .pipeThrough(new EventSourceParserStream())
          .getReader();

        while (true) {
          const x = await reader?.read();
          if (x) {
            const { done, value } = x;
            try {
              const val = JSON.parse(value?.data || '');
              const d = val?.data;
              if (typeof d !== 'boolean') {
                console.info('data:', d);
                setAnswer(d);
              }
            } catch (e) {
              console.warn(e);
            }
            if (done) {
              console.info('done');
              break;
            }
          }
        }
        console.info('done?');
        setDone(true);
        return response;
      } catch (e) {
        setDone(true);
        console.warn(e);
      }
    },
    [url],
  );

  return { send, answer, done };
};

这次终于得到了正确的结果,不容易啊。eventsource-parser/stream作用是将sse接口返回的字符串转为对象且避免了debug断点时接口不间断返回的数据被塞到一个字符串的问题,完整的测试代码在,sse-demo

4fa1aee3c4aa891123447455b1578ae.png

参考:
Event Streaming Made Easy with Event-Stream and JavaScript Fetch

react.jsumissehttpwebpack

标签:const,实践,response,sse,done,SSE,post,true,event
From: https://www.cnblogs.com/sexintercourse/p/18431245

相关文章

  • 前端如何接收SSE流式数据传输(大模型网站常用)
    使用fetchEventSource参考:https://blog.csdn.net/qq_43750656/article/details/131591198https://zhuanlan.zhihu.com/p/686618062首先安装:npminstall--save@microsoft/fetch-event-source我参考各个资料写的函数://流式传输处理函数exportfunctionsseRequest(url:s......
  • Canvas简历编辑器-Monorepo+Rspack工程实践
    Canvas简历编辑器-Monorepo+Rspack工程实践 Canvas简历编辑器-Monorepo+Rspack工程实践在之前我们围绕Canvas聊了很多代码设计层面的东西,在这里我们聊一下工程实践。在之前的文中我也提到过,因为是本着学习的态度以及对技术的好奇心来做的,所以除了一些工具类的库例如 ArcoDe......
  • DAMODEL丹摩智算平台实践CogVideoX
    文章目录前言一、平台账号注册并登录二、部署CogVideoX(一)简介(二)部署1.创建实例2.配置环境和依赖3.预制模型与配置文件三、开始运行总结前言该文章主要记录DAMODEL丹摩智算平台实践过程与心得体会,本次实践的内容为CogVideoX-2b-部署与使用;DAMODEL平台地址为:丹......
  • Eureka原理实践:构建高可用、可扩展的微服务架构
    Eureka原理实践:构建高可用、可扩展的微服务架构引言随着微服务架构的日益普及,服务注册与发现成为了分布式系统架构中的核心组件。Eureka,作为Netflix开源的一款高效稳定的服务注册与发现框架,凭借其自动注册、发现、健康监测及自我保护等特性,在微服务架构中占据了重要地位。......
  • MongoDB 双活集群在运营商的实践
    在现代电信行业中,订单中心作为核心业务系统之一,承担着处理客户订单、管理订单状态、与各个业务系统进行交互等重要职责。其订单中心的高效运作直接关系到客户体验和业务连续性。为了满足不断增长的业务需求和日益复杂的运营环境,运营商需要自身的订单中心架构具备高可用性及强大的......
  • 守护天空安全的科技利剑 创智信安(IFMN)的创新实践
    无人机技术飞速发展,无人机的应用已经渗透到我们生活的方方面面,从航拍、农业、物流到紧急救援,它们无处不在。然而,随着无人机的普及,其潜在的安全威胁也日益凸显。为了应对这些挑战,无人机反制技术应运而生,成为守护天空安全的重要科技利剑。在这一领域,创智信安(IFMN)凭借其深厚的技术......
  • 接口测试工具postman
    1、下载postman的安装包2、下载好的安装包,点击打开3、介绍postman(1)左边创建一个集合creatanewcollection(2)点击加号创建接口修改post或get请求方式  输入URLbody-网址 key:账号、请求体的入参参数:密码(3)保存接口,存放到集合中 (4)send发送接口创建了一个接......
  • postman关联、fiddler断点
    fiddler可以做什么?抓包、弱网、断点一、关联接口:定义:拿上一个接口的返回参数,做下一个接口入参省份接口:http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/getSupportProvince城市接口:post http://www.webxml.com.cn/WebServices/WeatherWebService.asmx/get......
  • 【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLo
    文章目录【JUC并发编程系列】深入理解Java并发机制:线程局部变量的奥秘与最佳实践(五、ThreadLocal原理、对象之间的引用)1.基本API介绍2.简单用法3.应用场景4.Threadlocal与Synchronized区别5.内存溢出和内存泄漏5.2内存溢出(MemoryOverflow)5.2内存泄漏(Mem......
  • Selenium与数据库结合:数据爬取与存储的技术实践
    在当今的数据驱动时代,信息的获取与分析变得尤为重要。网络爬虫作为一种自动抓取互联网信息的程序,在数据收集中扮演了关键角色。Selenium,作为一个强大的自动化测试工具,不仅支持多种浏览器,还能模拟真实用户的行为,如点击、输入文本等,因此在处理动态网页时尤为有效。结合数据库技术,Sel......