Dağıtılmış uygulamaların yapı taşları. İkinci yaklaşım

Duyuru

Meslektaşlarım, yaz ortasında kuyruk sistemlerinin tasarımı hakkında başka bir makale dizisi yayınlamayı planlıyorum: "VTrade Deneyi" - ticaret sistemleri için bir çerçeve yazma girişimi. Dizide borsa, müzayede ve mağaza kurmanın teori ve uygulamaları incelenecek. Yazının sonunda sizleri en çok ilginizi çeken konulara oy vermeye davet ediyorum.

Dağıtılmış uygulamaların yapı taşları. İkinci yaklaşım

Bu, Erlang/Elixir'deki dağıtılmış reaktif uygulamalar hakkındaki serinin son makalesidir. İÇİNDE ilk makale reaktif mimarinin teorik temellerini bulabilirsiniz. İkinci makale bu tür sistemlerin oluşturulmasına yönelik temel modelleri ve mekanizmaları göstermektedir.

Bugün kod tabanının ve genel olarak projelerin geliştirilmesi konularını gündeme getireceğiz.

Hizmetlerin organizasyonu

Gerçek hayatta, bir hizmet geliştirirken çoğu zaman birkaç etkileşim modelini tek bir denetleyicide birleştirmeniz gerekir. Örneğin, proje kullanıcı profillerinin yönetilmesi sorununu çözen kullanıcı hizmetinin, req-resp isteklerine yanıt vermesi ve profil güncellemelerini pub-sub aracılığıyla raporlaması gerekir. Bu durum oldukça basittir: Mesajlaşmanın arkasında hizmet mantığını uygulayan ve güncellemeleri yayınlayan bir denetleyici vardır.

Hataya dayanıklı bir dağıtılmış hizmet uygulamamız gerektiğinde durum daha da karmaşıklaşıyor. Kullanıcılara yönelik gereksinimlerin değiştiğini düşünelim:

  1. Artık hizmetin istekleri 5 küme düğümünde işlemesi gerekiyor,
  2. arka planda işleme görevlerini gerçekleştirebilecek,
  3. ve ayrıca profil güncellemeleri için abonelik listelerini dinamik olarak yönetebileceksiniz.

sözler: Tutarlı depolama ve veri çoğaltma konusunu dikkate almıyoruz. Bu sorunların daha önce çözüldüğünü ve sistemin zaten güvenilir ve ölçeklenebilir bir depolama katmanına sahip olduğunu ve işleyicilerin bununla etkileşime girecek mekanizmalarının olduğunu varsayalım.

Kullanıcı hizmetinin resmi açıklaması daha karmaşık hale geldi. Bir programcının bakış açısından, mesajlaşma kullanımından dolayı değişiklikler minimum düzeydedir. İlk gereksinimi karşılamak için req-resp değişim noktasında dengelemeyi yapılandırmamız gerekir.

Arka plan görevlerini işleme gereksinimi sıklıkla ortaya çıkar. Kullanıcılarda bu, kullanıcı belgelerini kontrol etmek, indirilen multimedyayı işlemek veya verileri sosyal medyayla senkronize etmek olabilir. ağlar. Bu görevlerin küme içinde bir şekilde dağıtılması ve yürütmenin ilerlemesinin izlenmesi gerekir. Bu nedenle iki çözüm seçeneğimiz var: Ya önceki makaledeki görev dağıtım şablonunu kullanın ya da uygun değilse işlemci havuzunu ihtiyacımız olan şekilde yönetecek özel bir görev zamanlayıcı yazın.

3. nokta, pub-sub şablon uzantısını gerektirir. Ve uygulama için pub-sub değişim noktası oluşturduktan sonra, bu noktanın denetleyicisini hizmetimiz içerisinde ek olarak başlatmamız gerekiyor. Böylece abonelikleri işleme ve abonelikten çıkma mantığını mesajlaşma katmanından kullanıcıların uygulamasına taşıyormuşuz gibi.

Sonuç olarak, sorunun ayrıştırılması, gereksinimleri karşılamak için hizmetin 5 örneğini farklı düğümlerde başlatmamız ve abonelikten sorumlu bir pub-sub denetleyicisi olan ek bir varlık oluşturmamız gerektiğini gösterdi.
5 işleyiciyi çalıştırmak için servis kodunu değiştirmenize gerek yoktur. Tek ek eylem, biraz sonra konuşacağımız değişim noktasında dengeleme kuralları oluşturmaktır.
Ayrıca ek bir karmaşıklık daha vardır: pub-sub denetleyicisi ve özel görev zamanlayıcının tek bir kopya halinde çalışması gerekir. Tekrar ediyorum, mesajlaşma hizmeti temel olarak bir lider seçmeye yönelik bir mekanizma sağlamalıdır.

Liderin Seçimi

Dağıtılmış sistemlerde lider seçimi, bazı yüklerin dağıtılmış işlenmesinin planlanmasından sorumlu tek bir sürecin atanması prosedürüdür.

Merkezileşmeye yatkın olmayan sistemlerde paxos veya raft gibi evrensel ve fikir birliğine dayalı algoritmalar kullanılır.
Mesajlaşma bir komisyoncu ve merkezi bir unsur olduğundan, tüm hizmet denetleyicileri - aday liderler - hakkında bilgi sahibi olur. Mesajlaşma, oylamaya gerek kalmadan bir lider atayabilir.

Değişim noktasına bağlanıp bağlandıktan sonra tüm hizmetler bir sistem mesajı alır #'$leader'{exchange = ?EXCHANGE, pid = LeaderPid, servers = Servers}. Eğer LeaderPid ile çakışır pid Mevcut süreçte lider olarak atanır ve liste Servers tüm düğümleri ve bunların parametrelerini içerir.
Yeni bir küme düğümü göründüğünde ve çalışan bir küme düğümünün bağlantısı kesildiğinde, tüm hizmet denetleyicileri #'$slave_up'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} и #'$slave_down'{exchange = ?EXCHANGE, pid = SlavePid, options = SlaveOpts} sırasıyla.

Bu şekilde tüm bileşenler tüm değişikliklerden haberdar olur ve kümenin herhangi bir zamanda bir lidere sahip olması garanti edilir.

aracılar

Karmaşık dağıtılmış işleme süreçlerini uygulamak ve mevcut mimariyi optimize etme problemlerinde aracıların kullanılması uygundur.
Hizmet kodunu değiştirmemek ve örneğin ek işleme, yönlendirme veya mesaj günlüğe kaydetme sorunlarını çözmek için, hizmetten önce tüm ek işleri gerçekleştirecek bir proxy işleyicisini etkinleştirebilirsiniz.

Pub-sub optimizasyonunun klasik bir örneği, pazardaki fiyat değişiklikleri gibi güncelleme olaylarını üreten bir iş çekirdeğine ve web istemcileri için bir websocket API sağlayan bir erişim katmanına (N) sahip sunuculara sahip dağıtılmış bir uygulamadır.
Doğrudan karar verirseniz, müşteri hizmetleri şöyle görünür:

  • istemci platformla bağlantılar kurar. Trafiği sonlandıran sunucu tarafında bu bağlantıya hizmet verecek bir işlem başlatılır.
  • Hizmet süreci kapsamında yetkilendirme ve güncelleme aboneliği gerçekleşir. Süreç, konular için abone olma yöntemini çağırır.
  • Çekirdekte bir olay oluşturulduktan sonra bağlantılara hizmet veren süreçlere iletilir.

“Haber” konusuna 50000 abonemiz olduğunu düşünelim. Aboneler 5 sunucuya eşit olarak dağıtılır. Sonuç olarak, değişim noktasına gelen her güncelleme 50000 kez kopyalanacaktır: abone sayısına göre her sunucuda 10000 kez. Çok etkili bir plan değil, değil mi?
Durumu iyileştirmek için değişim noktasıyla aynı ada sahip bir proxy tanıtalım. Global ad kayıt kuruluşu, en yakın süreci ada göre döndürebilmelidir, bu önemlidir.

Bu proxy'yi erişim katmanı sunucularında başlatalım ve websocket api'ye hizmet veren tüm süreçlerimiz, çekirdekteki orijinal pub-sub değişim noktasına değil, ona abone olacaktır. Proxy, yalnızca benzersiz bir abonelik durumunda çekirdeğe abone olur ve gelen mesajı tüm abonelerine kopyalar.
Sonuç olarak çekirdek ile erişim sunucuları arasında 5 yerine 50000 mesaj gönderilecek.

Yönlendirme ve dengeleme

Talep-Yanıt

