2016-06-08 22 views
4

我正在使用Rails 5.0.0.rc1 + ActionCable + Redis構建一個Messenger應用程序。Rails ActionCable中「無法查找帶標識符的訂閱」的原因是什麼?

我有單通道ApiChannel和其中的一些操作。有一些「單播」操作 - >請求某些東西,回收某些東西,然後「廣播」操作 - >執行某些操作,將負載廣播到某些連接的客戶端。

我不時得到RuntimeError例外:https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/connection/subscriptions.rb#L70Unable to find subscription with identifier (...)

這可能是什麼原因?在什麼情況下我可以得到這樣的例外?我花了相當多的時間來研究這個問題(並且會繼續這樣做),任何提示都將不勝感激!

+0

僅供參考,已通過https://github.com/rails/rails/pull/26547修復 –

回答

1

它看起來像它的與此相關的問題:https://github.com/rails/rails/issues/25381

幾樣時Rails的回覆訂閱已創建,但實際上卻尚未完成的比賽條件。

作爲臨時解決方案建立訂閱後添加一個小超時解決了問題。

雖然需要做更多的調查。

1

此錯誤的原因可能是您訂閱的標識符和消息傳遞的區別。我用ActionCable在導軌5 API模式(具有寶石 'devise_token_auth')和I面臨同樣的錯誤太:

SUBSCRIBE(ERROR):

{"command":"subscribe","identifier":"{\"channel\":\"UnreadChannel\"}"} 

SEND MESSAGE(ERROR):

{"command":"message","identifier":"{\"channel\":\"UnreadChannel\",\"correspondent\":\"[email protected]\"}","data":"{\"action\":\"process_unread_on_server\"}"} 

出於某種原因ActionCable需要您的客戶端實例應用相同的標識符兩次 - 在訂購的同時消息:

/var/lib/gems/2.3.0 /gems/actioncable-5.0.1/lib/action_cable/connection/subscriptions.rb:74

def find(data) 
    if subscription = subscriptions[data['identifier']] 
    subscription 
    else 
    raise "Unable to find subscription with identifier: #{data['identifier']}" 
    end 
end 

這是一個活生生的例子:我實現傳輸子系統,用戶在現實得到了未讀郵件通知時間模式。在訂閱的時候,我並不需要correspondent,但是在消息傳遞的時候 - 我確實需要。

所以解決方案是將correspondent從標識符散列移動到數據散列:

SEND MESSAGE(CORRECT):

{"command":"message","identifier":"{\"channel\":\"UnreadChannel\"}","data":"{\"correspondent\":\"[email protected]\",\"action\":\"process_unread_on_server\"}"} 

這種方式,誤差消失。

這裏是我的UnreadChannel代碼:

class UnreadChannel < ApplicationCable::Channel 
    def subscribed 

    if current_user 

     unread_chanel_token = signed_token current_user.email 

     stream_from "unread_#{unread_chanel_token}_channel" 

    else 
# http://api.rubyonrails.org/classes/ActionCable/Channel/Base.html#class-ActionCable::Channel::Base-label-Rejecting+subscription+requests 
     reject 

    end 

    end 

    def unsubscribed 
    # Any cleanup needed when channel is unsubscribed 
    end 

    def process_unread_on_server param_message 

    correspondent = param_message["correspondent"] 

    correspondent_user = User.find_by email: correspondent 

    if correspondent_user 

     unread_chanel_token = signed_token correspondent 

     ActionCable.server.broadcast "unread_#{unread_chanel_token}_channel", 
            sender_id: current_user.id 
    end 

    end 

end 

幫手:(你不應該讓普通的標識符 - 編碼他們以同樣的方式Rails的編碼普通餅乾簽署的)

def signed_token string1 

    token = string1 

# http://vesavanska.com/2013/signing-and-encrypting-data-with-tools-built-in-to-rails 

    secret_key_base = Rails.application.secrets.secret_key_base 

    verifier = ActiveSupport::MessageVerifier.new secret_key_base 

    signed_token1 = verifier.generate token 

    pos = signed_token1.index('--') + 2 

    signed_token1.slice pos..-1 

    end 

要總結一下如果您稍後要調用MESSAGE命令,則必須先調用SUBSCRIBE命令。兩個命令都必須具有相同的標識符散列(此處爲「通道」)。有趣的是,subscribed鉤子並不是必需的(!) - 即使沒有它,你仍然可以發送消息(在SUBSCRIBE之後)(但沒有人會收到它們 - 沒有subscribed鉤子)。

另一個有趣這裏的一點是,subscribed鉤裏面我用這個代碼:

stream_from "unread_#{unread_chanel_token}_channel" 

明明unread_chanel_token可能是什麼 - 它僅適用於「接收」方向。

所以訂閱標識符(如\"channel\":\"UnreadChannel\")一直被認爲是一個「密碼」,爲未來消息發送操作(例如,它僅適用於「發送」方向) - 如果你想送一條消息(首先發送訂閱,然後)再次提供相同的「通過」,否則您將得到所描述的錯誤。

而更多的是 - 它實際上只是一個「密碼」 - 正如你所看到的,實際上你可以發送郵件到。無論您想要的:

ActionCable.server.broadcast "unread_#{unread_chanel_token}_channel", sender_id: current_user.id 

奇怪的,對不對?

這一切都很複雜。爲什麼在official documentation中沒有描述?

相關問題