İçeriğe atla
megapromotingHadi konuşalım

Karmaşık mimarilerde uzmanlık

Birbirleriyle birlikte çalışması gereken birden fazla sistem, bunlardan biri devre dışı kaldığında bile.

Mesaj omurgası, sınırlı yeniden denemeler, idempotency anahtarları, circuit breaker’lar ve otomatik geri dönüşlü yayınlama ile çok hizmetli platformlar tasarlıyor ve işletiyoruz. Aşağıdaki her kalıp bir diyagramda değil, kendi sisteminde çalışır.

Zaten kurduklarımızOperăm patru platforme proprii cu arhitecturi diferite și le putem deschide pe toate. Cifrele sunt măsurate azi cu comenzi, nu preluate din documentație: aichat are 28 de directoare de serviciu, 235 de modele de date pe patru scheme și o magistrală de mesaje cu 297 de cozi și 17.525 de legături; Kallina are 472 de funcții edge, 559 de migrări și 28 de sarcini programate în bază; MEGA CRM are un backend de 9.516 linii cu 99 de rute și 179 de politici de acces pe rând; Taskin rulează 21 de sarcini programate fără niciun Docker. Rezerva pe care o spunem noi: în două locuri documentația proprie a rămas în urma codului — o afirmație despre numărul de linii era depășită cu 16%, iar o diagramă de infrastructură descria un server pe care nu mai rulăm nimic. Am folosit măsurătoarea, nu documentul.

„Karmaşık mimari” çizimde çok sayıda kutu demek değildir. Kutulardan biri yanıt vermediğinde ne olduğunu bilmek demektir. Üç servisin senkron çağrıldığı, yeniden deneme sınırı olmayan ve idempotency bulunmayan bir sistem, monolitten daha kırılgandır — çünkü her yeni bağlantı yeni bir hata modu demektir ve hata modları, işlevlerden daha hızlı çoğalır.

Çalışmamız sorumlulukların ayrılmasından başlar ve bir şey bozulduğunda görünen yerde biter. Kendi platformlarımızdan birinde mesajlar servisler arasında doğrudan geçmez, 297 kuyruk, 9 değişim ve 17.525 bağlantıdan oluşan bir mesaj omurgasından geçer; bunların 278 kuyruğu müşteri başına üretilir, elle yazılmaz. Sekiz kuyrukta ölü mektup değişimi vardır ve sekizinde mesaj yaşam süresi beş dakikadır — yani işlenemeyen bir mesaj görülebileceği bir yere gider, kaybolmaz.

Kurduğumuz kalıplar azdır ve tekrar eder: kodda sınırı yazılı yeniden denemeler, aynı olayın ikinci teslimini etkisiz hale getiren idempotency anahtarları, sürekli çöken bir servise çağrıları durduran bir kesici, para harcatan işlemlerden önce bir tüketim kapısı ve aralıklı bir arıza diğerlerini gömmesin diye tekrar eşiği olan uyarılar.

Görünmeyen son kısım yayınlamadır. Platformlardan biri Docker olmadan dosya senkronizasyonu ile yayınlanır; servis edilen sayfanın az önce yayımlanan paket kimliğini tam olarak içerdiği doğrulanır — yalnızca sunucu 200 dönüyor diye değil — ve doğrulama başarısız olursa önceki sürüme otomatik dönüş yapılır. Kural, yüzeysel bir kontrolün iyi bir yayını iptal ettiği gerçek bir olaydan sonra ortaya çıktı.

Neleri kapsıyor

Çalışma, bileşenlerine ayrılmış hâliyle

Bağımsız olarak bozulabilecek yerlere göre hizmetleri ayırıyoruz

Kendi platformlarımızdan birinde 28 hizmet dizini vardır; bunların 16’sının kendi giriş noktası bulunur ve üretimde bir süreç yöneticisi altında beş süreç çalışır. Her iletişim kanalı kendi hizmetine ve kendi portuna sahiptir; tam da biri bozulduğunda diğerleri durmasın diye. Veriler, dört ayrı şema üzerinde modellenmiştir ve toplam 235 model vardır — ayrım yalnızca süreç düzeyinde değil, şema düzeyindedir.

Zincirleme senkron çağrılar değil, bir mesaj omurgası kuruyoruz

