跳到內容 跳到搜尋

Action Cable Channel Base

通道提供將行為群組成群組到透過 WebSocket 連線進行通訊時邏輯單元的基礎結構。你可以將通道想成是控制器的一種形式,但除了單純回應訂閱者的直接要求外,它還能夠將內容推播給訂閱者。

Channel 實例是長佇留的。當連接使用者成為訂閱者時,將實例化一個通道物件,然後在使用者斷線之前持續運作。這可能是數秒、數分鐘、數小時,甚至數天。這表示你必須特別小心,不要在通道中進行任何會使記憶體使用量激增的傻事。參考會持續存在,因此不會像在每次請求後丟棄的控制器實例那樣被釋放。

長佇留通道(和連線)也表示你有責任確保資料的時效性。如果你保留對使用者的紀錄,但在保留參考期間變更了名稱,如果你沒有採取預防措施來避免,你可能會傳送過舊的資料。

長佇留通道實例的好處是你可以使用實例變數來保留與未來訂閱者要求可以互動的物件的參考。這裡有一個快速範例

class ChatChannel < ApplicationCable::Channel
  def subscribed
    @room = Chat::Room[params[:room_number]]
  end

  def speak(data)
    @room.speak data, user: current_user
  end
end

在消費者第一次訂閱通道時所建立的 Chat::Room 物件,只要消費者想要在房間中發言,speak 動作就會單純地使用該物件。

動作處理

ActionController::Base 的子類別不同,通道不會為其動作遵循 RESTful 限制表單。反之,Action Cable 會透過遠端程式呼叫模型來執行。你可以在通道上宣告任何公開的方法(選擇加上一個 data 參數),而此方法會自動對外公開,作為可以由客戶端呼叫的方法。

範例

class AppearanceChannel < ApplicationCable::Channel
  def subscribed
    @connection_token = generate_connection_token
  end

  def unsubscribed
    current_user.disappear @connection_token
  end

  def appear(data)
    current_user.appear @connection_token, on: data['appearing_on']
  end

  def away
    current_user.away @connection_token
  end

  private
    def generate_connection_token
      SecureRandom.hex(36)
    end
end

在此範例中,subscribed 及 unsubscribed 方法並不是可以呼叫的方法,因為它們已經在 ActionCable::Channel::Base 中宣告,但是 #appear#away 可以。#generate_connection_token 也不是可以呼叫的方法,因為它是一個私人方法。你會看到 appear 可以接受一個資料參數,然後將它用於模型呼叫的一部份。#away 不會接受,因為它只是一個觸發動作。

另外請注意,在此範例中,current_user 是可用的,因為它已在連線中標示為識別屬性。上述的所有識別碼都會自動在通道實例上建立一個具有相同名稱的委派方法。

取消訂閱要求

一個通道可以透過在 subscribed 回呼中呼叫 reject 方法來取消訂閱要求

class ChatChannel < ApplicationCable::Channel
  def subscribed
    @room = Chat::Room[params[:room_number]]
    reject unless current_user.can_access?(@room)
  end
end

在此範例中,如果 current_user 無權存取聊天室,將會取消訂閱。在用戶端,當伺服器取消訂閱要求時,將會呼叫 Channel#rejected 回呼。

方法
A
C
D
E
M
N
P
R
S
T
U
包括模組

屬性

[R] connection
[R] identifier
[R] params

類別公共方法

action_methods()

應視為動作的方法名稱清單。其中包括頻道上的所有公開實體方法,減去任何內部方法(定義在 Base 上),並新增任何內部但仍在類別本身存在的任何方法。

傳回

  • Set - 一組應視為動作的所有方法。

# File actioncable/lib/action_cable/channel/base.rb, line 128
def action_methods
  @action_methods ||= begin
    # All public instance methods of this class, including ancestors
    methods = (public_instance_methods(true) -
      # Except for public instance methods of Base and its ancestors
      ActionCable::Channel::Base.public_instance_methods(true) +
      # Be sure to include shadowed public instance methods of this class
      public_instance_methods(false)).uniq.map(&:to_s)
    methods.to_set
  end
end

new(connection, identifier, params = {})

