RESMİ KILAVUZA BU ADRES ÜZERİNDEN ULAŞABİLİRSİNİZ: https://guides.rubyonrails.org
Action Cable’a Genel Bakış
Bu kılavuzda Action Cable’ın nasıl çalıştığını ve WebSocketleri kullanarak gerçek zamanlı özellikleri Rails uygulamanıza nasıl dahil edeceğinizi öğreneceksiniz.
Bu kılavuzu okuduktan sonra şunları bileceksiniz:
- Action Cable’ın ne olduğunu, backend ve frontend’e nasıl entegre edeceğinizi
- Action Cable’ı nasıl kuracağınızı
- Kanalları nasıl kuracağınızı
- Action Cable’ı çalıştırmak için dağıtım ve mimari kurulumunu
Action Cable Nedir?
Action Cable, Rails uygulamanıza WebSocket‘i sorunsuz bir şekilde entegre eder. Performans ve ölçeklenebilirliği sağlarken aynı zamanda gerçek zamanlı özelliklerin, Rails uygulamanızın geri kalanında olduğu gibi aynı stil ve formda Ruby’de yazılmasını sağlar. Hem istemci taraflı Javascript framework’ü hem sunucu taraflı Ruby framework’ü sağlayan bir full-stack aracıdır. Active Record veya tercih ettiğiniz bir ORM ile yazılmış kendi tüm domain modelinize erişiminiz vardır.
Terimler
Action Cable, HTTP’nin istek-yanıt protokolü yerine WebSocket kullanır. Hem Action Cable hem WebSocket daha az bilinen bazı terminolojiler sunar:
Bağlantılar
Bağlantılar (Connections) istemci-sunucu ilişkisinin temelini oluşturur. Tek bir Action Cable sunucusu birden fazla bağlantının üstesinden gelebilir. Her bir WebSocket bağlantısı için bir bağlantı örneği vardır. Tek bir kullanıcı eğer birden fazla tarayıcı sekmesi veya cihaz kullanıyorsa uygulamanıza açık birden fazla WebSocket’e sahip olabilir.
Tüketiciler
Bir WebSocket bağlantısının istemcisine tüketici (consumer) denir. Action Cable’da tüketici, istemci-taraflı Javascript framework’ü tarafından oluşturulur.
Kanallar
Her bir tüketici sırayla birden fazla kanala (channel) abone olabilir. Her kanal, bir controllerın tipik bir MVC kurulumunda yaptığına benzer bir şekilde mantıksal bir iş birimini kapsar. Örneğin, ChatChannel ve AppearancesChannel adında kanallarınız olsun. Bir tüketici bu kanallardan birine veya her ikisine de abone olabilir. Bir tüketicinin en azından bir kanala abone olması gerekir.
Aboneler
Tüketici bir kanala abone olduğunda abone (subscriber) gibi davranır. Abone ile kanal arasındaki bağlantıya ise abonelik denir. Bir tüketici belirli bir kanala istediği kadar abone olabilir. Örneğin, bir tüketici birden fazla chat odasına (room) aynı anda abone olabilir. (Fiziksel bir kullanıcının bağlantınıza açık olan her bir sekme veya cihaz için bir tane olmak üzere birden fazla tüketicisinin olabildiğini unutmayın.)
Yayınla/Abone Ol
Yayınla/Abone Ol (Pub/Sub) paradigması; bilgi gönderenlerin (yayıncıların), belirli alıcıları belirtmeden, soyut bir alıcılar sınıfına (abonelere) verileri gönderdiği bir mesaj kuyruğu paradigmasını ifade eder. Action Cable, sunucu ile çok sayıda istemci arasında iletişim kurmak için bu yaklaşımı kullanır.
Yayınlar
Bir yayın (broadcasting); yayıncının ilettiği her şeyin, o isimle yayın yapılan yayını dinleyen kanal abonelerine doğrudan gönderildiği bir yayınla/abone ol (pub/sub) bağlantısıdır. Her kanal, sıfır veya daha fazla yayını dinleyebilir.
Sunucu Taraflı Bileşenler
Bağlantılar
Sunucu tarafından kabul edilen her WebSocket için bir bağlantı nesnesi oluşturulur. Bu nesne, bundan sonra oluşturulacak tüm kanal aboneliklerinin ebeveyni (parent) haline gelir. Bağlantının kendisi, kimlik doğrulama ve yetkilendirme dışında herhangi bir spesifik uygulama mantığı ile ilgilenmez. Bir WebSocket bağlantısının istemcisine bağlantı tüketicisi denir. Bireysel bir kullanıcı her bir açık tarayıcı sekmesi, penceresi veya cihazı için tüketici-bağlantı çifti oluşturur.
Bağlantılar, ActionCable::Connection::Base sınıfını genişleten (extend) ApplicationCable::Connection sınıfının örnekleridir. ApplicationCable::Connectionda gelen bağlantıyı yetkilendirirsiniz ve kullanıcı tanımlanabiliyorsa bağlantı kurmaya devam edersiniz.
Bağlantı Kurulumu
# 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
Buradaki identified_by, daha sonra spesifik bir bağlantıyı bulmak için kullanılabilen bağlantı tanımlayıcısını (identifier) belirtir. Unutmayın, tanımlayıcı olarak işaretlenen her şey, bağlantı dışında oluşturulan herhangi bir kanal örneği üzerinde otomatik olarak aynı isimle bir temsilci oluşturur.
Bu örnek, uygulamanızın başka bir yerinde kullanıcının çoktan bir kimlik doğrulaması gerçekleştirmiş olduğunuzu ve başarılı bir kimlik doğrulamasının kullanıcı ID’si ile şifrelenmiş bir çerez (cookie) ayarlayacağını varsayar.
Yeni bir bağlantı girişiminde bulunulduğunda çerez otomatik olarak bağlantı örneğine gönderilir, ve siz bunu current_userı ayarlamak için kullanırsınız. Aynı mevcut kullanıcı tarafından tanımladığınız bağlantıyla, daha sonra belirli bir kullanıcı tarafından açılan tüm açık bağlantıları geri alabilmeyi (veya kullanıcı silinir ya da yetkisiz hale gelirse potansiyel olarak tüm bağlantıları kesebilmeyi) sağlarsınız.
Eğer yetkilendirme yaklaşımınız bir oturum kullanmayı kapsıyorsai oturumu depolamak için çerezleri kullanırsınız. Oturum çerezinizin adı _session ve kullanıcı ID’niz user_id ise bu yaklaşımı kullanabilirsiniz:
verified_user = User.find_by(id: cookies.encrypted["_session"]["user_id"])
İstisna Yönetimi
Varsayılan olarak, işlenmemiş (unhandled) istisnalar yakalanır ve Rails’in kayıt tutucusunda (logger) tutulur. Eğer bu istisnaları global olarak yakalamak ve örneğin harici bir hata (bug) takip servisine bildirmek isterseniz, rescue_from ile bunu yapabilirsiniz:
# app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
rescue_from StandardError, with: :report_error
private
def report_error(e)
SomeExternalBugtrackingService.notify(e)
end
end
end
Bağlantı Callback’leri
ActionCable::Connection::Callbacks abone olma, abonelik iptali veya bir eylem gerçekleştirme gibi istemciye komut gönderilirken çağrılan callback hook’ları sağlar:
Kanallar
Bir kanal, bir controllerın tipik bir MVC kurulumunda yaptığına benzer bir şekilde mantıksal bir iş birimini kapsar. Varsayılan olarak Rails, kanal oluşturucuyu (channel generator) ilk kez kullandığınızda, kanallarınız arasında paylaşılan mantığı kapsüllemek için bir ebeveyn sınıf olan ApplicationCable::Channel (ki bu sınıf ActionCable::Channel::Base sınıfını genişletir/extend eder) oluşturur.
Ebeveyn Kanal Kurulumu
# app/channels/application_cable/channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base
end
end
Kendi kanal sınıflarınız şu örnekler gibi görünebilir:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
end
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
end
Bir tüketici, bu kanallardan birine veya her ikisine birden abone olabilir.
Abonelikler
Tüketiciler kanallara abone olarak “abone” durumuna gelirler. Bağlantılarına abonelik denir. Üretilen mesajlar daha sonra kanal tüketicisi tarafından gönderilen bir tanımlayıcıya göre bu kanal aboneliklerine yönlendirilir.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
# Tüketici başarılı bir şekilde
# bu kanala abone olduğunda çağrılır.
def subscribed
end
end
İstisna Yönetimi
ApplicationCable::Connectionda olduğu gibi, ortaya çıkan istisnaları işlemek için belirli bir kanalda rescue_from‘u da kullanabilirsiniz:
# 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
Kanal Callback’leri
ActionCable::Channel::Callbacks, bir kanalın yaşam döngüsü sırasında çağrılan callback hook’larını sağlar:
before_subscribeafter_subscribe(on_subscribetakma adıyla anılır)before_unsubscribeafter_unsubscribe(on_unsubscribetakma adıyla anılır)
İstemci Taraflı Bileşenler
Bağlantılar
Tüketiciler kendi taraflarında bir bağlantı örneğine ihtiyaç duyarlar. Bu, Rails tarafından varsayılan olarak oluşturulan aşağıdaki JavaScript kullanılarak kurulabilir:
Tüketici Bağlantısı
// app/javascript/channels/consumer.js
// Action Cable, Rails'te WebSocket ilebaşa çıkmak için bir framework sağlar.
// WebSocket özelliklerinin bulunduğu yeni kanalları
// `bin/rails generate channel` komutunu kullanarak oluşturabilirsiniz.
import { createConsumer } from "@rails/actioncable"
export default createConsumer()
Bu, varsayılan olarak sunucunuzdaki /cable ile bağlantı kuracak bir tüketici hazırlayacaktır. İlgi duyduğunuz en az bir aboneliği ayrıca belirtmediğiniz sürece bağlantı kurulmayacaktır.
Tüketici, isteğe bağlı olarak bağlanılacak URL’i belirten bir bağımsız değişken alabilir. Bu bir string veya WebSocket açıldığında çağrılacak string döndüren bir fonksiyon olabilir.
// Bağlanmak için farklı bir URL belirtin
createConsumer('wss://example.com/cable')
// veya HTTP üzerinden websocket kullanırken:
createConsumer('https://ws.example.com/cable')
// URL'i dinamik olarak oluşturmak için
// bir fonksiyon kullanın
createConsumer(getWebSocketURL)
function getWebSocketURL() {
const token = localStorage.get('auth-token')
return `wss://example.com/cable?token=${token}`
}
Aboneler
Bir tüketici, belirli bir kanala abonelik oluşturarak abone olur:
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" })
// app/javascript/channels/appearance_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "AppearanceChannel" })
Bu, aboneliği oluştururken, alınan verilere yanıt vermek için gereken işlevsellik daha sonra açıklanacaktır.
Bir tüketici belirli bir kanala istediği sayıda abone olabilir. Örneğin, bir tüketici aynı anda birden fazla sohbet odasına abone olabilir:
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "1st Room" })
consumer.subscriptions.create({ channel: "ChatChannel", room: "2nd Room" })
İstemci-Sunucu Etkileşimleri
Yayın Akışları
Yayın akışları (Streams), kanalların yayınlanan içeriği (yayınları) abonelerine yönlendirdiği mekanizmayı sağlar. Örneğin, aşağıdaki kod, :room parametresinin değeri "Best Room" olduğunda chat_Best Room adlı yayına abone olmak için stream_from kullanır:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
Ardından, Rails uygulamanızın başka bir yerinde broadcast çağrısı yaparak böyle bir odaya yayın yapabilirsiniz:
ActionCable.server.broadcast("chat_Best Room", { body: "This Room is Best Room." })
Bir modelle ilişkili bir yayın akışınız varsa, yayın adı kanal ve modelden oluşturulabilir. Örneğin, aşağıdaki kod posts:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE gibi bir yayına abone olmak için stream_for kullanır. Burada Z2lkOi8vVGVzdEFwc9Qb3N0LzE, Post modelinin GlobalID’sidir.
class PostsChannel < ApplicationCable::Channel
def subscribed
post = Post.find(params[:id])
stream_for post
end
end
Daha sonra broadcast_to çağrısı yaparak bu kanala yayın yapabilirsiniz:
PostsChannel.broadcast_to(@post, @comment)
Yayınlar
Bir yayın, bir yayıncı tarafından iletilen herhangi bir şeyin doğrudan adı geçen yayını yayınlayan kanal abonelerine yönlendirildiği bir yayınla/abone ol bağlantısıdır. Her kanal sıfır veya daha fazla yayın yayınlıyor olabilir.
Yayınlar tamamen çevrimiçi bir kuyruktur ve zamana bağlıdır. Eğer bir tüketici yayın akışında değilse (belirli bir kanala abone değilse), daha sonra bağlanması durumunda yayını alamayacaktır.
Abonelikler
Bir tüketici bir kanala abone olduğunda, bir abone olarak hareket eder. Bu bağlantıya abonelik denir. Gelen mesajlar daha sonra kablo tüketicisi (cable consumer) tarafından gönderilen bir tanımlayıcıya (identifier) dayalı olarak bu kanal aboneliklerine yönlendirilir.
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
this.appendLine(data)
},
appendLine(data) {
const html = this.createLine(data)
const element = document.querySelector("[data-chat-room='Best Room']")
element.insertAdjacentHTML("beforeend", html)
},
createLine(data) {
return `
<article class="chat-line">
<span class="speaker">${data["sent_by"]}</span>
<span class="body">${data["body"]}</span>
</article>
`
}
})
Kanallara Parametre Geçirme
Bir abonelik oluştururken parametreleri istemci tarafından sunucu tarafına geçirebilirsiniz. Örneğin:
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
end
subscriptions.create‘e ilk argüman olarak geçirilen bir nesne, kablo kanalındaki params hash’i olur. channel anahtar sözcüğü gereklidir:
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
this.appendLine(data)
},
appendLine(data) {
const html = this.createLine(data)
const element = document.querySelector("[data-chat-room='Best Room']")
element.insertAdjacentHTML("beforeend", html)
},
createLine(data) {
return `
<article class="chat-line">
<span class="speaker">${data["sent_by"]}</span>
<span class="body">${data["body"]}</span>
</article>
`
}
})
# Uygulamanızda bir yerde,
# belki de bir NewCommentJob'dan bu çağrılır.
ActionCable.server.broadcast(
"chat_#{room}",
{
sent_by: "Paul",
body: "This is a cool chat app."
}
)
Bir Mesajı Yeniden Yayınlama
Yaygın bir kullanım durumu, bir istemci tarafından gönderilen bir mesajın diğer bağlı istemcilere yeniden yayınlanmasıdır.
# app/channels/chat_channel.rb
class ChatChannel < ApplicationCable::Channel
def subscribed
stream_from "chat_#{params[:room]}"
end
def receive(data)
ActionCable.server.broadcast("chat_#{params[:room]}", data)
end
end
// app/javascript/channels/chat_channel.js
import consumer from "./consumer"
const chatChannel = consumer.subscriptions.create({ channel: "ChatChannel", room: "Best Room" }, {
received(data) {
// data => { sent_by: "Paul", body: "This is a cool chat app." }
}
})
chatChannel.send({ sent_by: "Paul", body: "This is a cool chat app." })
Yeniden yayın, mesajı gönderen istemci de dahil olmak üzere tüm bağlı istemciler tarafından alınacaktır. Parametrelerin kanala abone olduğunuzda olduğu gibi aynı olduğunu unutmayın.
Full-Stack Örnekler
Aşağıdaki kurulum adımları her iki örnek için de ortaktır:
Örnek 1: Kullanıcı Görünümleri
İşte bir kullanıcının çevrimiçi olup olmadığını ve hangi sayfada olduğunu izleyen basit bir kanal örneği. (Bu, çevrimiçi olduklarında kullanıcı adının yanında yeşil bir nokta göstermek gibi varlık özellikleri oluşturmak için kullanışlıdır.)
Sunucu tarafı görünüm kanalını oluşturun:
# app/channels/appearance_channel.rb
class AppearanceChannel < ApplicationCable::Channel
def subscribed
current_user.appear
end
def unsubscribed
current_user.disappear
end
def appear(data)
current_user.appear(on: data["appearing_on"])
end
def away
current_user.away
end
end
Bir abonelik başlatıldığında subscribed callback’i tetiklenir ve bu fırsatı “mevcut kullanıcı gerçekten göründü” demek için kullanırız. Bu görünme/kaybolma API’yı Redis, bir veritabanı veya başka bir şey tarafından desteklenebilir.
İstemci tarafı görünüm kanalı aboneliğini oluşturun:
// app/javascript/channels/appearance_channel.js
import consumer from "./consumer"
consumer.subscriptions.create("AppearanceChannel", {
// Abonelik oluşturulduğunda bir kez çağrılır.
initialized() {
this.update = this.update.bind(this)
},
// Abonelik sunucuda kullanıma hazır olduğunda çağrılır.
connected() {
this.install()
this.update()
},
// WebSocket bağlantısı kapatıldığında çağrılır.
disconnected() {
this.uninstall()
},
// Abonelik sunucu tarafından reddedildiğinde çağrılır.
rejected() {
this.uninstall()
},
update() {
this.documentIsActive ? this.appear() : this.away()
},
appear() {
// Sunucuda `AppearanceChannel#appear(data)` öğesini çağırır.
this.perform("appear", { appearing_on: this.appearingOn })
},
away() {
// Sunucuda `AppearanceChannel#away` öğesini çağırır.
this.perform("away")
},
install() {
window.addEventListener("focus", this.update)
window.addEventListener("blur", this.update)
document.addEventListener("turbo:load", this.update)
document.addEventListener("visibilitychange", this.update)
},
uninstall() {
window.removeEventListener("focus", this.update)
window.removeEventListener("blur", this.update)
document.removeEventListener("turbo:load", this.update)
document.removeEventListener("visibilitychange", this.update)
},
get documentIsActive() {
return document.visibilityState === "visible" && document.hasFocus()
},
get appearingOn() {
const element = document.querySelector("[data-appearing-on]")
return element ? element.getAttribute("data-appearing-on") : null
}
})
İstemci-Sunucu Etkileşimi
-
İstemci,
createConsumer()aracılığıyla Sunucuya bağlanır. (consumer.js). Sunucu bu bağlantıyıcurrent_userile tanımlar. -
İstemci,
consumer.subscriptions.create({ channel: "AppearanceChannel" })aracılığıyla görünüm kanalına abone olur. (appearance_channel.js) -
Sunucu görünüm kanalı için yeni bir abonelik başlatıldığını fark eder ve
current_userüzerindeappearmetodunu çağıraraksubscribedcallback’ini çalıştırır. (appearance_channel.rb) -
İstemci bir abonelik kurulduğunu fark eder ve
connected(appearance_channel.js) öğesini çağırır, bu dainstallveappearöğelerini çağırır.appearsunucudaAppearanceChannel#appear(data)öğesini çağırır ve{ appearing_on: this.appearingOn }şeklinde bir veri hash’i sağlar. Bu mümkündür çünkü sunucu tarafı kanal örneği, sınıfta bildirilen tüm genel metotları (callback’ler hariç) otomatik olarak açığa çıkarır, böylece bunlara bir aboneliğinperformmetodu aracılığıyla uzak yordam çağrıları/uzaktan fonksiyon çağrısı olarak erişilebilir. -
Sunucu,
current_user(appearance_channel.rb) tarafından tanımlanan bağlantı için görünüm kanalındaappearaction’ı için istek alır. Sunucu veri hash’inden:appearing_onanahtarına sahip veriyi alır vecurrent_user.appeardosyasına aktarılan:onanahtarının değeri olarak ayarlar.
Örnek 2: Yeni Web Bildirimlerinin Alınması
Görünüm örneği, sunucu işlevselliğinin WebSocket bağlantısı üzerinden istemci tarafı çağrısına maruz bırakılmasıyla ilgiliydi. Ancak WebSocket ile ilgili en güzel şey, bunun iki yönlü bir yol olmasıdır. Şimdi, sunucunun istemciye bir action çağrısında bulunduğu bir örnek gösterelim.
Bu, ilgili yayın akışlarına yayın yaptığınızda istemci tarafı web bildirimlerini tetiklemenizi sağlayan bir web bildirim kanalıdır:
Sunucu tarafı web bildirimleri kanalını oluşturun:
# app/channels/web_notifications_channel.rb
class WebNotificationsChannel < ApplicationCable::Channel
def subscribed
stream_for current_user
end
end
İstemci tarafı web bildirimleri kanal aboneliğini oluşturun:
// app/javascript/channels/web_notifications_channel.js
// Web bildirimleri gönderme hakkını
// zaten talep ettiğinizi varsayan istemci tarafı.
import consumer from "./consumer"
consumer.subscriptions.create("WebNotificationsChannel", {
received(data) {
new Notification(data["title"], { body: data["body"] })
}
})
Uygulamanızın başka bir yerinden bir web bildirim kanalı örneğine içerik yayınlayın:
# Uygulamanızda bir yerde,
# belki de bir NewCommentJob'dan bu çağrılır.
WebNotificationsChannel.broadcast_to(
current_user,
title: "New things!",
body: "All the news fit to print"
)
WebNotificationsChannel.broadcast_to çağrısı, her kullanıcı için ayrı bir yayın adı altında geçerli abonelik bağdaştırıcısının abone ol/yayınla kuyruğuna bir mesaj yerleştirir. ID’si 1 olan bir kullanıcı için yayın adı web_notifications:1 olur.
Kanala, web_notifications:1 adresine gelen her şeyi received callback’ini çağırarak doğrudan istemciye aktarması talimatı verilmiştir. Argüman olarak geçirilen veri; sunucu tarafındaki yayın çağrısına ikinci parametre olarak gönderilen hash, kablo boyunca yolculuk için JSON olarak kodlanmış ve received olarak gelen veri argümanı için paketten çıkarılmış veridir.
Daha Kapsamlı Örnekler
Bir Rails uygulamasında Action Cable’ın nasıl kurulacağına ve kanalların nasıl ekleneceğine dair tam bir örnek için rails/actioncable-examples deposuna bakın.
Yapılandırma
Action Cable’ın iki gerekli yapılandırması (configuration) vardır: Bir abonelik bağdaştırıcısı ve izin verilen istek kökenleri (Not: Allowed request origins; Sunucunun kabul edeceği istemci isteklerinin hangi alan adlarından (origin) gelebileceğini tanımlar.).
Abonelik Bağdaştırıcısı
Action Cable varsayılan olarak config/cable.yml içinde bir yapılandırma dosyası arar. Dosya, her Rails ortamı için bir bağdaştırıcı belirtmelidir. Bağdaştırıcılar hakkında ek bilgi için Bağımlılıklar bölümüne bakın.
development:
adapter: async
test:
adapter: test
production:
adapter: redis
url: redis://10.10.3.153:6381
channel_prefix: appname_production
Bağdaştırıcı Yapılandırması
Aşağıda son kullanıcılar için mevcut olan abonelik bağdaştırıcılarının bir listesi bulunmaktadır.
Asenkron Bağdaştırıcı
Asenkron bağdaştırıcı geliştirme/test için tasarlanmıştır, üretimde kullanılmamalıdır.
bin/rails console ile başlatılan bir terminalden değil, web konsolundan (geliştirme sürecinin içinde çalışan) yapmanız gerekir! Web konsolunun görünmesini sağlamak için herhangi bir action'a veya herhangi bir ERB şablon view'e console ekleyin.
Solid Cable Bağdaştırıcısı
Solid Cable bağdaştırıcısı, Active Record kullanan veritabanı destekli bir çözümdür. MySQL, SQLite ve PostgreSQL ile test edilmiştir. bin/rails solid_cable:install çalıştırıldığında otomatik olarak config/cable.yml kurulacak ve db/cable_schema.rb oluşturulacaktır. Bundan sonra, config/database.yml dosyasını veritabanınıza göre ayarlayarak manuel olarak güncellemeniz gerekir.Bkz. Solid Cable Kurulumu.
Redis Bağdaştırıcısı
Redis bağdaştırıcısı, kullanıcıların Redis sunucusuna işaret eden bir URL sağlamasını gerektirir.
Ek olarak, birden fazla uygulama için aynı Redis sunucusunu kullanırken kanal adı çakışmalarını önlemek için bir channel_prefix sağlanabilir. Daha fazla ayrıntı için Redis Pub/Sub belgelerine bakın.
Redis bağdaştırıcısı SSL/TLS bağlantılarını da destekler. Gerekli SSL/TLS parametreleri, YAML yapılandırma dosyasındaki ssl_params anahtarında geçirilebilir.
production:
adapter: redis
url: rediss://10.10.3.153:tls_port
channel_prefix: appname_production
ssl_params:
ca_file: "/path/to/ca.crt"
ssl_paramsa verilen seçenekler doğrudan OpenSSL::SSL::SSLContext#set_params metoduna aktarılır ve SSL bağlamının herhangi bir geçerli niteliği olabilir.
Mevcut diğer nitelikler için lütfen OpenSSL::SSL::SSLContext belgelerine bakın.
Bir güvenlik duvarının arkasındaki redis bağdaştırıcısı için self-signed sertifikalar kullanıyorsanız ve sertifika kontrolünü atlamayı tercih ediyorsanız, ssl verify_mode, OpenSSL::SSL::VERIFY_NONE olarak ayarlanmalıdır.
VERIFY_NONEkullanılması önerilmez. Redis bağdaştırıcısı için bu seçeneği ayarlamak için yapılandırma ssl_params' { verify_mode: <%= OpenSSL::SSL::VERIFY_NONE %> } şeklinde olmalıdır.
PostgreSQL Bağdaştırıcısı
PostgreSQL bağdaştırıcısı, bağlantı için Active Record’un bağlantı havuzunu ve dolayısıyla uygulamanın `config/database.yml’ veritabanı yapılandırmasını kullanır. Bu durum gelecekte değişebilir. #27214
NOTIFY (bildirim göndermek için kullanılan komut) üzerinde 8000 byte sınırı vardır ve bu, büyük yüklerle (payload) uğraşırken bir kısıtlama olabilir.
İzin Verilen İstek Kökenleri
Action Cable yalnızca sunucu yapılandırmasına bir array olarak aktarılan belirtilen kökenlerden (origin) gelen istekleri kabul edecektir. Kökenler, eşleşme için bir kontrolün gerçekleştirileceği string’lerin veya düzenli ifadelerin örnekleri olabilir.
config.action_cable.allowed_request_origins = ["https://rubyonrails.com", %r{http://ruby.*}]
Herhangi bir kökenden gelen istekleri devre dışı bırakmak ve izin vermek için:
config.action_cable.disable_request_forgery_protection = true
Varsayılan olarak Action Cable, geliştirme ortamında çalışırken localhost:3000 adresinden gelen tüm isteklere izin verir.
Tüketici Yapılandırması
URL’i yapılandırmak için HTML layout HEAD’inize action_cable_meta_tag çağrısı ekleyin. Bu, genellikle ortam yapılandırma dosyalarında config.action_cable.url aracılığıyla ayarlanan bir URL veya yol kullanır.
İş Parçacığı Havuzu Yapılandırması
İş parçacığı havuzu, bağlantı callback’lerini ve kanal action’larını sunucunun ana iş parçacığından ayrı olarak çalıştırmak için kullanılır. Action Cable, uygulamanın iş parçacığı havuzunda aynı anda işlenen iş parçacığı sayısını yapılandırmasına olanak tanır.
config.action_cable.worker_pool_size = 4
Ayrıca, sunucunuzun iş parçacığınızla en az aynı sayıda veritabanı bağlantısı sağlaması gerektiğini unutmayın. Varsayılan iş parçacığı havuzu boyutu 4 olarak ayarlanmıştır, bu da en az 4 veritabanı bağlantısını kullanılabilir hale getirmeniz gerektiği anlamına gelir. Bunu config/database.yml içinde pool niteliği aracılığıyla değiştirebilirsiniz.
İstemci Tarafı Log Kaydı
İstemci tarafı log kaydı varsayılan olarak devre dışıdır. Bunu ActionCable.logger.enabled değerini true olarak ayarlayarak etkinleştirebilirsiniz.
import * as ActionCable from '@rails/actioncable'
ActionCable.logger.enabled = true
Diğer Yapılandırmalar
Yapılandırılacak diğer yaygın seçenek ise bağlantı başına log kaydediciye uygulanan günlük etiketleridir. İşte etiketleme sırasında varsa kullanıcı hesabı kimliğini, yoksa “no-account” kullanan bir örnek:
config.action_cable.log_tags = [
-> request { request.env["user_account_id"] || "no-account" },
:action_cable,
-> request { request.uuid }
]
Tüm yapılandırma seçeneklerinin tam listesi için ActionCable::Server::Configuration sınıfına bakın.
Bağımsız Kablo Sunucularını Çalıştırma
Action Cable, Rails uygulamanızın bir parçası olarak ya da bağımsız bir sunucu olarak çalışabilir. Geliştirme aşamasında, Rails uygulamanızın bir parçası olarak çalıştırmak genellikle iyidir, ancak üretim ortamında bağımsız olarak çalıştırmalısınız.
Uygulamada
Action Cable, Rails uygulamanızla birlikte çalışabilir. Örneğin, /websocket üzerindeki WebSocket isteklerini dinlemek için, bu yolu config.action_cable.mount_path olarak belirtin:
# config/application.rb
class Application < Rails::Application
config.action_cable.mount_path = "/websocket"
end
Layout’ta action_cable_meta_tag çağrılmışsa kablo sunucusuna bağlanmak için ActionCable.createConsumer() işlevini kullanabilirsiniz. Aksi takdirde, createConsumer için ilk argüman olarak bir yol belirtilir (örneğin, ActionCable.createConsumer("/websocket")).
Oluşturduğunuz her sunucu örneği ve sunucunuzun ortaya çıkardığı her iş parçacığı için yeni bir Action Cable örneğiniz olacaktır, ancak Redis veya PostgreSQL bağdaştırıcısı mesajları bağlantılar arasında senkronize tutar.
Standalone
Kablo sunucuları normal uygulama sunucunuzdan ayrılabilir. Hâlâ bir Rack uygulamasıdır, ancak kendi Rack uygulamasıdır. Önerilen temel kurulum aşağıdaki gibidir:
# cable/config.ru
require_relative "../config/environment"
Rails.application.eager_load!
run ActionCable.server
Ardından sunucuyu başlatmak için:
$ bundle exec puma -p 28080 cable/config.ru
Bu, 28080 numaralı bağlantı noktasında bir kablo sunucusu başlatır. Rails’e bu sunucuyu kullanmasını söylemek için yapılandırmanızı güncelleyin:
# config/environments/development.rb
Rails.application.configure do
config.action_cable.mount_path = nil
config.action_cable.url = "ws://localhost:28080" # üretime çıkarken wss:// kullanın
end
Son olarak, tüketiciyi doğru şekilde yapılandırdığınızdan emin olun .
Notlar
WebSocket sunucusunun oturuma erişimi yoktur, ancak çerezlere erişimi vardır. Bu, kimlik doğrulama işlemini gerçekleştirmeniz gerektiğinde kullanılabilir. Bu makalede Devise ile bunu yapmanın bir yolunu görebilirsiniz .
Bağımlılıklar
Action Cable, yayınla/abone ol iç bileşenlerini işlemek için bir abonelik bağdaştırıcısı arabirimi (interface) sağlar. Varsayılan olarak; asenkron, satır içi, PostgreSQL ve Redis bağdaştırıcıları dahil edilmiştir. Yeni Rails uygulamalarında varsayılan bağdaştırıcı asenkron (async) bağdaştırıcısıdır.
Ruby tarafı websocket-driver, nio4r ve concurrent-ruby üzerine inşa edilmiştir.
Dağıtım
Action Cable, WebSocket ve iş parçacıklarının bir kombinasyonu tarafından desteklenmektedir. Hem framework tesisatı hem de kullanıcı tarafından belirtilen kanal çalışması Ruby’nin yerel iş parçacığı desteği kullanılarak dahili olarak ele alınır. Bu, herhangi bir iş parçacığı güvenliği günahı işlemediğiniz sürece mevcut tüm Rails modellerinizi sorunsuz bir şekilde kullanabileceğiniz anlamına gelir.
Action Cable sunucusu Rack socket hijacking API’ını uygular, böylece uygulama sunucusunun çok iş parçacıklı olup olmadığına bakılmaksızın bağlantıları dahili olarak yönetmek için çok iş parçacıklı bir modelin kullanılmasına izin verir.
Bu sebeple Action Cable, Unicorn, Puma ve Passenger gibi popüler sunucularla çalışır.
Test
Action Cable işlevselliğinizi nasıl test edeceğinize ilişkin ayrıntılı talimatları Test kılavuzu bölümünde bulabilirsiniz.