297 kuyruk, 9 değişim, 17.525 bağlantı. Kuyruk adlandırması müşteri başına ve bazı durumlarda thread başına yapılır — yani topoloji elle yazılmaz, üretilir. Ayrıca daha sonra gerçekleşmesi gereken şeyler için gecikmeli teslimat değişimi de vardır, şimdi değil. Sekiz kuyrukta ölü mektup değişimi yapılandırılmıştır ve sekizinde mesaj yaşam süresi beş dakikadır.

Sonsuza kadar değil, sınırlandırılmış yeniden denemeler

Üç farklı durum için üç farklı sınır, hepsi kodda yazılı: zamanlanmış mesajlar için, sayacı bellekte değil kalıcı tutulan beş yeniden deneme; kuyruk düzeyinde, mesaj başlığında sayılan üç yeniden deneme; ve e-posta gönderimi için, iki sağlayıcı yolunda üstel bekleme artışıyla üç yeniden deneme. Sınırı olmayan bir yeniden deneme dayanıklılık değildir, bir döngüdür.

İkinci teslimat hiçbir şeyi bozmasın diye idempotency anahtarları

Üçüncü taraflardan gelen olaylar için, olay daha önce görülmüşse başarısız olan bir insert kullanıyoruz: veritabanı benzersizlik ihlali „tekrar” olarak ele alınır, hata olarak değil; buna bir zaman tazeliği kontrolü eşlik eder. Ödemeler için anahtar kredi işlemi üzerindeki bir işarettir, yani aynı webhook’un yeniden gönderilmesi iki kez kredi oluşturmaz. Mesajlar için deduplication, kimlik üzerinden, yaşam süresi ve temizlik ile yapılır.

Kırılgan entegrasyonda bir circuit breaker

Üçüncü taraf bir sitenin oturumuna bağlı olan entegrasyonda, yalnızca dokümantasyonda bir not değil, gerçek bir circuit breaker vardır: üst üste on başarısızlıktan sonra çağrılar iki dakika durur. O olmadan, yanıt vermeyen bir site çöken bir kanalı yavaş bir platforma dönüştürür, çünkü herkes istekte bekler.

Maliyetli işlemlerden önce tüketim kapısı

Platformlardan birinde, ücretli her işlem dört aşamalı bir doğrulama işlevinden geçer; her aşamanın açıkça dönen kendi gerekçesi vardır: askıya alınmış hesap, aylık konuşma sınırına ulaşılmış olması, günlük harcama üst sınırının aşılması, yetersiz bakiye. İstisnada “izin verildi” değil, “izin verilmedi” döner — bir şey yolunda gitmediğinde gate kapanır, açılmaz. Arkada, sağlayıcıya, modele ve yönteme göre ayrılmış, olay başına maliyetli bir tüketim olayları kaydı durur.

Yeniden başlatmadan sağ kalan hız sınırı

Veritabanında tutulan kayan pencere, bir çağrı içindeki kısa yol olarak yalnızca bellekte bir haritayla birlikte. Gerekçe dosyanın tam başında yazılıdır: istekleri sunan süreçler kısa ömürlüdür, dolayısıyla bellekleri gerçeğin kaynağı olamaz. On beş işlev onu kullanır ve dış bir sağlayıcının kotası kendi, ayrı bir sınırlayıcısına sahiptir.

“Yanıt verdi” ile “çalıştı”yı ayırt eden gözlemlenebilirlik

200 yanıtı, görevin başarıyla tamamlandığı anlamına gelmez. Zamanlanmış görevleri çalıştırdığımız sarıcı, hata listeleri ya da hatalar üzerinden yanıtı özyineli olarak inceler ve “200 ile hata”yı ayrı bir sonuç olarak ele alır. Uyarıların yineleme eşiği vardır — bir sistemde on dakika, diğerinde iki saat — ve işaret, geri dönen bir arıza yeniden hemen uyarı verebilsin diye ilk başarıda silinir.

Doğrulamalı ve otomatik geri dönüşlü yayınlama

Yayınlama, yalnızca adresin 200 döndürdüğünü değil, sunulan sayfanın tam olarak az önce yayınlanan paket tanımlayıcısını içerdiğini de doğrular; aksi halde önceki sürüm geri alınır. Arka plan hizmeti için yayınlama, ağacı denetim toplamlarıyla karşılaştırır ve bir şey değişmemişse yeniden başlatmayı atlar — gereksiz bir yeniden başlatma çalışan bir döngüyü öldürür.