# File actioncable/lib/action_cable/channel/base.rb, line 155
def initialize(connection, identifier, params = {})
  @connection = connection
  @identifier = identifier
  @params     = params

  # When a channel is streaming via pubsub, we want to delay the confirmation
  # transmission until pubsub subscription is confirmed.
  #
  # The counter starts at 1 because it's awaiting a call to #subscribe_to_channel
  @defer_subscription_confirmation_counter = Concurrent::AtomicFixnum.new(1)

  @reject_subscription = nil
  @subscription_confirmation_sent = nil

  delegate_connection_identifiers
end

類別私人方法

clear_action_methods!()

action_methods 已快取,有時需要更新。 ::clear_action_methods! 讓您可以這麼做,因此下次執行 action_methods 時,它們會重新計算。

# File actioncable/lib/action_cable/channel/base.rb, line 144
def clear_action_methods! # :doc:
  @action_methods = nil
end

method_added(name)

當新增新的 action_method 時更新快取的 action_methods

# File actioncable/lib/action_cable/channel/base.rb, line 149
def method_added(name) # :doc:
  super
  clear_action_methods!
end

實體公共方法

perform_action(data)

從傳遞的資料中擷取動作名稱,並透過頻道處理。此處理程序會確保所請求的動作是使用者在頻道上宣告的公開方法(因此不是 subscribed 類似於的回呼之一)。

# File actioncable/lib/action_cable/channel/base.rb, line 175
def perform_action(data)
  action = extract_action(data)

  if processable_action?(action)
    payload = { channel_class: self.class.name, action: action, data: data }
    ActiveSupport::Notifications.instrument("perform_action.action_cable", payload) do
      dispatch_action(action, data)
    end
  else
    logger.error "Unable to process #{action_signature(action, data)}"
  end
end

subscribe_to_channel()

連結已新增至連線後會呼叫此方法,並確認或拒絕此訂閱。

# File actioncable/lib/action_cable/channel/base.rb, line 190
def subscribe_to_channel
  run_callbacks :subscribe do
    subscribed
  end

  reject_subscription if subscription_rejected?
  ensure_confirmation_sent
end

實體私人方法

defer_subscription_confirmation?()

# File actioncable/lib/action_cable/channel/base.rb, line 248
def defer_subscription_confirmation? # :doc:
  @defer_subscription_confirmation_counter.value > 0
end

ensure_confirmation_sent()

# File actioncable/lib/action_cable/channel/base.rb, line 238
def ensure_confirmation_sent # :doc:
  return if subscription_rejected?
  @defer_subscription_confirmation_counter.decrement
  transmit_subscription_confirmation unless defer_subscription_confirmation?
end

reject()

# File actioncable/lib/action_cable/channel/base.rb, line 256
def reject # :doc:
  @reject_subscription = true
end

subscribed()

當消費者變成頻道的訂閱者時會呼叫一次。通常是在此設定任何你要這個頻道傳送給訂閱者的串流。

# File actioncable/lib/action_cable/channel/base.rb, line 212
def subscribed # :doc:
  # Override in subclasses
end

subscription_confirmation_sent?()

# File actioncable/lib/action_cable/channel/base.rb, line 252
def subscription_confirmation_sent? # :doc:
  @subscription_confirmation_sent
end

subscription_rejected?()

# File actioncable/lib/action_cable/channel/base.rb, line 260
def subscription_rejected? # :doc:
  @reject_subscription
end

transmit(data, via: nil)

傳送資料雜湊到訂閱者。雜湊會自動包裝到一個 JSON 信封中,並標記為正確的頻道標示符。

# File actioncable/lib/action_cable/channel/base.rb, line 225
def transmit(data, via: nil) # :doc:
  logger.debug do
    status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
    status += " (via #{via})" if via
    status
  end

  payload = { channel_class: self.class.name, data: data, via: via }
  ActiveSupport::Notifications.instrument("transmit.action_cable", payload) do
    connection.transmit identifier: @identifier, message: data
  end
end

unsubscribed()

當消費者切斷連線時呼叫一次。可做為清除連線、標記使用者為離線或類似動作。

# File actioncable/lib/action_cable/channel/base.rb, line 218
def unsubscribed # :doc:
  # Override in subclasses
end