Node.js ve Redis Kullanarak Şirket İçinde Bir Pub/Sub Hizmeti Oluşturma

Yayınlanan: 2022-03-10
Hızlı özet ↬ Sistemimizdeki her mesajın veri boyutu birkaç bayttan 100 MB'a kadar farklılık gösterdiğinden, çok sayıda senaryoyu destekleyebilecek ölçeklenebilir bir çözüme ihtiyacımız vardı. Bu makalede, Dhimil Gosalia neden kurum içi bir Pub/Sub hizmeti oluşturmayı da düşünmeniz gerektiğini açıklıyor.

Günümüz dünyası gerçek zamanlı olarak işliyor. İster hisse senedi ticareti yapın ister yemek siparişi verin, bugün tüketiciler anında sonuç bekliyor. Aynı şekilde, ister haberlerde ister sporda olsun, hepimiz her şeyi hemen öğrenmeyi bekleriz. Sıfır, başka bir deyişle, yeni kahraman.

Bu, yazılım geliştiriciler için de geçerlidir - tartışmasız en sabırsız insanlardan bazıları! BrowserStack'in hikayesine dalmadan önce, Pub/Sub hakkında biraz bilgi vermemek benim için kusur olur. Temel bilgilere aşina olanlarınız için sonraki iki paragrafı atlamaktan çekinmeyin.

Günümüzde birçok uygulama gerçek zamanlı veri aktarımına güveniyor. Bir örneğe daha yakından bakalım: sosyal ağlar. Facebook ve Twitter'ın beğenileri ilgili beslemeleri oluşturur ve siz (uygulamaları aracılığıyla) onu tüketir ve arkadaşlarınızı gözetlersiniz. Bunu, bir kullanıcı veri üretirse, başkalarının göz açıp kapayıncaya kadar tüketmesi için gönderileceği bir mesajlaşma özelliği ile gerçekleştirirler. Herhangi bir önemli gecikme ve kullanıcılar şikayet edecek, kullanım düşecek ve devam ederse, tükenecektir. Bahisler yüksek ve kullanıcı beklentileri de öyle. Peki WhatsApp, Facebook, TD Ameritrade, Wall Street Journal ve GrubHub gibi hizmetler yüksek hacimli gerçek zamanlı veri aktarımlarını nasıl destekliyor?

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Hepsi, yaygın olarak Pub/Sub olarak adlandırılan “Yayınla-Abone Ol” modeli adı verilen yüksek düzeyde benzer bir yazılım mimarisi kullanır.

“Yazılım mimarisinde yayınla-abone ol, yayıncı olarak adlandırılan mesaj göndericilerinin, mesajları doğrudan abone adı verilen belirli alıcılara gönderilecek şekilde programlamadığı, bunun yerine yayınlanmış mesajları hangi abonelerin bilgisi olmadan sınıflara ayırdığı bir mesajlaşma modelidir. herhangi biri, olabilir. Benzer şekilde, aboneler bir veya daha fazla sınıfa ilgi gösterirler ve eğer varsa, hangi yayıncıların olduğunu bilmeden yalnızca ilgilendikleri mesajları alırlar.“

— Vikipedi

Tanımdan sıkıldınız mı? Hikayemize dönelim.

BrowserStack'te tüm ürünlerimiz (şu ya da bu şekilde) önemli bir gerçek zamanlı bağımlılık bileşenine sahip yazılımları destekler - otomatikleştirme testleri günlükleri, yeni hazırlanmış tarayıcı ekran görüntüleri veya 15 fps mobil akış olsun.

Bu gibi durumlarda, tek bir mesaj düşerse, müşteri bir hatayı önlemek için hayati önem taşıyan bilgileri kaybedebilir . Bu nedenle, çeşitli veri boyutu gereksinimleri için ölçeklendirmemiz gerekiyordu. Örneğin, belirli bir zamanda cihaz kaydedici hizmetlerinde, tek bir mesaj altında oluşturulan 50 MB veri olabilir. Bunun gibi boyutlar tarayıcıyı çökertebilir. BrowserStack'in sisteminin gelecekte ek ürünler için ölçeklenmesi gerekeceğinden bahsetmiyorum bile.