Mevcut mesajlaşma uygulamasında 7 istek dağıtım stratejisi vardır:

  • default. İstek tüm denetleyicilere gönderilir.
  • round-robin. İstekler numaralandırılır ve denetleyiciler arasında döngüsel olarak dağıtılır.
  • consensus. Hizmete hizmet eden denetleyiciler liderler ve köleler olarak ikiye ayrılır. İstekler yalnızca lidere gönderilir.
  • consensus & round-robin. Grubun bir lideri vardır ancak istekler tüm üyeler arasında dağıtılır.
  • sticky. Karma işlevi hesaplanır ve belirli bir işleyiciye atanır. Bu imzayla sonraki istekler aynı işleyiciye gider.
  • sticky-fun. Değişim noktası başlatılırken karma hesaplama işlevi sticky dengeleme.
  • fun. Yapışkan eğlenceye benzer şekilde, yalnızca siz onu yeniden yönlendirebilir, reddedebilir veya ön işleme tabi tutabilirsiniz.

Dağıtım stratejisi, değişim noktası başlatıldığında ayarlanır.

Dengelemenin yanı sıra mesajlaşma, varlıkları etiketlemenize olanak tanır. Sistemdeki etiket türlerine bakalım:

  • Bağlantı etiketi. Olayların hangi bağlantıdan geldiğini anlamanızı sağlar. Bir denetleyici işlemi aynı değişim noktasına ancak farklı yönlendirme anahtarlarıyla bağlandığında kullanılır.
  • Servis etiketi. İşleyicileri tek bir hizmet için gruplar halinde birleştirmenize ve yönlendirme ve dengeleme yeteneklerini genişletmenize olanak tanır. Req-resp modeli için yönlendirme doğrusaldır. Değişim noktasına bir istek gönderiyoruz, o da bunu servise iletiyor. Ancak işleyicileri mantıksal gruplara ayırmamız gerekirse, bu bölme etiketleri kullanılarak yapılır. Bir etiket belirlerken istek belirli bir denetleyici grubuna gönderilecektir.
  • Etiket isteyin. Cevaplar arasında ayrım yapmanızı sağlar. Sistemimiz eşzamansız olduğundan, hizmet yanıtlarını işlemek için istek gönderirken bir requestTag belirtebilmemiz gerekir. Ondan bize hangi isteğin geldiğinin cevabını anlayabileceğiz.

Pub-sub

Pub-sub için her şey biraz daha basit. Mesajların yayınlandığı bir değişim noktamız var. Değişim noktası, ihtiyaç duydukları yönlendirme anahtarlarına abone olan aboneler arasında mesajları dağıtır (bunun konulara benzediğini söyleyebiliriz).

Ölçeklenebilirlik ve hata toleransı

Sistemin bir bütün olarak ölçeklenebilirliği, sistem katmanlarının ve bileşenlerinin ölçeklenebilirlik derecesine bağlıdır:

  • Hizmetler, bu hizmet için işleyicilerin bulunduğu kümeye ek düğümler eklenerek ölçeklendirilir. Deneme çalışması sırasında en uygun dengeleme politikasını seçebilirsiniz.
  • Mesajlaşma hizmetinin kendisi ayrı bir küme içinde genellikle ya özellikle yüklü değişim noktalarının ayrı küme düğümlerine taşınmasıyla ya da kümenin özellikle yüklü alanlarına proxy işlemlerinin eklenmesiyle ölçeklenir.
  • Bir özellik olarak tüm sistemin ölçeklenebilirliği, mimarinin esnekliğine ve bireysel kümeleri ortak bir mantıksal varlıkta birleştirme yeteneğine bağlıdır.

Bir projenin başarısı genellikle ölçeklendirmenin basitliğine ve hızına bağlıdır. Mevcut sürümündeki mesajlaşma, uygulamayla birlikte büyüyor. 50-60 makinelik kümemiz olmasa bile federasyona başvurabiliriz. Ne yazık ki federasyon konusu bu yazının kapsamı dışındadır.

Rezervasyon

Yük dengelemeyi analiz ederken servis denetleyicilerinin yedekliliğini zaten tartıştık. Ancak mesajlaşmanın da rezerve edilmesi gerekir. Bir düğüm veya makinenin çökmesi durumunda mesajlaşma otomatik olarak ve mümkün olan en kısa sürede kurtarılmalıdır.

Projelerimde düşme durumunda yükü kaldıracak ek düğümler kullanıyorum. Erlang, OTP uygulamaları için standart bir dağıtılmış mod uygulamasına sahiptir. Dağıtılmış mod, arıza durumunda, başarısız uygulamayı daha önce başlatılan başka bir düğümde başlatarak kurtarma işlemini gerçekleştirir. Süreç şeffaftır; bir arıza sonrasında uygulama otomatik olarak yük devretme düğümüne geçer. Bu işlevsellik hakkında daha fazlasını okuyabilirsiniz burada.

