每当调用控制器中的update
操作时(或者每当我的模型更新时),我都想发出服务器发送的事件。我目前在具有虚拟发射器的同一控制器中有一个工作watch
动作:
def watch
self.response.headers["Content-Type"] = "text/event-stream"
self.response.headers["Last-Modified"] = Time.now.ctime.to_json
self.response_body = Enumerator.new do |y|
100.times do |i|
sleep 5
y << ["event: message", "data: #{i}\n\n"].join("\n")
end
end
# TODO catch IO error when client disconnects
end
如何在调用update
时获得一个可以产生/返回值的可枚举对象?(注意:它不一定是一个Enumerable;但它确实有响应#each
这个流媒体技术的工作。)从某种意义上讲,我试图在Rails中实现一个事件驱动的架构。
我知道Observable但我无法弄清楚如何让我的观察者可以根据需要进行枚举......或者如何将观察者放入Enumerator(如上所述)而不必循环和睡眠计时器。
这样做的目的是对发送给当前登录的所有其他用户的数据库进行更改,以便每个用户始终拥有数据库的当前反射。
谢谢 -
答案 0 :(得分:1)
这不一定是最好的解决方案,但我最终创建了一个消息队列,如下所示:我在我的数据库中创建了一个新表(“SSE”),在我想要观察的模型中,我添加了回调:
class MyModel < ActiveRecord::Base
after_save :queue_sse
after_delete :queue_sse
# ...
private
def queue_sse
connected_users.each do |user|
SSE.create(:changed_record_id => this.id, :user_id => user)
end
end
end
然后在watch
动作:
def watch
connected_users << current_user # pseudo for a mutex-synched accessor
self.response.headers["Content-Type"] = "text/event-stream"
self.response.headers["Last-Modified"] = Time.now.ctime.to_json
self.response_body = Enumerator.new do |y|
loop do
sleep 5
ActiveRecord::Base.uncached do
updates = SSE.find_all_by_user_id(current_user)
updates.each do |update|
puts "update found: #{update.id}\n"
y << ["event: message", "data: #{update.id}\n\n"].join("\n")
update.destroy
end
end
end
end
# TODO add error catching, and on IOError, remove the current_user
end
虽然这对数据库很有帮助。它应该建立在memcached,互斥类变量或类似变量上。
(NB - 需要线程,例如config.threadsafe!
和线程服务器。)
答案 1 :(得分:1)
我创建了一个gem,允许您使用SSE“广播”事件。它允许您从更新操作将数据推送到连接的客户端。它被称为R4S(https://github.com/biggihs/r4s)
def update
#do some updating
R4S.push_data("key",{data:"data"},:event=>"SomeJsEvent")
end
这会将数据推送到连接到sse流“key”的所有浏览器