Bu makalede Redis kullanarak first-in first-out (FIFO) mantığıyla çalışan, basit bir queue sistemi yapacağız. Queue sistemini sıralı olarak yapılmasını istediğimiz görevler için kullanırız. Bir görevi kuyruğa dahil ettikten sonra sırası geldiğinde kesin olarak çalışmasını bekleriz. Mesela uzun sürecek görevlerin mevcut yazılımın kontrol akışını bloke etmeden yapılabilmesini istiyorsak queue sistemini kesinlikle kullanmamız gerekir.

Redis, queue sistemi için biçilmiş bir kaftandır. Bunun performans ve işlem yetenekleri konusundaki becerilerinden faydalanacağız. Queue sistemi basittir; ilk önce kuyruğa yapılması için yeni bir görev eklenir, daha sonra kuyruktan sırası gelen bir görev tamamlanır ve sıradaki göreve geçilir. Redis’te bu işlem için kullanabileceğimiz çok fazla komut bulunuyor. Ben RPUSH ve BLPOP kullanmayı tercih ediyorum.

Öncelikle RPUSH komutundan başlayalım. Bu komut, Redis’te bir liste oluşturur ve listeye verilen item’leri ekler. Tıpkı programlama dillerindeki hashmap’ler gibi çalışmakta; ortak bir key’e sahiptir ve birden fazla value’ya sahip olabilir.

LPOP, bizim bu yazıda kullanacağımız komut değil ancak ne işe yaradığını bilmeliyiz. LPOP, listeye eklenmiş olan en üstteki itemi siler ve sildiği itemi döndürür. Bu komut için Javascript’teki .shift() method’unu örnek gösterebiliriz.

BLPOP ise tıpkı LPOP komutu gibi çalışır fakat tek farkı, LPOP gibi sonuç bulunsa da bulunmasa da istek anında yanıt vermek yerine, yeni bir kayıt bulana kadar yanıtı bloklar. BLPOP, LPOP’tan farklı olarak timeout için bir argüman alıyor. Burada belirttiğimiz süreye göre bloklama yapar ve süre dolduğunda bulunamazsa nil döndürür. İstersek timeout’a 0 değeri de verebiliriz; bu durumda timeout devre dışı kalır ve kayıt bulunana dek bloklamaya devam eder. Peki neden bunu kullanacağız? Çünkü BLPOP’u sonsuz döngü içerisinde kullanmak istiyorum. Böylece yeni kayıt bulunana kadar loop içerisinde gereksiz istek gönderimini engellemiş olacağız. Eğer LPOP kullansaydık Redis’e saniyede ~7000 sorgu göndermek zorunda kalabilirdik.

Şimdi küçük bir örnek yapalım.

Bir client (Client A) tarafından aşağıdaki BLPOP komutunun kullanıldığını farzedelim. Timeout argümanı gereklidir ve 0 (süresiz) olarak ayarladık.

BLPOP queue/email 0

Bu client şu anda sunucu henüz queue/email adında bir listeye sahip olmadığı için herhangi bir dönüş almıyor.

Şimdi Client B tarafından aşağıdaki RPUSH komutlarının gönderildiğini farzedelim:

RPUSH queue/email '{"email":"iamdual@example.com","type":"welcome"}'
RPUSH queue/email '{"email":"iamdual@example.com","type":"bye"}'
RPUSH queue/sms   '{"phone":"00905301234567","type":"mfa","code":"430808"}'

Bu komutları gönderdikten sonra Client A’ya bakarsak aşağıdaki gibi liste türünde dönüş yapılmış olduğunu görürüz:

1) queue/email
2) {"email":"iamdual@example.com","type":"welcome"}

Bu client, bu dönüşü aldığı anda ilgili item artık listeden silinmiş olur. Böylece LPOP/BLPOP komutlarını farklı client’larda eşzamanlı olarak çalıştırsak bile herkes için farklı değerler dönecektir.

LPOP kullanmış olsaydık (count argümanını kullanmadan) direkt olarak değeri döndürmüş olurdu; fakat loop içerisinde kullanımının dezavantajından bahsetmiştim. BLPOP’ta ise birden fazla liste key’leri girilebildiği için (birisinde bulunamazsa diğerinde vardır belki mantığıyla çalışmakta) liste olarak döndürülmektedir. Dönüşteki liste ikili item grupları halinde verilir. Grubun ilk itemi liste key’ini döndürürken, ikinci itemi ise değerini döndürür.