Proizvoditelnost

Rabbitmq'in performansını ve özel mesajlaşmamızı en azından kabaca karşılaştırmaya çalışalım.
buldum resmi sonuçlar openstack ekibinden tavşan mq testi.

Paragraf 6.14.1.2.1.2.2'de. Orijinal belge RPC CAST sonucunu gösterir:
Dağıtılmış uygulamaların yapı taşları. İkinci yaklaşım

İşletim sistemi çekirdeğine veya erlang VM'ye önceden herhangi bir ek ayar yapmayacağız. Test koşulları:

  • erl seçenekleri: +A1 +sbtu.
  • Tek bir erlang düğümü içindeki test, mobil sürümde eski bir i7'ye sahip bir dizüstü bilgisayarda gerçekleştirilir.
  • Küme testleri 10G ağına sahip sunucularda gerçekleştirilir.
  • Kod liman işçisi konteynerlerinde çalışır. NAT modunda ağ.

Test kodu:

req_resp_bench(_) ->
  W = perftest:comprehensive(10000,
    fun() ->
      messaging:request(?EXCHANGE, default, ping, self()),
      receive
        #'$msg'{message = pong} -> ok
      after 5000 ->
        throw(timeout)
      end
    end
  ),
  true = lists:any(fun(E) -> E >= 30000 end, W),
  ok.

1 betiği: Test, eski i7 mobil sürümüne sahip bir dizüstü bilgisayarda gerçekleştirilir. Test, mesajlaşma ve hizmet, tek bir Docker kapsayıcısındaki bir düğümde yürütülür:

Sequential 10000 cycles in ~0 seconds (26987 cycles/s)
Sequential 20000 cycles in ~1 seconds (26915 cycles/s)
Sequential 100000 cycles in ~4 seconds (26957 cycles/s)
Parallel 2 100000 cycles in ~2 seconds (44240 cycles/s)
Parallel 4 100000 cycles in ~2 seconds (53459 cycles/s)
Parallel 10 100000 cycles in ~2 seconds (52283 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (49317 cycles/s)

2 betiği: Docker (NAT) altında farklı makinelerde çalışan 3 düğüm.

Sequential 10000 cycles in ~1 seconds (8684 cycles/s)
Sequential 20000 cycles in ~2 seconds (8424 cycles/s)
Sequential 100000 cycles in ~12 seconds (8655 cycles/s)
Parallel 2 100000 cycles in ~7 seconds (15160 cycles/s)
Parallel 4 100000 cycles in ~5 seconds (19133 cycles/s)
Parallel 10 100000 cycles in ~4 seconds (24399 cycles/s)
Parallel 100 100000 cycles in ~3 seconds (34517 cycles/s)

Her durumda CPU kullanımı %250'yi aşmadı

sonuçlar

Umarım bu döngü bir zihin çöplüğü gibi görünmez ve deneyimlerim hem dağıtılmış sistem araştırmacıları hem de kendi iş sistemleri için dağıtılmış mimariler oluşturmanın başlangıcında olan ve Erlang/Elixir'e ilgiyle bakan uygulayıcılar için gerçekten faydalı olacaktır. , ama buna değer mi diye şüphelerim var...

Fotoğraf @chuttersnap

Ankete sadece kayıtlı kullanıcılar katılabilir. Giriş yapLütfen.

VTrade Experiment serisinin bir parçası olarak hangi konuları daha ayrıntılı olarak ele almalıyım?

  • Teori: Piyasalar, emirler ve zamanlamaları: DAY, GTD, GTC, IOC, FOK, MOO, MOC, LOO, LOC

  • Sipariş kitabı. Gruplandırmalarla bir kitabın uygulanması teorisi ve pratiği

  • Ticaretin görselleştirilmesi: Tikler, çubuklar, kararlar. Nasıl saklanır ve nasıl yapıştırılır

  • Arka ofis. Planlama ve geliştirme. Çalışan izleme ve olay soruşturması

  • API'dir. Hangi arayüzlerin gerekli olduğunu ve bunların nasıl uygulanacağını bulalım

  • Bilgi depolama: Ticaret sistemlerinde PostgreSQL, Timescale, Tarantool

  • Ticaret sistemlerinde reaktivite

  • Diğer. Yorumlara yazacağım

6 kullanıcı oy kullandı. 4 kullanıcı çekimser kaldı.

Kaynak: habr.com

Yorum ekle