Lorawan-stack: 未发送启动事件时,事件流阻塞控制台

创建于 2020-07-21  ·  14评论  ·  资料来源: TheThingsNetwork/lorawan-stack

概括

由于https://github.com/TheThingsNetwork/lorawan-stack/pull/2565事件在控制台中单击后在控制台块中流动。

重现步骤

  1. 转到https://tti.staging1.cloud.thethings.industries/console/gateways
  2. 在单个选项卡中,打开网关并使用面包屑返回
  3. 对每个网关重复

你现在看到了什么?

在某些时候,控制台会不断加载。 这似乎是由“打开的连接过多”引起的,因为/api/v3/events请求没有关闭。

你想看什么?

控制台应该保持响应。

环境

基于当前的master分支构建3df51cd750f57b37c3acffc28b417441babdbf30(不要关注控制台显示的3.8.5)

您建议如何实施?

我怀疑这是由于标题仅在流中的第一条消息之前写入这一事实造成的。 如果没有消息,则没有标题,浏览器会一直等待。

所以为了调查,我建议:

  • 检查我们是否正确写入标题
  • 检查 gRPC 网关是否正确写入和刷新标头
  • 检查 Echo/Mux 的东西是否不会干扰

你打算如何测试这个?

尝试复制步骤

shared dependencies

最有用的评论

所以这要么是在我们的依赖项中(grpc-gateway 没有正确刷新标头),要么是在我们的共享代码中(可能是一些 grpc 或 http 中间件没有刷新标头)。

删除bug标签,因为它目前不会影响我们的用户。

所有14条评论

如果有 6 个打开的选项卡,Firefox 控制台将永远加载 - 例如,如果刷新了工作选项卡之一 - 加载选项卡之一会解除阻塞,但有时似乎会失败。

无论如何,我尝试发送开始/结束消息,但这并没有改变任何东西 - 在有 6 个选项卡后它仍然挂起。

注意:所有选项卡都在网关的数据视图中(2 个不同的)

@kschiffer 有什么想法吗?

如果有 6 个打开的选项卡,Firefox 控制台将永远加载 - 例如,如果刷新了工作选项卡之一 - 加载选项卡之一会解除阻塞,但有时似乎会失败。

这是预期的,并且在不使用 HTTP/2 时取消事件流的限制

不通过 HTTP/2 使用时,SSE 会受到最大打开连接数的限制,这在打开各种选项卡时会特别痛苦,因为限制是 _per browser_ 并且设置为非常低的数字 (6)。 该问题已在ChromeFirefox 中标记为“无法修复”。 此限制是针对每个浏览器 + 域的,这意味着您可以在所有选项卡上打开 6 个 SSE 连接到www.example1.com ,另外 6 个 SSE 连接到www.example2.com. (来自Stackoverflow )。 使用 HTTP/2 时,最大并发 _HTTP 流数_在服务器和客户端之间协商(默认为 100)。

我无法重现原始问题,因为我只能访问临时环境中的两个连接的网关。

我怀疑这是由于标题仅在流中的第一条消息之前写入这一事实造成的。 如果没有消息,则没有标题,浏览器会一直等待。

我可以确认这一点。 如果请求没有得到响应头,它们就会停止。 在打开六个这样的连接后,所有后续的 XHR 也将停止。

在#2565 之前,这不是问题,因为事件流总是会立即发送“开始流消息”,这会导致发送标头。

因此,我不认为这是前端问题。 在前端更改唯一有意义的事情是确保停止的流连接在一段时间后被终止。

所以当事件流不发送任何数据时就会出现问题。 执行事件流连接的 XHR 只有在收到第一条消息后才会解析,否则将挂在pending状态。 一旦这些连接中的 6 个打开,所有其他连接也将挂起,因为已达到最大并发 TCP 连接数。 所以看起来 XHR 期望来自服务器的某种确认流已建立。

在#2565 之前,这不是问题,因为事件流将始终发送“开始流消息”。

我目前正在尝试找出是否以及如何在前端解决此问题。

根据我的经验,发送“初始消息”对解决此问题没有帮助。 请尝试使用以下差异:

