Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Bu yazımda üzerinde çalıştığım projenin büyük bir monolitten bir mikro hizmet kümesine nasıl dönüştüğünden bahsedeceğim.

Projenin tarihi çok uzun zaman önce, 2000 yılının başında başladı. İlk sürümler Visual Basic 6'da yazılmıştı. Zamanla, IDE'den bu yana bu dilde geliştirmeyi desteklemenin gelecekte zor olacağı açıkça ortaya çıktı. ve dilin kendisi yeterince gelişmemiştir. 2000'li yılların sonunda daha umut verici olan C#'a geçilmesine karar verildi. Yeni sürüm eskisinin revizyonuna paralel olarak yazıldı, giderek daha fazla .NET'te kod yazıldı. C#'ta arka uç başlangıçta bir hizmet mimarisine odaklanmıştı ancak geliştirme sırasında mantık içeren ortak kitaplıklar kullanıldı ve hizmetler tek bir süreçte başlatıldı. Sonuç, "hizmet monoliti" adını verdiğimiz bir uygulamaydı.

Bu kombinasyonun birkaç avantajından biri, hizmetlerin harici bir API aracılığıyla birbirlerini arayabilmesiydi. Daha doğru bir hizmete ve gelecekte mikro hizmet mimarisine geçişin net önkoşulları vardı.

Ayrıştırma çalışmalarımıza 2015 yılı civarında başladık. Henüz ideal bir duruma ulaşmadık - büyük bir projenin hala monolit olarak adlandırılamayan kısımları var, ancak bunlar da mikro hizmetlere benzemiyor. Ancak yine de ilerleme önemlidir.
Makalede bundan bahsedeceğim.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Içerik

Mevcut çözümün mimarisi ve sorunları


Başlangıçta mimari şuna benziyordu: Kullanıcı arayüzü ayrı bir uygulamadır, monolitik kısım Visual Basic 6'da yazılmıştır, .NET uygulaması oldukça büyük bir veritabanıyla çalışan bir dizi ilgili hizmettir.

Önceki çözümün dezavantajları

Tek başarısızlık noktası
Tek bir hata noktamız vardı: .NET uygulaması tek bir işlemde çalışıyordu. Herhangi bir modül arızalanırsa uygulamanın tamamı başarısız oldu ve yeniden başlatılması gerekiyordu. Farklı kullanıcılar için çok sayıda işlemi otomatikleştirdiğimiz için, birindeki arıza nedeniyle herkes bir süre çalışamadı. Bir yazılım hatası durumunda yedekleme bile işe yaramadı.

İyileştirme sırası
Bu dezavantaj oldukça organizasyoneldir. Uygulamamızın çok sayıda müşterisi var ve hepsi uygulamayı en kısa sürede geliştirmek istiyor. Daha önce bunu paralel olarak yapmak imkansızdı ve tüm müşteriler sıraya giriyordu. Bu süreç işletmeler açısından olumsuzdu çünkü görevlerinin değerli olduğunu kanıtlamak zorundaydılar. Geliştirme ekibi de bu kuyruğu organize etmek için zaman harcadı. Bu çok fazla zaman ve çaba gerektirdi ve sonuçta ürün istedikleri kadar hızlı değişemedi.

Kaynakların optimal olmayan kullanımı
Hizmetleri tek bir işlemde barındırırken, yapılandırmayı her zaman sunucudan sunucuya tamamen kopyaladık. Kaynakları israf etmemek ve dağıtım planımız üzerinde daha esnek kontrol elde etmek için en ağır yüklü hizmetleri ayrı ayrı yerleştirmek istedik.

Modern teknolojileri uygulamak zor
Tüm geliştiricilerin aşina olduğu bir sorun: Projeye modern teknolojileri dahil etme arzusu var, ancak fırsat yok. Büyük bir monolitik çözümle, mevcut kütüphanenin herhangi bir güncellemesi, yenisine geçişten bahsetmeye bile gerek yok, oldukça önemsiz bir göreve dönüşüyor. Bunun boşa harcanan sinirlerden daha fazla ikramiye getireceğini ekip liderine kanıtlamak uzun zaman alır.

Değişiklikleri yayınlamada zorluk
Bu en ciddi sorundu; iki ayda bir yayın yapıyorduk.
Geliştiricilerin testlerine ve çabalarına rağmen her sürüm banka için gerçek bir felakete dönüştü. İşletme, hafta başında bazı işlevlerinin çalışmayacağını anladı. Ve geliştiriciler, kendilerini bir hafta boyunca ciddi olayların beklediğini anladılar.
Herkesin durumu değiştirme arzusu vardı.

