无论用户在哪个页面上,服务器发送的事件都必须始终触发吗?
Must a server sent event always be firing regardless of what page a user is on?
我是 SSE 的新手,所以如果我误解了目的,请随时告诉我,还有更好的方法来实现我想要的!
我有一个正常工作的 SSE,它每分钟都会更新用户的仪表板。代码如下所示:
# SitesController
def dashboard
end
def regular_update
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, event: 'notice')
begin
sse.write(NoticeTask.perform) # custom code returning the JSOn
sleep 60
rescue ClientDisconnected
ensure
sse.close
end
end
# routes
get "/dashboard(/:id)" => "sites#dashboard"
get "/site_update" => 'sites#regular_update'
# view - /dashboard
var source = new EventSource('/site_update');
source.addEventListener('notice', function(event) {
var data = JSON.parse(event.data)
appendNoticeAndAlert(data)
});
这很好用。当我为用户 /dashboard
时,SSE 会定期更新正确的信息,太棒了!
但是,我注意到如果我在任何随机页面上,比如主页,SSE 仍然在后台 运行。现在......显然这是有道理的,因为代码中没有任何其他限制......但不应该有吗???就像不应该有某种方式来确定 SSE 的范围吗?如果用户永远不在 /dashboard
上,让 SSE 不断在后台工作,更新 /dashboard
页面,这不是一种巨大的资源浪费吗?
再次,初学SSE,如有根本错误,还请多多指教。谢谢!
在您的控制器中处理 SSE 时,您应该循环更新,
然后 ActionController::Live::ClientDisconnected
在客户离开后由 response.stream.write
引发:
def regular_update
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, event: 'notice')
loop do
sse.write(NoticeTask.perform) # custom code returning the JSOn
sleep 60
end
rescue ClientDisconnected
logger.info "Client is gone"
ensure
sse.close
end
您的代码在第一次更新和延迟后断开客户端连接,但一切似乎都正常,因为 EventSource
自动重新连接(因此您获得了长轮询更新)。
在客户端 EventSource
上应该 close()
d 一旦不需要。通常它会在导航离开包含它的页面时自动完成,所以:
- 确保事件源 javascript 仅在仪表板页面上,而不在 javascript 捆绑包中(或者在捆绑包中,但仅在特定页面上启用)
- 如果您使用的是 turbolinks - 作为快速解决方案,您必须手动
close()
连接 - 尝试将 <meta name="turbolinks-visit-control" content="reload">
添加到页眉或暂时禁用 turbolinks。
还要再想想你是否真的需要 SSE 来完成这个特定的任务,因为对于普通的定期更新,你可以从客户端代码中轮询一个 json 动作,这将呈现相同的数据。这将使控制器更简单,不会为每个客户端保持连接繁忙,具有更广泛的服务器兼容性等。
要对 SSE 进行推理 - 至少检查是否确实发生了某些变化,如果没有,则跳过消息。更好的方法是使用某种 pub-sub(如 Redis 的 SUBSCRIBE
/PUBLISH
,或 Postgres 的 LISTEN
/NOTIFY
)——每次都向主题发送事件影响仪表板变化的东西,订阅 SSE connect 等等(也可能是限制更新,取决于你的应用程序)。类似的可以用 ActionCable 实现(有点矫枉过正,但可以很方便,因为已经集成了 pub-sub)
我是 SSE 的新手,所以如果我误解了目的,请随时告诉我,还有更好的方法来实现我想要的!
我有一个正常工作的 SSE,它每分钟都会更新用户的仪表板。代码如下所示:
# SitesController
def dashboard
end
def regular_update
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, event: 'notice')
begin
sse.write(NoticeTask.perform) # custom code returning the JSOn
sleep 60
rescue ClientDisconnected
ensure
sse.close
end
end
# routes
get "/dashboard(/:id)" => "sites#dashboard"
get "/site_update" => 'sites#regular_update'
# view - /dashboard
var source = new EventSource('/site_update');
source.addEventListener('notice', function(event) {
var data = JSON.parse(event.data)
appendNoticeAndAlert(data)
});
这很好用。当我为用户 /dashboard
时,SSE 会定期更新正确的信息,太棒了!
但是,我注意到如果我在任何随机页面上,比如主页,SSE 仍然在后台 运行。现在......显然这是有道理的,因为代码中没有任何其他限制......但不应该有吗???就像不应该有某种方式来确定 SSE 的范围吗?如果用户永远不在 /dashboard
上,让 SSE 不断在后台工作,更新 /dashboard
页面,这不是一种巨大的资源浪费吗?
再次,初学SSE,如有根本错误,还请多多指教。谢谢!
在您的控制器中处理 SSE 时,您应该循环更新,
然后 ActionController::Live::ClientDisconnected
在客户离开后由 response.stream.write
引发:
def regular_update
response.headers['Content-Type'] = 'text/event-stream'
sse = SSE.new(response.stream, event: 'notice')
loop do
sse.write(NoticeTask.perform) # custom code returning the JSOn
sleep 60
end
rescue ClientDisconnected
logger.info "Client is gone"
ensure
sse.close
end
您的代码在第一次更新和延迟后断开客户端连接,但一切似乎都正常,因为 EventSource
自动重新连接(因此您获得了长轮询更新)。
在客户端 EventSource
上应该 close()
d 一旦不需要。通常它会在导航离开包含它的页面时自动完成,所以:
- 确保事件源 javascript 仅在仪表板页面上,而不在 javascript 捆绑包中(或者在捆绑包中,但仅在特定页面上启用)
- 如果您使用的是 turbolinks - 作为快速解决方案,您必须手动
close()
连接 - 尝试将<meta name="turbolinks-visit-control" content="reload">
添加到页眉或暂时禁用 turbolinks。
还要再想想你是否真的需要 SSE 来完成这个特定的任务,因为对于普通的定期更新,你可以从客户端代码中轮询一个 json 动作,这将呈现相同的数据。这将使控制器更简单,不会为每个客户端保持连接繁忙,具有更广泛的服务器兼容性等。
要对 SSE 进行推理 - 至少检查是否确实发生了某些变化,如果没有,则跳过消息。更好的方法是使用某种 pub-sub(如 Redis 的 SUBSCRIBE
/PUBLISH
,或 Postgres 的 LISTEN
/NOTIFY
)——每次都向主题发送事件影响仪表板变化的东西,订阅 SSE connect 等等(也可能是限制更新,取决于你的应用程序)。类似的可以用 ActionCable 实现(有点矫枉过正,但可以很方便,因为已经集成了 pub-sub)