Her ileti için veri boyutu birkaç bayttan 100 MB'a kadar farklılık gösterdiğinden, çok sayıda senaryoyu destekleyebilecek ölçeklenebilir bir çözüme ihtiyacımız vardı. Başka bir deyişle, tüm pastaları kesebilecek bir kılıç aradık. Bu yazıda Pub/Sub hizmetimizi kendi bünyemizde kurmanın neden, nasıl ve sonuçlarından bahsedeceğim.

BrowserStack'in gerçek dünya sorununun merceğinden, kendi Pub/Sub'ınızı oluşturma gereksinimleri ve süreci hakkında daha derin bir anlayış elde edeceksiniz.

Pub/Sub Hizmet İhtiyacımız

BrowserStack, her biri yaklaşık 2 bayt ile 100+ MB arasında bir yerde olan yaklaşık 100 milyondan fazla mesaja sahiptir. Bunlar her an dünyanın her yerine farklı İnternet hızlarında iletilir.

Bu mesajların mesaj boyutuna göre en büyük oluşturucuları BrowserStack Automate ürünlerimizdir. Her ikisinde de, bir kullanıcı testinin her komutu için tüm istekleri ve yanıtları görüntüleyen gerçek zamanlı panolar bulunur. Dolayısıyla, ortalama istek-yanıt boyutunun 10 bayt olduğu 100 istekle bir test çalıştırılırsa, bu 1×100×10 = 1000 bayt iletir.

Şimdi büyük resmi düşünelim - tabii ki - günde sadece bir test yapmıyoruz. BrowserStack ile her gün yaklaşık 850.000'den fazla BrowserStack Automate ve App Automate testi gerçekleştirilir. Ve evet, test başına ortalama 235 istek-yanıt alıyoruz. Kullanıcılar Selenium'da ekran görüntüsü alabildiğinden veya sayfa kaynakları isteyebildiğinden, ortalama istek-yanıt boyutumuz yaklaşık 220 bayttır.

Hesap makinemize geri dönersek:

850.000×235×220 = 43,945,000,000 bayt (yaklaşık) veya günde yalnızca 43.945 GB

Şimdi BrowserStack Live ve App Live hakkında konuşalım. Elbette, veri boyutu biçiminde kazananımız olarak Automate'e sahibiz. Ancak, iletilen mesaj sayısı söz konusu olduğunda Live ürünleri başı çekiyor. Her canlı test için, döndüğü her dakika yaklaşık 20 mesaj iletilir. Yaklaşık 100.000 canlı test yapıyoruz ve her testin ortalama 12 dakikası şu anlama geliyor:

100.000×12×20 = günde 24.000.000 mesaj

Şimdi harika ve dikkat çekici bit için: 6 t1.micro ec2 örneği ile bu itici adı verilen uygulamayı oluşturuyor, çalıştırıyor ve bakımını yapıyoruz. Hizmeti çalıştırmanın maliyeti? Ayda yaklaşık 70 dolar.

Satın Alma ve İnşa Etmeyi Seçme

İlk önce ilk şeyler: Bir startup olarak, çoğu diğerleri gibi biz de her zaman kendi bünyemizde bir şeyler inşa etmekten heyecan duyduk. Ama yine de orada birkaç hizmeti değerlendirdik. Sahip olduğumuz birincil gereksinimler şunlardı:

  1. Güvenilirlik ve istikrar,
  2. Yüksek performans ve
  3. Maliyet etkinliği.

Aylık maliyeti 70 doların altında olan herhangi bir harici hizmet düşünemediğim için maliyet-etkililik kriterlerini bir yana bırakalım (bunu biliyorsan beni tweetle!). Yani oradaki cevabımız belli.

Güvenilirlik ve istikrar açısından, Pub/Sub'ı hizmet olarak yüzde 99,9+ çalışma süresi SLA'sı ile sağlayan şirketler bulduk, ancak ekli birçok Şart ve Koşul vardı. Sorun, düşündüğünüz kadar basit değil, özellikle de sistem ve istemci arasında uzanan geniş açık İnternet topraklarını düşündüğünüzde. İnternet altyapısına aşina olan herkes, kararlı bağlantının en büyük zorluk olduğunu bilir. Ek olarak, gönderilen veri miktarı trafiğe bağlıdır. Örneğin, bir dakika boyunca sıfırda olan bir veri hattı, bir sonraki dakika boyunca patlayabilir. Bu tür patlama anlarında yeterli güvenilirlik sağlayan hizmetler nadirdir (Google ve Amazon).

Projemiz için performans, sıfıra yakın gecikmeyle tüm dinleme düğümlerine veri almak ve göndermek anlamına gelir. BrowserStack'te, ortak konum barındırma ile birlikte bulut hizmetlerini (AWS) kullanırız. Ancak yayıncılarımız ve/veya abonelerimiz herhangi bir yere yerleştirilebilir. Örneğin, çok ihtiyaç duyulan günlük verilerini üreten bir AWS uygulama sunucusunu veya terminalleri (kullanıcıların test için güvenli bir şekilde bağlanabileceği makineler) içerebilir. Açık İnternet sorununa tekrar dönecek olursak, riskimizi azaltacak olsaydık, Pub/Sub'ın en iyi barındırma hizmetlerini ve AWS'yi kullandığından emin olmamız gerekirdi.

Diğer bir temel gereklilik ise her türlü veriyi (Bayt, metin, garip medya verileri vb.) iletebilme yeteneğiydi. Her şey düşünüldüğünde, ürünlerimizi desteklemek için üçüncü taraf bir çözüme güvenmek mantıklı değildi. Biz de kendi çözümümüzü kodlamak için kolları sıvayarak startup ruhumuzu canlandırmaya karar verdik.

Çözümümüzü Oluşturmak

Tasarım gereği Pub/Sub, veri üreten ve gönderen bir yayıncı ile bunları kabul eden ve işleyen bir Abone olacağı anlamına gelir. Bu bir radyoya benzer: Bir radyo kanalı, bir aralık içinde her yerde içerik yayınlar (yayınlar). Abone olarak, o kanalı açıp dinlememeye (veya radyonuzu tamamen kapatmaya) karar verebilirsiniz.

Verilerin herkes için ücretsiz olduğu ve herkesin bağlantı kurmaya karar verebileceği radyo benzetmesinden farklı olarak, dijital senaryomuzda doğrulamaya ihtiyacımız var, bu da yayıncı tarafından oluşturulan verilerin yalnızca belirli bir müşteri veya abone için olabileceği anlamına geliyor.

Pub/Sub'ın temel çalışması
Pub/Sub'ın temel çalışması (Büyük önizleme)

Yukarıda, aşağıdakilere sahip iyi bir Pub/Sub örneği sağlayan bir şema verilmiştir:

  • yayıncılar
    Burada, önceden tanımlanmış mantığa göre mesajlar üreten iki yayıncımız var. Radyo benzetmemizde bunlar, içeriği oluşturan radyo jokeylerimizdir.
  • Konular
    Burada iki tane var, yani iki tür veri var. Bunlar bizim radyo kanallarımız 1 ve 2 diyebiliriz.
  • aboneler
    Her biri belirli bir konudaki verileri okuyan üç tane var. Dikkat edilmesi gereken bir şey, Abone 2'nin birden çok konudan okumasıdır. Radyo benzetmemizde, bunlar bir radyo kanalına ayarlanmış kişilerdir.