Mikro hizmetlerden beklentiler


Hazır olduğunda bileşenlerin verilmesi. Çözümün ayrıştırılması ve farklı süreçlerin ayrılmasıyla bileşenlerin hazır olduğunda teslim edilmesi.

Küçük ürün ekipleri. Bu önemli çünkü eski monolit üzerinde çalışan büyük bir ekibi yönetmek zordu. Böyle bir ekip katı bir sürece göre çalışmaya zorlandı ancak daha fazla yaratıcılık ve bağımsızlık istiyorlardı. Bunu ancak küçük takımlar karşılayabilirdi.

Hizmetlerin ayrı süreçlerde izolasyonu. İdeal olarak, onu kapsayıcılarda izole etmek istedim, ancak .NET Framework'te yazılan çok sayıda hizmet yalnızca Windows'ta çalışıyor. .NET Core tabanlı hizmetler artık ortaya çıkıyor, ancak henüz bunlardan çok azı var.

Dağıtım esnekliği. Hizmetleri, kuralların gerektirdiği şekilde değil, ihtiyacımız olduğu şekilde birleştirmek istiyoruz.

Yeni teknolojilerin kullanımı. Bu her programcı için ilginçtir.

Geçiş sorunları


Elbette bir monoliti mikro hizmetlere bölmek kolay olsaydı konferanslarda bunun hakkında konuşmaya ve makaleler yazmaya gerek kalmazdı. Bu süreçte pek çok tuzak var, bizi engelleyen başlıcalarını anlatacağım.

İlk problem çoğu monolit için tipik olan şey: iş mantığının tutarlılığı. Bir monolit yazdığımızda gereksiz kod yazmamak için sınıflarımızı yeniden kullanmak istiyoruz. Ve mikro hizmetlere geçerken bu bir sorun haline gelir: tüm kod oldukça sıkı bir şekilde birbirine bağlıdır ve hizmetleri ayırmak zordur.

Çalışmanın başladığı sırada depoda 500'den fazla proje ve 700 binden fazla kod satırı vardı. Bu oldukça büyük bir karar ve ikinci problem. Basitçe alıp mikro hizmetlere bölmek mümkün değildi.

Üçüncü problem — gerekli altyapı eksikliği. Aslında kaynak kodunu sunuculara manuel olarak kopyalıyorduk.

Monolitten mikro hizmetlere nasıl geçilir


Mikro Hizmetlerin Sağlanması

İlk olarak, mikro hizmetlerin ayrılmasının yinelenen bir süreç olduğuna kendimiz karar verdik. Her zaman iş sorunlarını paralel olarak geliştirmemiz gerekiyordu. Bunu teknik olarak nasıl uygulayacağımız zaten bizim sorunumuz. Bu nedenle yinelemeli bir sürece hazırlandık. Büyük bir uygulamanız varsa ve başlangıçta yeniden yazılmaya hazır değilse, başka şekilde çalışmaz.

Mikro hizmetleri izole etmek için hangi yöntemleri kullanıyoruz?

ilk yöntem, — mevcut modülleri hizmet olarak taşıyın. Bu bakımdan şanslıydık: WCF protokolünü kullanarak çalışan kayıtlı hizmetler zaten vardı. Ayrı meclislere ayrıldılar. Her yapıya küçük bir başlatıcı ekleyerek bunları ayrı ayrı taşıdık. Uygulamayı hem hizmet hem de konsol olarak çalıştırmanıza olanak tanıyan harika Topshelf kütüphanesi kullanılarak yazılmıştır. Çözümde hiçbir ek proje gerekmediğinden bu, hata ayıklama için uygundur.

Hizmetler, ortak derlemeler kullandıklarından ve ortak bir veritabanıyla çalıştıklarından iş mantığına göre bağlandı. Saf haliyle mikro hizmetler olarak adlandırılamazlar. Ancak bu hizmetleri ayrı ayrı, farklı süreçlerde sağlayabiliriz. Tek başına bu bile birbirleri üzerindeki etkilerini azaltmayı, paralel gelişme ve tek başarısızlık noktası sorununu azaltmayı mümkün kıldı.

Ana bilgisayarla derleme, Program sınıfındaki yalnızca bir kod satırıdır. Topshelf ile çalışmayı yardımcı bir sınıfta sakladık.

namespace RBA.Services.Accounts.Host
{
   internal class Program
   {
      private static void Main(string[] args)
      {
        HostRunner<Accounts>.Run("RBA.Services.Accounts.Host");

       }
    }
}

