簡體   English   中英

Sidekiq和Puma中的Redis變量,線程安全嗎?

[英]Redis variable in Sidekiq and Puma, thread safe?

我目前正在構建儀表板應用程序,但不確定我的解決方案是否正確。

目前,我有一個Sidekiq作業,每30秒運行一次。 該作業將數據保存到數據庫,並將新結果流式傳輸到每個儀表板。

我要解決的問題是從此工作開始,僅將數據(使用ActionCable)流式傳輸到“在線”儀表板。 (如果儀表板已在瀏覽器選項卡中打開)

解決方案是從我的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

我將在“ dashboard _#{DASHBOARD_ID} _online”鍵中打開的選項卡數保存下來。 保存選項卡的數量很重要,因為如果僅保存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?

我的問題:可以在Sidekiq作業的'app / initializers / sidekiq.rb'中使用聲明的'$ redis'全局變量嗎? 是線程保存的嗎?

目前,我還沒有找到使用這種代碼方法的工作流程問題,但是我擔心'$ redis'全局變量不應該位於Sidekiq Job的附近。

隨時添加代碼建議。 謝謝你,祝你有美好的一天!

實際上,安全線程沒有全局變量$ redis。 $ redis只是保持與redis服務器的連接。 Redis服務器是一個有關處理客戶端請求的單線程模型,因此它將以命令隊列的順序處理請求命令。

但是請注意,Sidekiq是多線程的。 如果您的同伴持有許多線程,並且每個線程都會將命令發送到Redis服務器。 將存在線程安全問題。 請閱讀redis交易以了解更多信息。

關於您的代碼,我不太了解。 但是有一點,如果您的DashboardChannel的訂閱者和取消訂閱者正在多線程或多處理中運行,您應該注意,壞事將會發生。

例如,如果操作按以下順序發生,

數字1 =客戶端1獲取密鑰

number2 =客戶端2獲取密鑰

客戶端1設置密鑰號1 + 1

客戶端2設置密鑰號2 +1

然后,您的密鑰將保留一個與實數相對的錯誤值。 如果是,請嘗試使用redis手表。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM