前兩天介紹了輪詢、WebSocket,雖然不是很完整,但有稍稍了解一點即時通訊不是像我們平常想得理所當然!而今天要講的是 Action Cable 的背後原理,以及在 Rails 應用中如何通過WebSocket 實現即時功能。
Actioncable 是一個 Pub/Sub 模型 (發布/訂閱) + WebSocket 的 Ruby 框架,可以讓 Rails 透過websocket,實現即時通訊。
Pub/Sub 設計模式是即時通訊
很常使用的架構,將訊息的傳送者、接收者拆分為發布方、訂閱方。而發布方可以將訊息傳送給許多訂閱方。
全名 REmote DIctionary Server,屬於 NOSQL 家族中的一個成員。
NOSQL (Not Only SQL) 分散式非關聯式資料庫的統稱,不同於傳統的關聯式資料庫,ex: MySQL、PostgreSQL。
是快速的開放原始碼記憶體內
鍵值
資料存放區,可做為資料庫、快取、訊息代理程式和佇列使用。目前 Redis 的回應時間低於一毫秒,使遊戲、金融服務、醫療保健等領域的即時應用程式每秒處理數百萬個請求。Redis 是用於快取、工作階段管理、遊戲、排行榜、即時分析、地理空間、叫車、聊天/簡訊、媒體串流和發佈/訂閱應用程式的熱門選項。
(會想要用 PG 是因為打算部署在 Heroku 上,因為預設的情況下,rails 會採用 SQLite )
$ rails new chatchat database: postgresql
小提醒:前面的 $ 表示為終端機指令,不用輸入喔!
修改前:
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: chatchat_production
修改後:
development:
adapter: redis
url: redis://localhost:6379/1
test:
adapter: test
production:
adapter: redis
url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
channel_prefix: chatchat_production
(Mac)使用 Homebrew 安裝 Redis 會減少大量的安裝時間。
詳細安裝過程可以請 Google 協助!
$ brew install redis
可能會要你先 update homebrew。
開機即自動啟動 Redis 指令(之後就不怕忘記啟動啦~)
$ ln -sfv /usr/local/opt/redis/*.plist ~/Library/LaunchAgents
# gemfile.rb
gem 'redis', '~> 3.3', '>= 3.3.1'
$ bundle install
$ rails g channel room
執行結果如下
產生的測試檔就不管了,主要有以下重點檔案:
app/channels/room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
# stream_from "some_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
app/javascript/channels/consumer.js
// Action Cable provides the framework to deal with WebSockets in Rails.
// You can generate new channels where WebSocket features live using the `rails generate channel` command.
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
app/javascript/channels/index.js
// Load all the channels within this directory and all subdirectories.
// Channel files must be named *_channel.js.
const channels = require.context('.', true, /_channel\.js$/)
channels.keys().forEach(channels)
app/javascript/channels/room_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("RoomChannel", {
connected() {
// Called when the subscription is ready for use on the server
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
// Called when there's incoming data on the websocket for this channel
}
});
明天來解釋這個過程做了什麼事?
參考資料:
railsguides-Action Cable Overview
rubyonrails-api
實戰聖經 - Action Cable 即時通訊
AWS - 什麼是 Redis?
Rails Tutorial | Action Cable Basics in Rails 6
學無止盡,每天都要進步一點點!