Mikro hizmetleri tahsis etmenin ikinci yolu şudur: yeni sorunları çözmek için bunları yaratın. Aynı zamanda monolit büyümezse, bu zaten mükemmel, bu da doğru yönde ilerlediğimiz anlamına geliyor. Yeni sorunları çözmek için ayrı hizmetler oluşturmaya çalıştık. Böyle bir fırsat olsaydı, kendi veri modelini tamamen yöneten, ayrı bir veritabanı olan daha "kanonik" hizmetler oluşturduk.

Pek çok kişi gibi biz de kimlik doğrulama ve yetkilendirme hizmetleriyle başladık. Bunun için mükemmeller. Bağımsızdırlar, kural olarak ayrı bir veri modellerine sahiptirler. Monolitle kendileri etkileşime girmiyorlar, yalnızca bazı sorunları çözmek için onlara başvuruyorlar. Bu hizmetleri kullanarak yeni bir mimariye geçişe başlayabilir, altyapıdaki hataları ayıklayabilir, ağ kitaplıklarıyla ilgili bazı yaklaşımları deneyebilirsiniz vb. Kuruluşumuzda kimlik doğrulama hizmeti oluşturamayan ekibimiz bulunmamaktadır.

Mikro hizmetleri tahsis etmenin üçüncü yoluKullandığımız biraz bize özel. Bu, iş mantığının UI katmanından kaldırılmasıdır. Ana kullanıcı arayüzü uygulamamız masaüstüdür; arka uç gibi C# ile yazılmıştır. Geliştiriciler periyodik olarak hatalar yaptı ve arka uçta bulunması ve yeniden kullanılması gereken mantık bölümlerini kullanıcı arayüzüne aktardı.

UI bölümünün kodundan gerçek bir örneğe bakarsanız, bu çözümün çoğunun yalnızca UI formunu oluşturmak için değil, diğer süreçlerde de yararlı olan gerçek iş mantığını içerdiğini görebilirsiniz.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Gerçek kullanıcı arayüzü mantığı yalnızca son birkaç satırda mevcuttur. Tekrar kullanılabilecek şekilde sunucuya aktardık, böylece kullanıcı arayüzünü azaltıp doğru mimariye ulaştık.

Mikro hizmetleri izole etmenin dördüncü ve en önemli yoluMonolitin azaltılmasını mümkün kılan, mevcut hizmetlerin işleme tabi tutularak ortadan kaldırılmasıdır. Mevcut modülleri olduğu gibi çıkardığımızda sonuç her zaman geliştiricilerin hoşuna gitmiyor ve iş süreci, işlevsellik oluşturulduğundan beri güncelliğini kaybetmiş olabilir. Yeniden düzenleme ile yeni bir iş sürecini destekleyebiliriz çünkü iş gereksinimleri sürekli değişmektedir. Kaynak kodunu iyileştirebilir, bilinen kusurları ortadan kaldırabilir ve daha iyi bir veri modeli oluşturabiliriz. Pek çok fayda sağlanıyor.

Hizmetlerin işlemeden ayrılması, sınırlı bağlam kavramıyla ayrılmaz bir şekilde bağlantılıdır. Bu, Etki Alanına Dayalı Tasarımdan bir konsepttir. Tek bir dilin tüm terimlerinin benzersiz şekilde tanımlandığı alan modelinin bir bölümü anlamına gelir. Örnek olarak sigorta ve faturaların bağlamına bakalım. Monolitik bir uygulamamız var ve sigortada hesapla çalışmamız gerekiyor. Geliştiricinin başka bir derlemede mevcut bir Hesap sınıfını bulmasını, buna Sigorta sınıfından referans vermesini bekliyoruz ve çalışma kodumuz olacak. DRY prensibine uyulacak, mevcut kod kullanılarak görev daha hızlı gerçekleştirilecek.

Sonuç olarak, hesap ve sigorta bağlamlarının bağlantılı olduğu ortaya çıkıyor. Yeni gereksinimler ortaya çıktıkça, bu bağlantı geliştirmeyi engelleyecek ve zaten karmaşık olan iş mantığının karmaşıklığını artıracaktır. Bu sorunu çözmek için koddaki bağlamlar arasındaki sınırları bulmanız ve ihlallerini ortadan kaldırmanız gerekir. Örneğin sigorta kapsamında 20 haneli Merkez Bankası hesap numarası ve hesabın açıldığı tarihin yeterli olması oldukça olasıdır.

Bu sınırlı bağlamları birbirinden ayırmak ve mikro hizmetleri monolitik bir çözümden ayırma sürecini başlatmak için uygulama içinde harici API'ler oluşturmak gibi bir yaklaşım kullandık. Eğer bir modülün süreç içinde bir şekilde değiştirilmiş bir mikro hizmet haline gelmesi gerektiğini bilseydik, hemen dış çağrılar aracılığıyla başka bir sınırlı bağlama ait olan mantığa çağrılar yapardık. Örneğin, REST veya WCF aracılığıyla.

Dağıtılmış işlemler gerektiren kodlardan kaçınmayacağımıza kesin olarak karar verdik. Bizim durumumuzda bu kurala uymanın oldukça kolay olduğu ortaya çıktı. Kesin olarak dağıtılmış işlemlerin gerçekten gerekli olduğu durumlarla henüz karşılaşmadık - modüller arasındaki nihai tutarlılık oldukça yeterli.

Belirli bir örneğe bakalım. Bir orkestratör kavramına sahibiz - "uygulamanın" varlığını işleyen bir boru hattı. Sırasıyla bir müşteri, bir hesap ve bir banka kartı yaratır. Müşteri ve hesap başarıyla oluşturulduğu halde kart oluşturma işlemi başarısız olursa uygulama “başarılı” durumuna geçmez ve “kart oluşturulmadı” durumunda kalır. Gelecekte, arka plan etkinliği onu alıp bitirecek. Sistem bir süredir tutarsızlık içindeydi ama genel olarak bu durumdan memnunuz.

Verilerin bir kısmının tutarlı bir şekilde kaydedilmesinin gerekli olduğu bir durum ortaya çıkarsa, büyük olasılıkla hizmeti tek bir süreçte işlemek için birleştirmeye gideceğiz.

Bir mikro hizmetin tahsis edilmesine ilişkin bir örneğe bakalım. Nispeten güvenli bir şekilde üretime nasıl getirebilirsiniz? Bu örnekte, sistemin ayrı bir parçası var - kod bölümlerinden biri olan mikro hizmeti yapmak istediğimiz bordro hizmet modülü.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Öncelikle kodu yeniden yazarak bir microservice oluşturuyoruz. Memnun olmadığımız bazı yönleri iyileştiriyoruz. Müşteriden gelen yeni iş gereksinimlerini uyguluyoruz. UI ile backend arasındaki bağlantıya çağrı yönlendirmeyi sağlayacak API Gateway ekliyoruz.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Daha sonra, bu konfigürasyonu kullanıma sunuyoruz, ancak pilot durumda. Kullanıcılarımızın çoğu hâlâ eski iş süreçleriyle çalışıyor. Yeni kullanıcılar için monolitik uygulamanın artık bu süreci içermeyen yeni bir versiyonunu geliştiriyoruz. Temel olarak, bir monolit ile pilot olarak çalışan bir mikro hizmetin birleşimine sahibiz.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Başarılı bir pilot uygulama ile yeni konfigürasyonun gerçekten uygulanabilir olduğunu anlarız, eski monoliti denklemden çıkarabilir ve eski çözümün yerine yeni konfigürasyonu bırakabiliriz.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Toplamda, bir monolitin kaynak kodunu bölmek için neredeyse mevcut tüm yöntemleri kullanıyoruz. Bunların hepsi uygulamanın bazı bölümlerinin boyutunu küçültmemize ve bunları yeni kitaplıklara çevirmemize, böylece daha iyi kaynak kodu oluşturmamıza olanak tanır.

Veritabanıyla çalışma


Veritabanı, yalnızca mevcut şemayı değil aynı zamanda birikmiş geçmiş verileri de içerdiğinden kaynak kodundan daha kötü bölünebilir.

Veritabanımızın, diğerleri gibi, bir başka önemli dezavantajı daha vardı: devasa boyutu. Bu veritabanı, bir monolitin karmaşık iş mantığına ve çeşitli sınırlı bağlamlardaki tablolar arasında biriken ilişkilere göre tasarlandı.

Bizim durumumuzda, tüm sorunların (büyük veritabanı, çok sayıda bağlantı, bazen tablolar arasındaki sınırların belirsiz olması) yanı sıra, birçok büyük projede ortaya çıkan bir sorun ortaya çıktı: paylaşılan veritabanı şablonunun kullanılması. Veriler görünüm yoluyla, çoğaltma yoluyla tablolardan alındı ​​ve bu çoğaltmanın gerekli olduğu diğer sistemlere gönderildi. Sonuç olarak tabloları aktif olarak kullanıldığı için ayrı bir şemaya taşıyamadık.

