Open Swoole WebSocket ile Redis PUB/SUB

Merhaba! OpenSwoole kullanarak istemci (client) ve sunucu (server) tarafı arasında iletişim kurmanın yanı sıra Redis pub/sub kullanarak yayınlanan mesajları istemci tarafına iletecek basit bir uygulamayı anlatacağım.

Open Swoole ve Swoole aslında aynılar fakat eklenen bir kod bloğu yüzünden çıkan tartışmadan dolayı Swoole geliştiricilerinden bazıları projeyi çatallayarak Open Swoole olarak geliştirmeye devam ediyorlar.

Open Swoole ile Websocket

Bu bölümde WebSocket ile ilgili bir örneği inceleyeceğiz. Her sunucu sınıfının olayları (events) bulunur. Bu örnekte şu olayları göreceksiniz:

  • Start (Başlatma): $server->start() yönteminin olayını dinler.
  • Open (Açma): İstemci (client) ile sunucu arasındaki bağlantının başladığı olayı dinler.
  • Message (Mesaj): İstemci bir mesaj gönderdiğinde çalışır.
  • Close (Kapatma): İstemci bağlantıyı sonlandırdığında çalışır.

İstemci tarafı için JavaScript WebSocket’i kullanıldı. Detaylarına buradan ulaşabilirsiniz.

Oluşturulan WebSocket bağlantısı $server değişkenine atandı ve her bağlantı kuran istemci için bir kimlik değeri (ID) mevcut. Bu kimlik değeri, bağlantı kurulduğunda (Open olayı) veya istemci tarafından bir mesaj geldiğinde (Message olayı) alınabilir. Bu kimlik değeri önemlidir, çünkü hangi istemciye hangi mesajın gönderileceğini belirlemek için kullanılır.

İstemciye mesaj göndermek istediğinizde $server->push({client_id}, {message}) şeklinde gönderebilirsiniz.

Tüm bağlantı kurulan istemci bilgilerini olay dinleyicilerinde (EventListener) kullanmak için yine OpenSwoole’un Table sınıfını kullanabiliriz.

Bağlantı kurma ve mesaj alıp gönderme işlemlerinden sonra, Redis pub/sub için gerekli ortam artık hazır.

Redis PUB/SUB

Redis Pub/Sub, Redis üzerinden belirli bir kanala abone olan her bir istemciye mesaj göndermenizi sağlayan bir iletişim yöntemidir. Bu, bir veya daha fazla yayımcı (Publisher) ile bir veya daha fazla abone (Subscriber) arasındaki uygulamalar arası iletişim için kullanılır. Yayınlanan bir mesaj yalnızca ilgili bir abone bulunduğunda aboneye ulaşır; mesajlar biriktirilmez veya bir abone ortaya çıkana kadar bekletilmez. Bu sistemi, bir radyo istasyonuna benzetebilirsiniz; ancak bir kanala abone olduğunuzda, sadece o kanalın yayınlarını dinlersiniz.

Redis komut satırı aracılığıyla veya uygulama istemcisi ile mesajları hem yayınlayabilir (publish) hem de abone olabilirsiniz.

Redis Pub/Sub, uygulamanızdaki farklı bileşenler veya mikro hizmetler arasında hızlı ve güvenilir bir iletişim sağlamak için güçlü bir araçtır. Bu yöntemi kullanarak, verilerinizi yayınlayabilir ve farklı parçalardan gelen mesajları aboneler arasında iletebilirsiniz.

Websocket ve PUB/SUB

<?php
declare(strict_types=1);

use OpenSwoole\WebSocket\Server;
use OpenSwoole\Http\Request;
use OpenSwoole\WebSocket\Frame;

require __DIR__ . '/../vendor/autoload.php';

$server = new Server("0.0.0.0", 9502);
$redis = new Redis(['host' => '127.0.0.1', 'port' => 6379, 'readTimeout' => 0, 'connectTimeout' => 0, 'persistent' => true]);
$redis->setOption(Redis::OPT_READ_TIMEOUT,-1);
$server->on('Message', function (Server $server, Frame $frame) use ($redis) {
    $channel = $frame->data;
    $redis->subscribe([$channel], function ($redis, $channel, $message) use ($server, $frame) {
        $server->push($frame->fd, $message);
    });
});

