umi icon indicating copy to clipboard operation
umi copied to clipboard

[Feature Request] 代理配置里,能否提供一个是否进行压缩的配置项

Open WangKB opened this issue 11 months ago • 3 comments

Background

Hello,首先感谢umijs团队对开源界的付出和努力。 我最近在做一个类似chatgpt的网页聊天应用,需要使用HTTP的SSE特性实现文本流式输出的功能。但是在使用umijs提供的代理功能对服务端的sse接口进行代理之后,接口会被阻塞,直到所有的event的都处理结束,才一次性返回到页面端。

即,正常的情况下,sse请求应该是如下的情况:

事件 时间
发起请求 00:00:00
事件1 00:00:01
事件2 00:00:03
事件3 00:00:05
结束 00:00:05

实际在使用了代理后,sse变成了如下情况:

事件 时间
发起请求 00:00:00
事件1 00:00:05
事件2 00:00:05
事件3 00:00:05
结束 00:00:05

这样用户实际上还是要等到所有事件处理完后才能得到响应,失去了sse的意义。

我搜索了其他issue,也有其他人遇到了同样的问题: https://github.com/umijs/umi/issues/11958 https://github.com/umijs/umi/issues/11453 但是好像没有人给出直接问题原因所在。

Proposal

根据我的研究,问题出在umi代码中,bundler-webpack/src/server/server.ts的48行: server.ts 即: app.use(require('@umijs/bundler-webpack/compiled/compression')()); 这里为代理服务器的express实例引入了压缩中间件,并且没有提供配置来避免引入这个中间件。而强制压缩,会导致sse的事件不会如预期的流式输出,而是等到所有所有事件都到达,并且完成压缩后才传递到页面。 目前我有两个解决方案, 一个是自己自定义一个插件来修改express的app实例,如下

api.onBeforeMiddleware(({app}) => {
  console.log(app._router.stack);
  const compressionMiddleware = app._router.stack.findIndex((layer: any) => {
    return layer.name === 'compression';
  });
  app._router.stack.splice(compressionMiddleware, 1);
});

另一个方案是,手动删除这一行来保证我的sse请求正常执行。 但我觉得这个两个方法的侵入性都太强了,尤其第二个。所以希望umijs官方能否提供一个类似webpack里devServer的compress配置项,来把这个压缩的逻辑交给用户来决定。

WangKB avatar Feb 26 '24 03:02 WangKB

表示有同样的需求

consistent-k avatar Feb 27 '24 06:02 consistent-k

是的,欢迎 PR 提供一个 devServer.compress 的选项用来关闭 compress 插件,或者做一个环境变量判断 if (process.env.UMI_DEV_SERVER_COMPRESS !== 'none') 来关闭 compress 插件。

综合 issue 来看,有这个需求的场景仅限于开发时的 sse 传输不达预期,而生产构建后和 dev server 无关,所以只是很少数人的需求。

fz6m avatar Feb 29 '24 05:02 fz6m

是的,欢迎 PR 提供一个 devServer.compress 的选项用来关闭 compress 插件,或者做一个环境变量判断 if (process.env.UMI_DEV_SERVER_COMPRESS !== 'none') 来关闭 compress 插件。

综合 issue 来看,有这个需求的场景仅限于开发时的 sse 传输不达预期,而生产构建后和 dev server 无关,所以只是很少数人的需求。

感谢回复。 已提交pr,因为看到你们官网的QA说umi4不再提供devServer的配置,所以我这里采用了环境变量的方式。 同时在examples/dev-server-no-compress里创建了一个验证子项目

WangKB avatar Mar 01 '24 07:03 WangKB

看起来和这个issue 有关: https://github.com/chimurai/http-proxy-middleware/issues/371

也可以在 proxy 中添加 类似这样的配置,对单独的接口进行处理:

      onProxyRes: (proxyRes: any, req: any, res: any) => {
        if (req.headers.accept === 'text/event-stream') {
          res.writeHead(res.statusCode, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-transform',
            'Connection': 'keep-alive',
            'X-Accel-Buffering': 'no',
            'Access-Control-Allow-Origin': '*'            
          });
          proxyRes.pipe(res);
        }        
      }

smartwang avatar May 15 '24 11:05 smartwang

生产环境会有问题吗

1192163186github avatar May 30 '24 15:05 1192163186github

上面的对话中,有两种解决方案,一个是环境变量( UMI_DEV_SERVER_COMPRESS=none umi dev ) ,一个是配置 onProxyRes ,都可以解决开发时 SSE 的问题。

这个问题只出现在开发时,本地服务,如果要发到生产,需要配置好你的 nginx 或者网关等,有专门 SSE 的配置,详情搜索下吧。

fz6m avatar May 31 '24 22:05 fz6m

看起来和这个issue 有关: chimurai/http-proxy-middleware#371

也可以在 proxy 中添加 类似这样的配置,对单独的接口进行处理:

      onProxyRes: (proxyRes: any, req: any, res: any) => {
        if (req.headers.accept === 'text/event-stream') {
          res.writeHead(res.statusCode, {
            'Content-Type': 'text/event-stream',
            'Cache-Control': 'no-transform',
            'Connection': 'keep-alive',
            'X-Accel-Buffering': 'no',
            'Access-Control-Allow-Origin': '*'            
          });
          proxyRes.pipe(res);
        }        
      }

proxyRes.pipe(res)会导致流式消息嵌套,例如 event: message [{"field":value}](event: message {"field":value}) 导致数据解析异常

SmellyTrotter avatar Jul 30 '24 09:07 SmellyTrotter