Koddaki sınırlı bağlamlara yapılan aynı bölünme, ayırmada bize yardımcı olur. Genellikle bize verileri veritabanı düzeyinde nasıl parçaladığımız konusunda oldukça iyi bir fikir verir. Hangi tabloların sınırlı bir bağlama, hangisinin diğerine ait olduğunu anlarız.

Veritabanı bölümlemesinde iki genel yöntem kullandık: mevcut tabloların bölümlenmesi ve işlemle bölümlenmesi.

Veri yapısının iyi olması, iş gereksinimlerini karşılaması ve herkesin bundan memnun olması durumunda mevcut tabloları bölmek iyi bir yöntemdir. Bu durumda mevcut tabloları ayrı bir şemaya ayırabiliriz.

İş modelinin çok değiştiği, tabloların artık bizi hiç tatmin etmediği bir dönemde işlemeli bir departmana ihtiyaç duyuluyor.

Mevcut tabloların bölünmesi. Neyi ayıracağımızı belirlememiz gerekiyor. Bu bilgi olmadan hiçbir şey işe yaramaz ve burada koddaki sınırlı bağlamların ayrılması bize yardımcı olacaktır. Kural olarak, kaynak kodundaki bağlamların sınırlarını anlayabilirseniz, bölüm listesine hangi tabloların dahil edilmesi gerektiği netleşir.

İki monolit modülün tek bir veritabanıyla etkileşime girdiği bir çözümümüz olduğunu düşünelim. Ayrılmış tabloların bulunduğu bölümle yalnızca bir modülün etkileşime girdiğinden, diğer modülün API aracılığıyla etkileşime girdiğinden emin olmamız gerekiyor. Başlangıç ​​​​olarak, yalnızca kaydın API aracılığıyla yapılması yeterlidir. Bu, mikro hizmetlerin bağımsızlığından bahsetmemiz için gerekli bir koşuldur. Büyük bir sorun olmadığı sürece okuma bağlantıları kalabilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Bir sonraki adım, kodun ayrı tablolarla, işlenerek veya işlenmeden çalışan bölümünü ayrı bir mikro hizmete ayırıp ayrı bir işlemde, bir konteynerde çalıştırabilmemizdir. Bu, monolit veritabanına ve onunla doğrudan ilgili olmayan tablolara bağlantısı olan ayrı bir hizmet olacaktır. Monolit, okuma için hala çıkarılabilir parçayla etkileşim halindedir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Daha sonra bu bağlantıyı kaldıracağız, yani monolitik bir uygulamadan gelen verileri ayrılmış tablolardan okumak da API'ye aktarılacaktır.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Daha sonra genel veritabanından yalnızca yeni mikro hizmetin çalıştığı tabloları seçeceğiz. Tabloları ayrı bir şemaya, hatta ayrı bir fiziksel veritabanına taşıyabiliriz. Mikro hizmet ile monolit veritabanı arasında hala bir okuma bağlantısı var ancak endişelenecek bir şey yok, bu konfigürasyonda oldukça uzun süre yaşayabilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Son adım, tüm bağlantıları tamamen kaldırmaktır. Bu durumda ana veritabanından veri taşımamız gerekebilir. Bazen harici sistemlerden kopyalanan bazı verileri veya dizinleri çeşitli veritabanlarında yeniden kullanmak isteriz. Bu bize periyodik olarak olur.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

İşleme departmanı. Bu yöntem ilkine çok benzer, yalnızca ters sıradadır. Hemen yeni bir veritabanı ve bir API aracılığıyla monolitle etkileşime giren yeni bir mikro hizmet tahsis ediyoruz. Ancak aynı zamanda gelecekte silmek istediğimiz bir dizi veritabanı tablosu da var. Artık ihtiyacımız kalmadı, yeni modelde değiştirdik.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Bu planın işe yaraması için muhtemelen bir geçiş dönemine ihtiyacımız olacak.

O halde iki olası yaklaşım vardır.

Ilk: Yeni ve eski veritabanlarındaki tüm verileri kopyalıyoruz. Bu durumda veri fazlalığımız olur ve senkronizasyon sorunları ortaya çıkabilir. Ama iki farklı müşteriyi alabiliriz. Biri yeni versiyonla, diğeri eski versiyonla çalışacak.

Ikinci: Verileri bazı iş kriterlerine göre bölüyoruz. Örneğin sistemde eski veritabanında saklanan 5 ürünümüz vardı. Altıncısını yeni bir veritabanındaki yeni iş görevinin içine yerleştiriyoruz. Ancak bu verileri senkronize edecek ve müşteriye nereden ve ne alacağını gösterecek bir API Ağ Geçidine ihtiyacımız olacak.

Her iki yaklaşım da işe yarar, duruma göre seçim yapın.

Her şeyin çalıştığından emin olduktan sonra monolitin eski veritabanı yapılarıyla çalışan kısmı devre dışı bırakılabilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Son adım eski veri yapılarını kaldırmaktır.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Özetlemek gerekirse veritabanıyla ilgili sorunlarımız olduğunu söyleyebiliriz: Kaynak koda göre onunla çalışmak daha zor, paylaşmak daha zor ama yapılabilir ve yapılmalıdır. Bunu oldukça güvenli bir şekilde yapmamızı sağlayan bazı yollar bulduk, ancak yine de verilerde hata yapmak kaynak kodunda hata yapmaktan daha kolaydır.

Kaynak koduyla çalışma


Monolitik projeyi analiz etmeye başladığımızda kaynak kodu diyagramı böyle görünüyordu.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Kabaca üç katmana ayrılabilir. Bu, başlatılan modüller, eklentiler, hizmetler ve bireysel etkinliklerden oluşan bir katmandır. Aslında bunlar yekpare bir çözümün giriş noktalarıydı. Hepsi ortak bir katmanla sıkıca kapatılmıştı. Hizmetlerin paylaştığı bir iş mantığı ve çok sayıda bağlantı vardı. Her hizmet ve eklenti, boyutlarına ve geliştiricilerin vicdanına bağlı olarak 10 veya daha fazla ortak derleme kullandı.

Ayrı olarak kullanılabilecek altyapı kütüphanelerine sahip olduğumuz için şanslıydık.

Bazen bazı ortak nesnelerin aslında bu katmana ait olmadığı, ancak altyapı kütüphaneleri olduğu bir durum ortaya çıktı. Bu, yeniden adlandırmayla çözüldü.

En büyük endişe sınırlı bağlamlardı. 3-4 bağlamın bir Ortak montajda karıştırıldığı ve birbirini aynı iş fonksiyonları içerisinde kullandığı görüldü. Bunun nereye ve hangi sınırlara göre bölünebileceğini ve bu bölümün kaynak kod derlemeleriyle eşleştirilmesiyle bundan sonra ne yapılacağını anlamak gerekiyordu.

Kod bölme işlemi için çeşitli kurallar formüle ettik.

Birinci: Artık hizmetler, etkinlikler ve eklentiler arasında iş mantığını paylaşmak istemiyorduk. Mikro hizmetler içerisinde iş mantığını bağımsız hale getirmek istedik. Mikro hizmetler ise ideal olarak tamamen bağımsız olarak var olan hizmetler olarak düşünülür. Bu yaklaşımın biraz israf olduğuna ve bunu başarmanın zor olduğuna inanıyorum çünkü örneğin C#'taki hizmetler her durumda standart bir kitaplıkla birbirine bağlanacaktır. Sistemimiz C# ile yazılmıştır, diğer teknolojileri henüz kullanmadık. Bu nedenle ortak teknik montajları kullanmaya gücümüzün yettiğine karar verdik. Önemli olan, iş mantığının herhangi bir parçasını içermemeleridir. Kullandığınız ORM üzerinde kullanışlı bir paketleyiciniz varsa, bunu hizmetten hizmete kopyalamak çok pahalıdır.

Ekibimiz alan odaklı tasarımın hayranı olduğundan, soğan mimarisi bizim için çok uygundu. Hizmetlerimizin temeli veri erişim katmanı değil, sadece iş mantığını içeren ve altyapı ile hiçbir bağlantısı olmayan, domain mantığına sahip bir derlemedir. Aynı zamanda çerçevelerle ilgili sorunları çözmek için etki alanı derlemesini bağımsız olarak değiştirebiliriz.

Bu aşamada ilk ciddi sorunumuzla karşılaştık. Hizmetin tek bir etki alanı derlemesine başvurması gerekiyordu, mantığı bağımsız hale getirmek istedik ve DRY ilkesi burada bizi büyük ölçüde engelledi. Geliştiriciler, kopyaları önlemek için komşu derlemelerdeki sınıfları yeniden kullanmak istediler ve sonuç olarak alanlar yeniden birbirine bağlanmaya başladı. Sonuçları analiz ettik ve sorunun kaynak kodu depolama cihazı alanında da olabileceğine karar verdik. Tüm kaynak kodunu içeren geniş bir havuzumuz vardı. Tüm projeye yönelik çözümün yerel bir makinede birleştirilmesi çok zordu. Bu nedenle, projenin bölümleri için ayrı küçük çözümler oluşturuldu ve hiç kimse bunlara bazı ortak veya etki alanı derlemesi eklemeyi ve yeniden kullanmayı yasaklamadı. Bunu yapmamıza izin vermeyen tek araç kod incelemesiydi. Ama bazen de başarısız oldu.