diff --git a/pkg/events/grpc/grpc.go b/pkg/events/grpc/grpc.go
index 03f229ca0..5a305b5ac 100644
--- a/pkg/events/grpc/grpc.go
+++ b/pkg/events/grpc/grpc.go
@@ -119,6 +119,10 @@ func (srv *EventsServer) Stream(req *ttnpb.StreamEventsRequest, stream ttnpb.Eve
    if err := stream.SendHeader(metadata.MD{}); err != nil {
        return err
    }
+   if err := stream.Send(&ttnpb.Event{}); err != nil {
+       return err
+   }
+   defer stream.Send(&ttnpb.Event{})

    for {
        select {

这是一个视频:
https://youtu.be/0Ir0lakV-Mc

我确实看到了区别。

在没有差异的master

htdvisser % curl -v 'http://localhost:1885/api/v3/events' --compressed -H 'Authorization: Bearer MFRWG.DVTINRZNJDORITWD64NUJRIYVIXWCKJ3Q6VYLQY.E4K63GZ3WWTZGCIFMD7XLN7A7ACA35YCQSMFCBVCTEOMATCYGG6Q' -H 'Accept: text/event-stream' -H 'Content-Type: application/json' --data-raw '{"identifiers":[{"application_ids":{"application_id":"admin-app"}}]}'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 1885 (#0)
> POST /api/v3/events HTTP/1.1
> Host: localhost:1885
> User-Agent: curl/7.64.1
> Accept-Encoding: deflate, gzip
> Authorization: Bearer MFRWG.DVTINRZNJDORITWD64NUJRIYVIXWCKJ3Q6VYLQY.E4K63GZ3WWTZGCIFMD7XLN7A7ACA35YCQSMFCBVCTEOMATCYGG6Q
> Accept: text/event-stream
> Content-Type: application/json
> Content-Length: 68
> 
* upload completely sent off: 68 out of 68 bytes
^C

master使用差异。

htdvisser % curl -v 'http://localhost:1885/api/v3/events' --compressed -H 'Authorization: Bearer MFRWG.DVTINRZNJDORITWD64NUJRIYVIXWCKJ3Q6VYLQY.E4K63GZ3WWTZGCIFMD7XLN7A7ACA35YCQSMFCBVCTEOMATCYGG6Q' -H 'Accept: text/event-stream' -H 'Content-Type: application/json' --data-raw '{"identifiers":[{"application_ids":{"application_id":"admin-app"}}]}'
*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 1885 (#0)
> POST /api/v3/events HTTP/1.1
> Host: localhost:1885
> User-Agent: curl/7.64.1
> Accept-Encoding: deflate, gzip
> Authorization: Bearer MFRWG.DVTINRZNJDORITWD64NUJRIYVIXWCKJ3Q6VYLQY.E4K63GZ3WWTZGCIFMD7XLN7A7ACA35YCQSMFCBVCTEOMATCYGG6Q
> Accept: text/event-stream
> Content-Type: application/json
> Content-Length: 68
> 
* upload completely sent off: 68 out of 68 bytes
< HTTP/1.1 200 OK
< Content-Type: text/event-stream
< Referrer-Policy: strict-origin-when-cross-origin
< X-Content-Type-Options: nosniff
< X-Frame-Options: SAMEORIGIN
< X-Request-Id: 01EDTX9RGKRGM02YZVWNB3RXJP
< X-Xss-Protection: 1; mode=block
< Date: Wed, 22 Jul 2020 09:22:32 GMT
< Transfer-Encoding: chunked
< 
{"result":{"time":"0001-01-01T00:00:00Z"}}
^C

我们必须区分这个问题:

  1. 每当每个域和浏览器同时打开六个 TCP 连接时,任何后续请求都会挂起。 这是浏览器限制,IMO 处理此问题的方法应在另一个问题中讨论。 这是我在您的视频 @rvolosatovs 中看到的问题
  2. 当流端点没有立即发送响应头时,请求将停止(直到头最终在第一条消息上发送)。 这意味着虽然只使用一个选项卡,但在重复导航到打开流连接的页面时,控制台将累积挂起的流连接。 在打开六个这样的连接后,控制台将挂起,原因与 (1) 中相同。

为了缓解 2,需要立即发送响应头。

就控制台而言,这里需要注意的一件事是,我们还应该考虑取消挂起的请求。 目前只有在建立流连接后才能取消请求。

@kschiffer你能给出在 2. 中重现问题的步骤吗?
我不断刷新单个网关数据选项卡,并且在使用最新的master (无补丁)时没有遇到任何挂起

最简单的方法是创建一个全新且未连接的网关,因为它不会发送任何消息。 然后导航到其概览页面并返回网关列表六次。

顺便说一下,这不是网关事件特有的问题,而是所有事件流的问题。

取消分配我,因为这不是源自控制台的问题。

关闭 #2989

https://github.com/TheThingsNetwork/lorawan-stack/pull/2989中引入了一个临时解决方案 - 尚未找到更强大的解决方案。

所以这要么是在我们的依赖项中(grpc-gateway 没有正确刷新标头),要么是在我们的共享代码中(可能是一些 grpc 或 http 中间件没有刷新标头)。

删除bug标签,因为它目前不会影响我们的用户。

如果还有需要解决的问题,请打开一个新问题。

此页面是否有帮助?
0 / 5 - 0 等级

相关问题

adamsondelacruz picture adamsondelacruz  ·  7评论

ecities picture ecities  ·  5评论

ecities picture ecities  ·  5评论

johanstokking picture johanstokking  ·  8评论

johanstokking picture johanstokking  ·  3评论