Hizmet için gerekli gereksinimleri anlamaya başlayalım.

  1. Olaylı bir bileşen
    Bu, yalnızca tekmelenecek bir şey olduğunda devreye girer.
  2. Geçici depolama
    Bu, verileri kısa bir süre için kalıcı tutar, böylece abone yavaşsa, tüketmek için hala bir penceresi vardır.
  3. Gecikmeyi azaltmak
    Minimum atlama ve mesafe ile bir ağ üzerinden iki varlığı birbirine bağlamak.

Yukarıdaki gereksinimleri karşılayan bir teknoloji yığını seçtik:

  1. Node.js
    Çünkü neden olmasın? Olay, yoğun veri işlemeye ihtiyacımız olmaz, ayrıca sisteme dahil edilmesi kolaydır.
  2. redis
    Mükemmel kısa ömürlü verileri destekler. Başlatmak, güncellemek ve otomatik olarak sona ermek için tüm yeteneklere sahiptir. Ayrıca uygulamaya daha az yük bindirir.

İş Mantığı Bağlantısı İçin Node.js

Node.js, IO ve olayları içeren kod yazmak söz konusu olduğunda neredeyse mükemmel bir dildir. Belirli sorunumuz her ikisine de sahipti ve bu seçeneği ihtiyaçlarımız için en pratik hale getirdi.

Elbette Java gibi diğer diller daha optimize edilebilir veya Python gibi bir dil ölçeklenebilirlik sunar. Ancak, bu dillerle başlamanın maliyeti o kadar yüksektir ki, bir geliştirici aynı sürede Node'da kod yazmayı bitirebilir.