Daha sonra ayrı depolara sahip bir modele geçmeye başladık. İş mantığı artık hizmetten hizmete akmıyor; alanlar gerçekten bağımsız hale geldi. Sınırlı bağlamlar daha net bir şekilde desteklenir. Altyapı kütüphanelerini nasıl yeniden kullanırız? Bunları ayrı bir depoya ayırdık, ardından Artifactory'ye koyduğumuz Nuget paketlerine koyduk. Herhangi bir değişiklikle birlikte montaj ve yayın otomatik olarak gerçekleşir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Hizmetlerimiz, dış altyapı paketleriyle aynı şekilde iç altyapı paketlerine referans vermeye başladı. Nuget'ten harici kütüphaneler indiriyoruz. Bu paketleri yerleştirdiğimiz Artifactory ile çalışmak için iki paket yöneticisi kullandık. Küçük depolarda da Nuget'i kullandık. Çoklu hizmetin olduğu depolarda modüller arasında daha fazla sürüm tutarlılığı sağlayan Paket'i kullandık.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Böylece kaynak kodu üzerinde çalışarak, mimariyi biraz değiştirerek ve depoları ayırarak hizmetlerimizi daha bağımsız hale getiriyoruz.

Altyapı sorunları


Mikro hizmetlere geçmenin olumsuzluklarının çoğu altyapıyla ilgilidir. Otomatik dağıtıma ihtiyacınız olacak, altyapıyı çalıştırmak için yeni kitaplıklara ihtiyacınız olacak.

Ortamlarda manuel kurulum

Başlangıçta ortamlar için çözümü manuel olarak kurduk. Bu süreci otomatikleştirmek için bir CI/CD işlem hattı oluşturduk. Sürekli dağıtım sürecini iş süreçleri açısından bizim için henüz kabul edilebilir olmadığı için seçtik. Bu nedenle, çalışmaya gönderme bir düğme kullanılarak ve test için otomatik olarak gerçekleştirilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Kaynak kodu depolamak için Atlassian, Bitbucket ve inşaat için Bamboo kullanıyoruz. Cake'de derleme komut dosyaları yazmayı seviyoruz çünkü C# ile aynı. Hazır paketler Artifactory'ye gelir ve Ansible otomatik olarak test sunucularına ulaşır ve ardından hemen test edilebilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Ayrı günlük kaydı


Bir zamanlar monolitin fikirlerinden biri ortak kayıt sağlamaktı. Ayrıca disklerdeki bireysel günlüklerle ne yapacağımızı da anlamamız gerekiyordu. Günlüklerimiz metin dosyalarına yazılır. Standart bir ELK yığını kullanmaya karar verdik. ELK'ye doğrudan sağlayıcılar aracılığıyla yazmadık, ancak metin günlüklerini değiştirip izleme kimliğini tanımlayıcı olarak bunlara yazmaya ve hizmet adını eklemeye karar verdik, böylece bu günlükler daha sonra ayrıştırılabilir.

Monolitten mikro hizmetlere geçiş: tarih ve uygulama

Filebeat'i kullanarak, günlüklerimizi sunuculardan toplama, ardından bunları dönüştürme, kullanıcı arayüzünde sorgular oluşturmak için Kibana'yı kullanma ve çağrının hizmetler arasında nasıl gittiğini görme fırsatını yakalıyoruz. Trace ID bu konuda çok yardımcı oluyor.

İlgili hizmetleri test etme ve hata ayıklama


Başlangıçta, geliştirilmekte olan hizmetlerde nasıl hata ayıklanacağını tam olarak anlamadık. Monolitte her şey basitti; onu yerel bir makinede çalıştırdık. İlk başta aynısını mikro hizmetlerle yapmaya çalıştılar, ancak bazen bir mikro hizmeti tam olarak başlatmak için birkaç mikro hizmeti başlatmanız gerekir ve bu sakıncalıdır. Yerel makinede yalnızca hata ayıklamak istediğimiz hizmet veya hizmetleri bıraktığımız bir modele geçmemiz gerektiğini fark ettik. Geri kalan hizmetler prod ile konfigürasyonla eşleşen sunuculardan kullanılır. Hata ayıklamanın ardından, test sırasında her görev için yalnızca değiştirilen hizmetler test sunucusuna gönderilir. Böylece çözümün gelecekte üretimde görüneceği formda test edilmesi sağlanır.

