Strangler Fig ve API Sürümleme — Monoliti Kimseyi Kırmadan Taşımak
Önce şunu oku: Servis Sınırları — Bounded Context ve Database-per-Service , API Gateway ve Service Discovery — İstek Doğru Kapıyı Nasıl Bulur
30 saniyede özet
Monoliti bir gecede yeniden yazmak yerine önüne bir yönlendirici koyup yetenekleri tek tek taşırsın. Model değişirken eski istemciler eski şekli almaya devam eder: alan eklenir, sürüm açılır, eskisi duyurulup kapatılır.
Bir restoranın mutfağını yenilemen gerekiyor, ama restoran bir akşam bile kapanamaz. Masalar dolu, siparişler durmadan geliyor.
Adım adım oku
- Masalardan gelen her sipariş önce garsona uğrar; garson hangi tezgâha gideceğini bilir.
- Salata tezgâhı yeni mutfağa taşınır. Eski mutfak geri kalan her şeyi pişirmeye devam eder.
- Garson yalnızca salata siparişlerini yeni mutfağa götürür. Masalar bir şey fark etmez.
- Tezgâhlar birer birer taşınır; sonunda eski mutfak boşalır ve restoran hiç kapanmamıştır.
-
Bayt: Monolit çok büyüdü. Bir hafta sonu oturup her şeyi mikroservis olarak baştan yazalım mı?
-
Sen: Bir hafta sonu yeter mi ki?
-
Bayt: Yetmez! O sırada eski uygulama da değişmeye devam eder, yenisi hep arkasından koşar.
-
Bayt: Başka bir yol var: restoranı kapatmadan mutfağı tezgâh tezgâh taşımak.
Önde bir kapı, arkada adım adım taşınma
strangler figBir monoliti tek seferde yeniden yazmak yerine önüne bir yönlendirici koyup yetenekleri tek tek yeni servislere taşımak. Adını, konak ağacını yavaşça saran incir türünden alır.Sözlükte gör → deseninde monolitin önüne bir yönlendirici konur: bir facade, çoğu zaman bir API gateway. Başta bütün istekleri olduğu gibi monolite geçirir.
Sonra yetenekler tek tek yeni servislere taşınır. Facade yalnızca taşınan yeteneğin yolunu yeni adrese çevirir; istemciler hep aynı adresi çağırır. Bir adım ters giderse geri almak, o tek yolu eski yerine çevirmektir.
İlk taşınan yetenek genellikle az bağımlı ve kaybı küçük olandır, katalog gibi. Para taşıyan ödeme sona kalır.
Yeni servis eski modeli miras almaz. Arada bir anti-corruption layerYeni sistemle eski sistem arasında modelleri birbirine çeviren ince katman. Eski modelin kavramları yeni servisin içine sızmaz, eski istemciler de eski şekli görmeye devam eder.Sözlükte gör → durur ve iki modeli birbirine çevirir. Böylece monolitin eski tuhaflıkları yeni servise sızmaz.
Kafam karıştı, daha basit anlat
Önce kapıya bir garson koy. Sonra tezgâhları tek tek yeni mutfağa taşı ve garsona yalnızca o tezgâhın yeni yerini söyle. Masalar hiçbir şey fark etmez.
Strangler fig yaklaşımıyla bir monolitten servis ayırmak nasıl işler?
Strangler fig taşımasında istemcilerin önündeki facade (ya da gateway) neden bu kadar önemli?
Sözleşmeyi kırmadan değiştirmek
Taşırken modeller de değişir. Yeni sipariş servisi tutarı artık para birimiyle birlikte tutuyor.
Sipariş cevabındaki total alanı düz bir sayıydı; yeni serviste amount ve currency taşıyan bir nesne oldu. Web'i aynı gün güncelledin. Telefonlarda kalan eski mobil uygulama ne görür? Cevabı göster
Cevabı okuyamaz. Eski uygulama total’de bir sayı bekliyor, karşısına bir nesne çıkıyor. Kimse güncelleyemediği için bu kırılma, eski sürüm kullanıldıkça sürer.
Bir API’nin cevabı, onu çağıran herkesle yapılmış bir sözleşmedir. Kısa kural: eklemek güvenlidir; silmek, adını ya da şeklini değiştirmek kırar.
Eklemenin güvenli olması istemciye bağlıdır. tolerant readerCevaptan yalnızca ihtiyaç duyduğu alanları okuyan, tanımadığı alanları görmezden gelen istemci. Sunucunun alan eklemesi onu kırmaz.Sözlükte gör → yalnızca ihtiyacı olan alanları okur ve tanımadığını görmezden gelir. Tanımadığı alanda hata veren katı bir istemci, masum bir eklemede bile kırılır.
Şekli gerçekten değiştirmen gerekiyorsa yeni bir sürüm açar, eskisini bir süre yaşatırsın. İki yaygın yol var:
URI: /v2/orders | Başlık: Accept-Version: 2 | |
|---|---|---|
| Görünürlük | Log’da ve tarayıcıda hemen görünür | İsteğin başlığına bakmak gerekir |
| Önbellek | Adres farklı, kendiliğinden ayrılır | Vary başlığı gerekir |
| Başlıksız / eski istek | Eski yol eskisi gibi çalışır | Hangi sürümü alacağı senin kararın |
İkisi de işe yarar. Önemli olan, eski sürümün bir çeviri katmanıyla yaşaması ve duyurularak kapatılmasıdır: cevaba Sunset başlığı eklenir, log’dan onu kimin çağırdığı izlenir.
Spring'de sürüm· istersen atla
Spring Framework 7 (Spring Boot 4) sürümlemeyi yerleşik destekler: @GetMapping(version = "2") yazılır, sürümün nereden okunacağı configureApiVersioning ile seçilir.
Kafam karıştı, daha basit anlat
Sözleşmeye yeni bir madde eklemek kimseyi üzmez. Bir maddeyi silmek ya da değiştirmek imzalayanları zor durumda bırakır. Değiştirmen gerekiyorsa yeni bir sözleşme aç, eskisini bir süre yaşat.
Bir REST cevabında aşağıdaki değişikliklerden hangisi mevcut istemciler için genellikle geriye uyumludur?
Sipariş cevabına yalnızca yeni bir totalMoney alanı eklendi, eski total alanı yerinde duruyor. Mobil uygulama sorunsuz çalışırken bir partner entegrasyonu hata vermeye başladı. En olası sebep ne?
Satır satır: bir yolu yeni servise çevirmek
Gateway taşımanın ortasında: katalog ve sipariş taşındı, gerisi monolitte.
Taşınanı yeni servise, gerisini monolite
@BeanRouteLocator strangler(RouteLocatorBuilder builder) { return builder.routes() // Moved: catalog lives in its own service now .route("catalog", r -> r.path("/api/catalog/**") .uri("lb://catalog-service")) // Moved: orders, the old unversioned path and v2 together .route("orders", r -> r.path("/api/orders/**", "/api/v2/orders/**") .uri("lb://order-service")) // Not moved yet: everything else still reaches the monolith .route("monolith", r -> r.order(Ordered.LOWEST_PRECEDENCE) .path("/**") .uri("http://legacy-shop:8080")) .build();}Debug
gateway GET /api/catalog/42 geldi. İlk eşleşen kural bu: katalog artık kendi servisinde.
Sol/sağ ok tuşlarıyla da gezebilirsin.
Çeviri katmanı ve katı olmayan bir istemci kodda şöyle görünür:
Derinleş · Sipariş: yeni model, eski şekil, hoşgörülü okuyucu 3 dosya · ~46 satır · ilk okumada atlayabilirsin
Gateway'de strangler fig kuralları yazıyorsun: /api/catalog/** yeni servise, geri kalan her şey /** ile monolite gidiyor. /** kuralı en yüksek öncelikle değerlendirilirse ne olur?
Kendin gör
Dört yetenek, üç istemci. Her adımda kimin hâlâ çalıştığına bak.
Monoliti parça parça taşı — kim kırılıyor?
Tohum 325168Facade yönlendirme tablosu
- /catalogmonolit
- /cartmonolit
- /ordersmonolit
- /paymentmonolit
Web · her yayında güncellenir
- Katalogmonolit
- Sepetmonolit
- Siparişmonolit
- Ödememonolit
Mobil v1 · eski sürüm, güncellenemiyor
- Katalogmonolit
- Sepetmonolit
- Siparişmonolit
- Ödememonolit
Partner · bilinmeyen alanda hata verir
- Katalogmonolit
- Sepetmonolit
- Siparişmonolit
- Ödememonolit
Şu an ne oldu?
Her şey monolitte, üç istemci de mutlu
Web her yayında güncelleniyor. Mobil v1 eski bir sürüm ve güncellenemiyor. Partner bilinmeyen bir alan görünce hata veriyor.
Görevler0/3
Dört yeteneği de taşı, hiçbir istemci bir an bile kırılmasınaçık
İpucu
Önde bir facade, eski istemciye eski şekli veren bir sürüm ve tek bir yazan.
Eski mobil uygulama ayakta kalsın, katı okuyan partner kırılsınaçık
İpucu
Eski alanı silmeden yeni bir alan eklersen kim onu görmezden gelebilir?
Bir siparişin iki kopyasını birbirinden ayıraçık
İpucu
Aynı isteği iki veritabanına yazarsan ve ikincisi düşerse?
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanla oynat. Katalog ve sepet sessizce taşınıyor; sipariş modeli yerinde değişince iki eski istemci kırılıyor.
- “Yeni alan ekle” seç. Mobil v1 kurtuldu, Partner hâlâ kırık: aradaki fark okuma biçimi.
- URI ya da başlık sürümü seç. Dört adımın hiçbirinde kırmızı kutu yok.
- Önde facade olmasın. Daha ilk adımda, katalogda bile, iki istemci 404 alıyor.
- Çift yazmayı seç. Sipariş taşınınca bir istemcinin ödemesi sarıya dönüyor: iki veritabanı ayrıştı.
Simülatörde önde facade yokken katalog taşındığında Web çalışmaya devam etti ama Mobil v1 ve Partner kırıldı. Neden?
Tuzaklar
İki yere birden yazmak. Taşırken aynı siparişi hem monolite hem yeni servise yazmak cazip gelir. Bu bir dual writeAynı işlemde iki ayrı sisteme (ör. veritabanı ve mesaj kuyruğu) yazmak. Ortak bir transaction olmadığı için biri başarılı olup diğeri başarısız olabilir.Sözlükte gör →: ikisini kapsayan bir transaction yok, ikinci yazma düşerse iki farklı gerçek kalır.
Çözüm, her verinin tek bir yazanı olması. Yazma sahipliği bir anda yeni servise geçer; monolit kopyasını olaylardan (outbox) ya da veritabanı değişikliklerini okuyan CDC’den alır.
“Başlık yoksa en yeni sürüm.” Başlıksız istek en yeni sürüme düşerse, başlığı hiç bilmeyen eski istemciler her yeni sürümde kırılır. Başlıksız istek desteklenen en eski sürümü almalı.
Kendini sına
Strangler fig, monoliti tek seferde kapatıp yenisini açmaktır.
Taşıma sırasında sipariş, aynı istekte hem monolitin hem yeni sipariş servisinin veritabanına yazılıyor. Bu tasarımın asıl riski ve yaygın düzeltmesi hangisi?
Aklında kalacak üç şey
- 1 Strangler fig monoliti yetenek yetenek taşır. Önde duran facade her adımda tek bir yolu yeni servise çevirir; bir adımı geri almak o yolu eski yerine çevirmek kadar kolaydır.
- 2 Alan eklemek geriye uyumludur; silmek, adını ya da şeklini değiştirmek kırıcıdır. Eski istemcilere eski şekli bir sürüm ve bir çeviri katmanı verir.
- 3 Aynı veriyi iki yere birden yazmak, iki yazmadan biri düşünce iki farklı gerçek bırakır. Taşıma sırasında her verinin tek bir yazanı olur, öteki taraf kopyasını olaylardan alır.
4 kart sonraki derste seni bekliyor