我目前正在制作仪表板应用,但我不确定我的解决方案是否正确。
目前我有一个每30秒运行一次的Sidekiq作业。作业将数据保存到数据库,并将新结果流式传输到每个仪表板。
我必须解决的问题是从这项工作中将数据(使用ActionCable)仅流式传输到' online'仪表板。 (如果仪表板在浏览器选项卡中打开)
解决方案是从我的DashboardChannel中保存Redis,例如:
class DashboardChannel < ApplicationCable::Channel
def subscribed
stream_from "dashboard:#{params['dashboard_id']}"
tabs_number = $redis.get("dashboard_#{params['dashboard_id']}_online").to_i
$redis.set("dashboard_#{params['dashboard_id']}_online", tabs_number+=1)
end
def unsubscribed
tabs_number = $redis.get("dashboard_#{params['dashboard_id']}_online").to_i
tabs_number-=1
if tabs_number == 0
$redis.del("dashboard_#{params['dashboard_id']}_online")
else
$redis.set("dashboard_#{params['dashboard_id']}_online", tabs_number)
end
end
end
我保存了按键&#34;仪表板_#{DASHBOARD_ID} _online&#34;中打开的标签数量。保存标签数量非常重要,因为如果只保存true / false(1/0),当仪表板在2个标签中打开,而其中一个标签关闭时,它将被标记为离线。
在我的Sidekiq工作中我有类似的东西:
if Dashboard.online?(widget.dashboard_id) # returns true / false
ActionCable.server.broadcast "dashboard:#{widget.dashboard_id}",
{ widget_id: widget.id,
dashboard_id: widget.dashboard_id,
value: data_value.value,
recorded_at: data_value.recorded_at.strftime("%I:%M%p"),
in_bounds: data_value.in_bounds
}
end
在线方法app / models / dashboard.rb
def self.online?(dashboard_id)
!$redis.get("dashboard_#{dashboard_id}_online").nil?
end
在app / initializers / sidekiq.rb
中url = ENV["REDISTOGO_URL"] || "redis://localhost:6379/0"
uri = URI.parse(ENV["REDISTOGO_URL"] || "redis://localhost:6379/")
Sidekiq.configure_server do |config|
config.redis = { url: url, size: 4 }
end
Sidekiq.configure_client do |config|
config.redis = { url: url, size: 4 }
end
$redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)
在app / initializers / redis.rb
中uri = URI.parse(ENV["REDISTOGO_URL"] || "redis://localhost:6379/")
$redis = Redis.new(host: uri.host, port: uri.port, password: uri.password)
# Remove all 'online dashboard' keys
keys = $redis.keys("dashboard_")
$redis.del(*keys) unless keys.empty?
我的问题:是否可以使用&#39; $ redis&#39;在&#39; app / initializers / sidekiq.rb&#39;中声明的全局变量在Sidekiq工作?是线程保存吗?
目前我没有发现这种代码方法存在工作流程问题,但我担心“redis&#39;全局变量,不应该接近&#39;一个Sidekiq工作。
随意添加代码建议。谢谢你,祝你有愉快的一天!
答案 0 :(得分:0)
实际上,线程安全与全局变量$ redis没有任何关系。 $ redis只是保持与redis服务器的连接。 redis服务器是一个关于进程客户端请求的单线程模型,因此它将处理请求命令作为命令队列的顺序。
但请注意Sidekiq是多线程的。如果你的sidekip持有很多线程,每个线程都会向redis服务器发送命令。会出现线程安全问题。请阅读transaction of redis了解详情。
关于你的代码,我不太了解。但有一点,如果您的DashboardChannel的订阅和取消订阅在多线程或多处理中运行,您应该注意到会发生坏事。
e.g。如果行动按以下顺序发生,
number1 = client-1 get key
number2 = client-2 get key
client-1设置密钥编号1 + 1
client-2设置密钥number2 + 1
然后您的密钥将保留与实数相对应的错误值。如果是这样,请尝试使用redis手表。