Nasıl görünüyor

Süreç, adım adım.

01

Kutuları değil, hata haritasını çiziyoruz

İki sistem arasındaki her bağlantı için: öteki yavaşsa, çökmüşse, iki kez yanıt verirse, yanlış yanıt verirse ne olur. Teslim ediyoruz: dört durumun her birinde beklenen davranışla birlikte bağlantı listesi ve her biri için senkron/asenkron kararı.

02

Mesaj omurgasını ve sözleşmelerini kuruyoruz

Kuyruklar, değişimler, dead letter'lar, mesaj başına yaşam süresi ve her olay türü için idempotency anahtarı. Teslim ediyoruz: topoloji, mesaj sözleşmeleri ve yeniden gönderimde belgelenmiş davranış.

03

Gate'leri ekliyoruz: hız, tüketim, anahtar

Tabanda kayan pencereyle hız sınırlama, maliyetli eylemlerden önce tüketim gate'i ve kontrolümüz dışındaki entegrasyonlarda circuit breaker. Teslim ediyoruz: ayarlanmış eşikler, açıkça dönen gerekçeler ve her reddetme aşaması için testler.

04

Yayınlamayı ve geri dönüşü kuruyoruz

Yalnızca yanıt koduna değil, içerik doğrulamalı yayınlama, yanında tutulan önceki sürüm ve otomatik geri dönüş. Teslim ediyoruz: yayınlama prosedürü, en az bir kez uygulanmış geri dönüş prosedürü ve sağlık sondaları — biri “yaşıyor” için yüzeysel, biri “gerçekten çalışıyor” için derin.

05

Dokümantasyonu kodla karşılaştırmalı doğrulanmış şekilde teslim ediyoruz

Doküman, teslimden önce ölçümle karşılaştırılır; çünkü geride kalan dokümantasyon, yokluğundan daha tehlikelidir. Teslim ediyoruz: diyagram, prosedürler ve doküman ile kodun uyumsuz bulunduğu yerlerin açık listesi, neyi düzelttiğimizle birlikte.

DocumenteConversațiiSurseSintezăAcțiuneEchipăContexteazăSurse autorizate. Acțiuni revizuite.
Sistemele nu se apelează în lanț: între ele stă o magistrală cu cozi per client, schimburi separate pe tip de trafic și scrisori moarte pentru ce nu se poate procesa. În jurul ei, porțile — limitare de rată, poartă de consum, întrerupător de circuit — și sondele de sănătate, una superficială și una adâncă.

Veriler

Neye dokunuyoruz, nerede duruyorlar ve ne kadar kalıyorlar

Veri koruma sorumlusu olan herkesin soracağı sorular — o sormadan önce burada sorulmuş hâliyle.

Verilerin platform bazında nerede durduğu
Tek bir cevap yok ve olmaması iyi: bir platform kendi sunucusunda MySQL kullanıyor, iki platform Supabase üzerinden PostgreSQL kullanıyor — biri barındırılıyor, biri altyapımız üzerine kurulmuş — bir diğeri ise içeriği hiç veritabanı olmadan, yalnızca statik dosyalar olarak tutuyor. Seçim alışkanlığa göre değil, gereksinimlere göre yapılır.
Erişim yalnızca uygulamada değil, veritabanında da uygulanır
Bizim iç sistemlerimizden birinde satır düzeyi güvenlik 43 tabloda, 90 deyim üzerinden etkin ve 179 yazılı politika var. İzlediğimiz kural şu: bir çağrı uygulamayı atlayıp doğrudan veritabanına giderse bile, yine de başkasının satırlarını görmemeli.
Tüketim kaydı
Her olay için tür, sağlayıcı, moda göre ayrı miktarlar — konuşma saniyeleri, giriş tokenleri, çıkış tokenleri, karakterler — artı altı ondalık basamakla para birimi cinsinden maliyet ve düşülen krediler. Bundan günlük harcama limiti, çağrı başı maliyet ve tüketim projeksiyonu çıkar.
Migrasyonlar dokümantasyon değil, tarihtir
Bir platformda 559 migrasyon, diğerinde 80 migrasyon. Şema versiyonlu migrasyonlarla değişir, bu yüzden veritabanının durumu yeniden oluşturulabilir ve kronolojik olarak okunabilir. Doküman ile migrasyon aynı fikirde değilse, migrasyon haklıdır.
Planlanmış görevler süreç içinde değil, veritabanında ya da cron içinde durur
Bir platformda 28 planlanmış görev veritabanının içinde çalışır; diğerinde 21 görev sistem cron’u üzerinden çalışır. Bunun nedeni dosyada yazılıdır: bir servis içindeki zamanlayıcılar her yeniden başlatmada sıfırlanır, dolayısıyla yeniden yayınlanan bir serviste üç saatte bir çalışan bir döngü hiç tetiklenmez.