$server->start();

İlk olarak, localhost’ta 9502 numaralı bağlantı noktasını dinleyen bir bağlantı açıyoruz. Ardından Redis istemcisini oluşturuyoruz. Burada dikkat etmemiz gereken önemli bir nokta, $redis->setOption(Redis::OPT_READ_TIMEOUT, -1); kod satırıdır. Bu satırı eklememizin nedeni, timeout değerlerini 0 (sınırsız) olarak ayarlamış olsak bile, PHP’nin soket bağlantıları için varsayılan 60 saniyelik bir zaman sınırlaması olduğunu unutmamaktır. Bu örnekte bu zaman sınırlamasını kaldırdık, ancak bu sınırlama olmadan da çalışmanın yolları bulunmaktadır. PRedis, pub/sub için bir döngü özelliği sunar ve bunu kullanabilirsiniz. Ayrıca, her bir istemci için ayrı bir Redis bağlantısı yerine Open Swoole Pool kullanarak sınırlı bağlantılar oluşturabilir ve sistem sınırlarını aşmamış olursunuz.

Mesaj olayının içinde bu işlemi gerçekleştirme nedenimiz, istemcinin önce abone olmak istediği kanalı göndermesini beklemek ve ardından aboneliğe ait mesajları istemciye iletmektir. Gelen mesajları (json formatında olduğunu varsayalım) işlemek için filtreleme yapabilir ve ne yapılması gerektiğine dair işlemleri yürütebilirsiniz. Ayrıca, bağlantı ilk açıldığında istemciye ait bilgilere erişebiliriz. Open Swoole Table kullanarak istemci bilgilerini kaydedebilir ve daha sonra uygulama senaryonuza göre kullanabilirsiniz.

İstemciden gelen bir mesajda (benim senaryomda sadece abone olmak istediği kanalın bilgisini aldığımızı varsayalım) doğrudan Redis üzerinden abone oluyoruz. Birden fazla istemci için farklı Redis kanallarına abone olabilirsiniz.

Son olarak, Redis-cli üzerinden PUBLISH {kanal} {mesaj} komutunu kullanarak dinleyicilere mesaj gönderebilirsiniz.

Yaşanılan Talihsizlikler

Öncelikle şunu söylemeliyim Open Swoole’u kurmak bir miktar zor olabilir. Tüm yönergelere uysanız dahi bir sürekli saçma sapan problemler çıkıyor. Docker için kendilerinin hazırlamış olduğu bir image mevcut.

Redis tarafında herhangi bir paket kullanmadığım için timeout değerlerini 0(sınırsız) olarak ayarlamış olsam bile redis bağlantısı bir süre sonra kapanıyordu ve yukarıda bahsetmiş olduğum soket bağlantısını sınırsız hale getirerek çözdüm fakat sonrasında bu işler için bir loop olduğunu hatta Open Swoole’un genel olarak kullanılan teknolojiler için Coroutine Clientları olduğunu gördüm. Bunlarıda kullanabilirsiniz.

Ve Diğer Şeyler

Özellikle Websocket kullanımı, Open Swoole’un sunduğu önemli bir özelliktir ve Websocket uygulamaları geliştirmek için oldukça verimlidir. Ayrıca, RoadRunner ve ReactPHP gibi diğer teknolojiler de Websocket uygulamaları için mükemmel seçenekler sunmaktadır.

Redis PUB/SUB ve Websocket’leri kullanarak, istemciler arasında canlı bir bağlantı kurabilirsiniz. Bu tür teknolojiler, mesajlaşma, bildirimler, oyunlar gibi birçok farklı uygulama senaryosunda kullanılabilirler ve muhtemelen pek çok projede zaten kullanılmaktadır.

Eğer daha fazla ayrıntı ve örnek kodlar için ilgileniyorsanız, örnek uygulamananın GitHub bağlantısına göz atabilirsiniz.

Teşekkürler.