Dürüst olmak gerekirse, hizmetin daha karmaşık özellikler ekleme şansı olsaydı, diğer dillere veya tamamlanmış bir yığına bakabilirdik. Ama burada cennette yapılan bir evlilik var. İşte paketimiz.json :

 { "name": "Pusher", "version": "1.0.0", "dependencies": { "bstack-analytics": "*****", // Hidden for BrowserStack reasons. :) "ioredis": "^2.5.0", "socket.io": "^1.4.4" }, "devDependencies": {}, "scripts": { "start": "node server.js" } }

Basitçe söylemek gerekirse, özellikle kod yazma söz konusu olduğunda minimalizme inanıyoruz. Öte yandan, bu proje için genişletilebilir kod yazmak için Express gibi kütüphaneleri kullanabilirdik. Ancak startup içgüdülerimiz bunu aktarmaya ve bir sonraki projeye saklamaya karar verdi. Kullandığımız ek araçlar:

  • ıoredis
    Bu, Alibaba dahil şirketler tarafından kullanılan Node.js ile Redis bağlantısı için en çok desteklenen kitaplıklardan biridir.
  • soket.io
    WebSocket ve HTTP ile zarif bağlantı ve geri dönüş için en iyi kitaplık.

Geçici Depolama için Redis

Hizmet ölçeği olarak Redis, son derece güvenilir ve yapılandırılabilir. Ayrıca, AWS dahil olmak üzere Redis için birçok güvenilir yönetilen hizmet sağlayıcı vardır. Bir sağlayıcı kullanmak istemeseniz bile, Redis'i kullanmaya başlamak kolaydır.

Yapılandırılabilir kısmı parçalayalım. Her zamanki master-slave konfigürasyonuyla başladık, ancak Redis ayrıca küme veya sentinel modlarıyla birlikte geliyor. Her modun kendine göre avantajları vardır.

Verileri bir şekilde paylaşabilseydik, en iyi seçim bir Redis kümesi olurdu. Ancak, verileri herhangi bir buluşsal yöntemle paylaşırsak, buluşsal yöntemin baştan sona izlenmesi gerektiğinden daha az esnekliğe sahibiz . Daha az kural, daha fazla kontrol yaşam için iyidir!

Redis Sentinel bizim için en iyi sonucu verir, çünkü veri arama tek bir düğümde yapılır ve veriler parçalanmadan belirli bir zamanda bağlanır. Bu aynı zamanda, birden fazla düğüm kaybolsa bile, verilerin hala dağıtıldığı ve diğer düğümlerde bulunduğu anlamına gelir. Yani daha fazla HA ve daha az kayıp şansınız var. Tabii ki, bu, profesyonelleri bir kümeye sahip olmaktan çıkardı, ancak kullanım durumumuz farklı.

30000 Feet'te Mimari

Aşağıdaki şema, Automate ve App Automate panolarımızın nasıl çalıştığına dair çok üst düzey bir resim sunmaktadır. Önceki bölümdeki gerçek zamanlı sistemi hatırlıyor musunuz?

BrowserStack'in gerçek zamanlı Otomatikleştirme ve Uygulama Otomatikleştirme panoları.
BrowserStack'in gerçek zamanlı Otomatikleştirme ve Uygulama Otomatikleştirme panoları (Geniş önizleme)

Diyagramımızda ana iş akışımız daha kalın kenarlıklarla vurgulanmıştır. “Otomatikleştirme” bölümü şunlardan oluşur:

  1. Terminaller
    BrowserStack üzerinde test ederken edindiğiniz Windows, OSX, Android veya iOS'un bozulmamış sürümlerinden oluşur.
  2. merkez
    BrowserStack ile tüm Selenium ve Appium testleriniz için iletişim noktası.

Buradaki "kullanıcı hizmeti" bölümü, verilerin doğru kişiye gönderilmesini ve doğru kişiye kaydedilmesini sağlayan kapı bekçimizdir. Aynı zamanda bizim güvenlik görevlimizdir. "İtici" bölümü, bu makalede tartıştığımız şeyin özünü içerir. Aşağıdakiler dahil olağan şüphelilerden oluşur:

  1. redis
    Bizim durumumuzda otomatikleştirme günlüklerinin geçici olarak depolandığı mesajlar için geçici depolamamız.
  2. Yayımcı
    Bu, temel olarak hub'dan veri alan varlıktır. Tüm istek yanıtlarınız, kanal olarak session_id ile Redis'e yazan bu bileşen tarafından yakalanır.
  3. Abone
    Bu, session_id için oluşturulan Redis'ten gelen verileri okur. Aynı zamanda, istemcilerin WebSocket (veya HTTP) aracılığıyla bağlanıp veri almaları ve ardından bunları kimliği doğrulanmış istemcilere göndermeleri için web sunucusudur.

Son olarak, session_id günlüklerinin gönderilmesini sağlamak için kimliği doğrulanmış bir WebSocket bağlantısını temsil eden kullanıcının tarayıcı bölümüne sahibiz. Bu, ön uç JS'nin onu kullanıcılar için ayrıştırmasını ve güzelleştirmesini sağlar.

Günlük hizmetine benzer şekilde, burada diğer ürün entegrasyonları için kullanılan iticimiz var. session_id yerine, o kanalı temsil etmek için başka bir kimlik biçimi kullanırız. Bunların hepsi iticiden çalışıyor!

Sonuç (TLDR)

Pub/Sub'ı geliştirmede önemli bir başarı elde ettik. Neden kendi bünyemizde inşa ettiğimizi özetlemek gerekirse:

  1. İhtiyaçlarımız için daha iyi ölçeklenir;
  2. Dış kaynaklı hizmetlerden daha ucuz;
  3. Genel mimari üzerinde tam kontrol.

JS'nin bu tür bir senaryo için mükemmel bir seçim olduğundan bahsetmiyorum bile. Olay döngüsü ve büyük miktarda IO, sorunun ihtiyacı olan şeydir! JavaScript, tek sözde iş parçacığının büyüsüdür.

Bir sistem olarak Olaylar ve Redis, verileri bir kaynaktan alıp Redis aracılığıyla başka bir kaynağa aktarabileceğiniz için geliştiriciler için işleri basitleştirir. Biz de inşa ettik.

Kullanım sisteminize uyuyorsa, aynısını yapmanızı tavsiye ederim!