Bir vaka

Her müşteri için kendiliğinden oluşturulan bir mesaj topolojisi

Durum

Birden çok müşterisi olan, her birinin kendi iletişim kanalları, kendi konuşma dizileri ve kendi bildirimleri bulunan bir platform. Naif seçenek — ortak bir kuyruk ve müşteri kimliği üzerinde bir filtre — yüksek hacimli bir müşterinin diğerlerini engellemesine yol açar, bir dizideki hata ise kuyruğu herkes için durdurur.

Ne kurduk

Topoloji müşteriye göre üretilir, elle yazılmaz: 297 kuyruktan 278'i belirli bir hesap için bildirim türündedir ve bazıları tek bir konuşma dizisi seviyesine kadar iner. Bunların üstünde, trafik türlerini ayıran dokuz exchange vardır — bildirimler, tokenler, webhooklar, kanallar — ve daha sonra olması gerekenler için gecikmeli teslimatlı bir exchange bulunur. Sekiz kuyrukta dead letter exchange, sekizinde ise mesaj başına beş dakika yaşam süresi vardır. Orkestrasyon kuralları, tüketicileri yeniden başlatmadan, bir yayın kanalı üzerinden sıcak olarak yeniden yüklenebilir.

Ne çıktı

Yüksek hacimli bir müşteri diğer müşterileri geciktirmez ve işlenemeyen bir mesaj kaybolmak ya da kuyruğu kilitlemek yerine görülebileceği ve yeniden çalıştırılabileceği bir yere gider. Omurgadaki bağlantı sayısı — 17.525 — topolojinin neden üretilmesi gerektiğini tam olarak gösterir: bunu hiç kimse elle sürdürmez.

Vakanın söylemedikleri

Bedeli operasyoneldir: bu boyutta bir omurga kendi izlemesine ve terk edilmiş kuyruklar için bir plana ihtiyaç duyar, yoksa sonsuza kadar büyür. Ve müşteri bazında üretim, bir müşterinin silinmesinin onun topolojisini de silmesi demektir — bu adım eksikse, ölü kuyruklar birikir.

Sorular

İnsanların aramadan önce bize sordukları

Bana ihtiyacım olmayan bir karmaşıklığı sattığınızı nereden biliyorum?

Çünkü sıkça verdiğimiz ilk öneri ayırmamanız yönündedir. Her yeni servis yeni bir hata modudur ve hata modları işlevlerden daha hızlı çoğalır. Kendi sistemlerimizden bir örnek: bunlardan birinin backend'i 9.516 satırlık, 99 rotalı tek bir dosyadır. Zarif değil, bunu söylüyoruz; ama tek adımda yayınlanır ve tek yerde hata ayıklanır. Ayrım, ölçülebilir bir gerekçe olduğunda yapılır — ayrı ölçeklenmesi gereken bir servis, ayrı bir ekip, ayrı bir yayın hızı.

Zincirdeki bir sistem yanıt vermezse ne olur?

Bu, harita aşamasında birlikte neye karar verdiğimize bağlıdır ve esas fikir de budur. Sistemlerimizde: mesaj kuyrukta bekler ve sınırlı sayıda yeniden denenir, ardından görülebileceği ölü mektup değişimine gider; kararsız entegrasyonda, art arda on başarısızlıktan sonra istekleri bekletmek yerine iki dakika duran bir kesici vardır; para maliyeti olan işlemler ise, istisna halinde reddeden, izin vermeyen bir kapı tarafından durdurulur.

Aynı olayın iki kez işlenmesini nasıl önlüyorsunuz?

Bir idempotency anahtarıyla, veri yarışı olan bir “bunu daha önce gördüm mü?” kontrolüyle değil. Somut olarak: olay kimliğiyle bir satır ekleriz ve veritabanı benzersizlik ihlali nedeniyle reddederse, ilgili hata kodunu arıza değil, çoğaltma sinyali olarak ele alırız. Ödemelerde işaretleme kredi işlemi üzerindedir, bu yüzden aynı webhook’un yeniden gönderilmesi iki kez kredi oluşturmaz.

