Rail6 Action Cable 概述

來,見證奇蹟發表於2021-05-17

在本指南中,我們將學習Action Cable的工作方式以及如何使用WebSocket將實時通訊功能整合到我們的Rails應用程式中。

我們將在本指南中學習:

  • 什麼是 Action Cable 以及前後端整合的概念
  • 如何設定 Action Cable
  • 如何設定 channels
  • 執行以及部署 Action Cable 應用

1 什麼是Action Cable?

Action Cable將WebSockets與Rails應用程式無縫整合 。它允許我們使用Ruby編寫程式碼,使用Rails風格,同時也保證具有高效能和可伸縮性。Action Cable提供了全棧支援,包括客戶端JavaScript框架和伺服器端Ruby框架。我們也能通過Action Cable 訪問用Active Record或選擇ORM編寫的完整模型。

2 術語

Action Cable 使用 WebSockets 代替 HTTP 請求, 與此同時也就引入了一些與 http 不同的概念:

2.1 Connections:連線

Connections 使客戶端和和伺服器形成了關聯. 一個簡單的 Action Cable 服務可以處理多個連線例項. 一個WebSocket連線代表一個連線例項。如果一個使用者使用多個瀏覽器選項卡或裝置,則他們可能會向我們的應用程式開啟多個WebSocket。

2.2 Consumers:消費者

一個使用了 WebSocket 連線的客戶端就是一個 消費者consumer. 在 Action Cable 中,消費者被客戶端的 JavaScript框架建立.

2.3 Channels:通道

每個消費者可以依次訂閱多個通道channel。每個通道都封裝一個邏輯工作單元,這類似於控制器在常規MVC的框架中所做的工作。例如,我們可能有一個ChatChannel和一個AppearancesChannel,消費者可以訂閱這兩個渠道中的一個或兩個。但是消費者至少要訂閱一個頻道。

2.4 Subscribers:租戶

當消費者訂閱頻道時,他們就充當了租戶。訂戶與頻道之間的連線被稱為稱為'訂閱'。消費者可以多次訂閱頻道。例如,消費者可以同時訂閱多個聊天室。(請記住,一個真實的使用者可能成為多個消費者,每開啟一個選項卡或者裝置,都會建立一個連線)。

2.5 Pub/Sub:釋出/訂閱

釋出/訂閱(Pub / Sub)或釋出-訂閱(Publish-Subscribe)是指一種訊息佇列範例,通過該佇列,資訊傳送者(釋出者)將資料傳送到抽象類的接收者(訂閱者),而無需指定單個接收者。Action Cable使用這種方法在伺服器和許多客戶端之間進行通訊。

2.6 Broadcastings:廣播

廣播是釋出/訂閱連結,其中廣播者傳送的任何內容都直接傳送到訂閱了當前頻道的訂戶。每個通道可以容納零個或多個廣播。

3 服務端元件

3.1 連線

對於伺服器接受的每個WebSocket連線,都將例項化一個連線物件。該物件成為所有由此物件建立的訂閱者的父物件。連線本身不處理認證和授權之外的任何特定應用程式邏輯。WebSocket連線的客戶端稱為連線的消費者。使用者開啟的每一個瀏覽器選項卡,桌面軟體或裝置都會建立一個消費者-連線對。

連線是ApplicationCable::Connection的例項,它是 ActionCable::Connection::Base類的擴充套件。在ApplicationCable::Connection中,我們可以授權傳入連線,如果可以識別使用者,則可以繼續建立連線。

3.1.1 連線設定
# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    identified_by :current_user

    def connect
      self.current_user = find_verified_user
    end

    private
      def find_verified_user
        if verified_user = User.find_by(id: cookies.encrypted[:user_id])
          verified_user
        else
          reject_unauthorized_connection
        end
      end
  end
end

在此identified_by指定一個連線識別符號,該識別符號可用於以後查詢特定的連線。請注意,任何標記為識別符號的內容都會自動在連線之外建立的任何通道例項上以相同的名稱建立一個 Delegate。

此示例基於以下事實:我們已經在應用程式中的其他位置處理了使用者的身份驗證,並且已經使用使用者ID設定了加密的cookie。

然後,當嘗試建立新的連線時,該cookie會自動傳送到該連線例項,我們可以使用它來設定current_user。通過標識同一位當前使用者的連線,我們還可以確保以後可以檢索給定使用者的所有開啟的連線(如果刪除或未授權使用者,則有可能斷開所有連線)。

如果我們使用了 session 進行身份驗證,並且我們使用cookie儲存該會話的 session資訊,我們的session cookie名稱是_session,並且使用者ID名稱是user_id,那麼我們就可以使用以下方法獲取使用者資訊:

verified_user = User.find_by(id: cookies.encrypted['_session']['user_id'])
3.1.2 異常處理

預設情況下,沒有被處理的異常都會被框架自動捕獲並記錄到日誌中。如果我們想自己全域性處理這些異常,可以使用 rescue from 語法,就像下面這樣:

# app/channels/application_cable/connection.rb
module ApplicationCable
  class Connection < ActionCable::Connection::Base
    # 使用 report_error 處理 標準異常
    rescue_from StandardError, with: :report_error

    private
    # 自定義處理異常的方法
    def report_error(e)
      SomeExternalBugtrackingService.notify(e)
    end
  end
end

3.2 Channels:通道

每個通道都封裝一個邏輯工作單元,這類似於控制器在常規MVC的框架中所做的工作。預設情況下,Rials會建立一個繼承自ActionCable::Channel::Base類的ApplicationCable::Channel 類作為父類,來封裝通道之間資料共享的邏輯。

3.2.1 父通道設定
# app/channels/application_cable/channel.rb
module ApplicationCable
  # 父類
  class Channel < ActionCable::Channel::Base
  end
end

然後我們就可建立自己的通道類了,例如下面兩個類:ChatChannelAppearanceChannel

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
end

# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
end

然後消費者就可以訂閱這兩個通道或者其中一個了。

3.2.2 訂閱

消費者可以作為“訂閱者”來訂閱通道。然後根據通道使用者傳送的識別符號將生產的訊息傳送到訂閱者。

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  # Called when the consumer has successfully
  # become a subscriber to this channel.
  def subscribed
  end
end
3.2.3 異常處理

ApplicationCable::Connection一樣,我們也可以使用rescue_from在特定渠道上來處理引發的異常:

# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
  rescue_from 'MyError', with: :deliver_error_message

  private

  def deliver_error_message(e)
    broadcast_to(...)
  end
end

相關文章