Hizmetlerin yalnızca üretim sürümlerini çalıştıran sunucular vardır. Bu sunuculara, herhangi bir olay durumunda, dağıtımdan önce teslimatı kontrol etmek ve şirket içi eğitim için ihtiyaç duyulur.

Popüler Specflow kütüphanesini kullanarak otomatik bir test süreci ekledik. Testler, Ansible'dan dağıtımdan hemen sonra NUnit kullanılarak otomatik olarak çalıştırılır. Görev kapsamı tamamen otomatikse manuel teste gerek yoktur. Her ne kadar bazen ek manuel testler hala gerekli olsa da. Belirli bir sorun için hangi testlerin çalıştırılacağını belirlemek için Jira'da etiketleri kullanırız.

Ek olarak yük testine olan ihtiyaç da arttı; önceden yalnızca nadir durumlarda yapılıyordu. Testleri yürütmek için JMeter'ı, bunları depolamak için InfluxDB'yi ve süreç grafikleri oluşturmak için Grafana'yı kullanıyoruz.

Neyi başardık?


Öncelikle “serbest bırakma” kavramından kurtulduk. Bu devasa ürünün bir üretim ortamında konuşlandırıldığı ve iş süreçlerini geçici olarak kesintiye uğratan iki aylık devasa sürümler artık geride kaldı. Artık hizmetleri ortalama 1,5 günde bir dağıtıyoruz ve onaylandıktan sonra faaliyete geçtikleri için bunları gruplandırıyoruz.

Sistemimizde ölümcül arızalar bulunmamaktadır. Bir mikro hizmeti hatayla birlikte yayınlarsak, onunla ilişkili işlevler bozulacak ve diğer tüm işlevler etkilenmeyecektir. Bu, kullanıcı deneyimini büyük ölçüde geliştirir.

Dağıtım modelini kontrol edebiliriz. Gerekirse hizmet gruplarını çözümün geri kalanından ayrı olarak seçebilirsiniz.

Ayrıca büyük bir iyileştirme kuyruğu ile sorunu önemli ölçüde azalttık. Artık bazı hizmetlerle bağımsız olarak çalışan ayrı ürün ekiplerimiz var. Scrum süreci zaten buraya çok uygun. Belirli bir ekibin kendisine görevler atayan ayrı bir Ürün Sahibi olabilir.

Özet

  • Mikro hizmetler karmaşık sistemleri ayrıştırmak için çok uygundur. Bu süreçte sistemimizde ne olduğunu, hangi bağlamların sınırlı olduğunu, sınırlarının nerede olduğunu anlamaya başlarız. Bu, iyileştirmeleri modüller arasında doğru şekilde dağıtmanıza ve kod karışıklığını önlemenize olanak tanır.
  • Mikro hizmetler organizasyonel faydalar sağlar. Genellikle yalnızca mimari olarak bahsedilir, ancak herhangi bir mimariye tek başına değil, iş ihtiyaçlarını çözmek için ihtiyaç vardır. Dolayısıyla Scrum'ın artık çok popüler olduğu göz önüne alındığında, mikro hizmetlerin küçük ekiplerdeki sorunların çözümü için çok uygun olduğunu söyleyebiliriz.
  • Ayırma yinelenen bir süreçtir. Bir uygulamayı alıp basitçe mikro hizmetlere bölemezsiniz. Ortaya çıkan ürünün işlevsel olması muhtemel değildir. Mikro hizmetleri tahsis ederken mevcut mirası yeniden yazmak, yani onu sevdiğimiz ve işlevsellik ve hız açısından iş ihtiyaçlarını daha iyi karşılayan koda dönüştürmek faydalıdır.

    Küçük bir uyarı: Mikro hizmetlere geçmenin maliyetleri oldukça önemlidir. Altyapı sorununu tek başına çözmek uzun zaman aldı. Dolayısıyla, belirli ölçeklendirme gerektirmeyen küçük bir uygulamanız varsa ve ekibinizin dikkatini ve zamanını kazanmak için yarışan çok sayıda müşteriniz yoksa, mikro hizmetler bugün ihtiyacınız olan şey olmayabilir. Oldukça pahalı. Sürece mikro hizmetlerle başlarsanız, maliyetler başlangıçta aynı projeye bir monolitin geliştirilmesiyle başlamanızdan daha yüksek olacaktır.

    PS Daha duygusal bir hikaye (ve sanki kişisel olarak sizin içinmiş gibi) - göre bağlantı.
    Raporun tam versiyonunu burada bulabilirsiniz.

Kaynak: habr.com

Yorum ekle