Docker kullanıyor musunuz, kullanmıyor musunuz?

İkisini de kullanıyoruz ve seçimi her seferinde gerekçelendiriyoruz. Bir sistem konteyner içinde çalışır, ancak volume’ler bağlanmamıştır; bu da yayınlamanın konteynere kopyalama ve yeniden başlatma olduğu anlamına gelir — oradaki bir `docker rm` durumu kaybeder ve işletim belgesinde aynen böyle yazılıdır. Başka bir platformda hiç Docker yoktur: yayınlama dosya eşitlemedir ve yayın betikleri yönetici yetkileriyle elle kurulur ve otomatik akışa tam olarak izin verilen iki yolla açılır. Neden kodda yazılıdır: akışın üzerine yazabildiği bir betik, akışın yetki yükseltebileceği bir betiktir.

Zamanlanmış bir görevin gerçekten çalıştığını nasıl biliyorsunuz?

Kötü deneyimden. Günlük raporlarımızdan biri 7 ile 17 Ağustos arasında on bir gün boyunca ölüydü, zamanlanmış görev her gün tetiklenirken — kullanılan komut hata durumunda sessizce çıkıyor ve hiçbir şey yazmıyordu. O zamandan beri her görev, yanıtı başarısızlık listelerine göre tarayan, “200 ile başarısızlıklar”ı ayrı sonuç olarak ele alan, azami çalışma süresine sahip olan ve her çalıştırma için yapılandırılmış bir satır yazan bir sarmalayıcı içinde çalışır. Uyarıların tekrar eşiği vardır ve işaret ilk başarıda silinir.

Sağlayıcı soyutlaması ne kadar önemli?

Hızla değişen bir alandaysa çok önemlidir. Ses için, gerçek zamanlı konuşma sağlayıcısının her biri için birer tane olmak üzere üç ayrı köprüye sahibiz; bunlar aynı arayüzü uygular. Köprü, ses merkezi ile sağlayıcı arasında ses dönüşümünü özel bir port üzerinde yapar; sağlık sunucusu yalnızca yerel arayüze bağlıdır, dışa açık değildir. Sağlayıcı değişikliği bir karar olur, yeniden yazım değil.

Dokümantasyonunuz güncel mi?

Her yerde değil ve bunu nerede olmadığını söylemeyi tercih ediyoruz. Bu sayfayı hazırlarken iki sistemi ölçtük ve kendi belgemizin geride kaldığını gördük: bir dosya boyutuna ilişkin bir ifade gerçeğe göre yaklaşık %16 daha küçüktü ve bir altyapı diyagramı taşındığımız bir sunucuyu tarif ediyordu. Uyguladığımız ve projelerde istediğimiz kural şudur: belge ile ölçüm uyuşmadığında ölçüm haklıdır ve belge aynı adımda düzeltilir.

Ne yapmıyorsunuz?

Ölçüm olmadan kullanılabilirlik hedefleri vaat etmiyoruz — “%99,9” gibi bir oran, bir süre boyunca saha verisi gerektirir ve bunlar yoksa bunu söylemeyiz. Operasyonunu yapamayacağımız veya devredemeyeceğimiz sistemler tasarlamıyoruz: sonuç, ekibinizin sürdüremeyeceği bir mimariyse, o mimari yanlıştır. Ve uygunluk sertifikaları düzenlemiyoruz — kontrolleri kurabiliriz, sertifika veremeyiz.

Yukarıdaki iddialar neye dayanıyor (23 kaynak)

Bunlardan 23 tanesi depolarımızdaki kod ve dosyalardır. Adlarını ya da satırlarını yayımlamıyoruz: hepsi bir arada, tek bir sayfada, yalnızca bize ait olmayan sistemlerin nasıl kurulduğunu fazla net anlatırdı. İstek üzerine, depoda sizinle birlikte gözden geçiriyoruz — doğrulama hâlâ mümkün, sadece bir görüşme içinde yapılıyor.

Neyin daha iyi çalışmasını isterdiniz?

Bize sürecinizi anlatın. Neyin kurulmaya değer olduğuna, neyi bağlayabileceğimize ve sonucu nasıl doğrulayacağımıza birlikte karar verelim.

Hadi konuşalım