paint-brush
.NET'te Olay Akışı Uygulaması Nasıl Oluşturulurile@bbejeck
2,955 okumalar
2,955 okumalar

.NET'te Olay Akışı Uygulaması Nasıl Oluşturulur

ile Bill Bejeck14m2023/02/13
Read on Terminal Reader
Read this story w/o Javascript

Çok uzun; Okumak

Akış işleme, olayları bir uygulamanın birincil girişi veya çıkışı olarak gören bir yazılım geliştirme yaklaşımıdır. Bu blog yazısında,.NET üreticisi ve tüketici istemcileri olan Apache Kafka'yı ve Microsoft'un Görev Paralel Kitaplığını (TPL) kullanarak bir olay akışı uygulaması oluşturacağız. Kafka istemcisi ve TPL ağır işlerin çoğunu halleder; yalnızca iş mantığınıza odaklanmanız gerekir.
featured image - .NET'te Olay Akışı Uygulaması Nasıl Oluşturulur
Bill Bejeck HackerNoon profile picture
0-item


Gündelik hayatı durup düşündüğünüzde her şeyi rahatlıkla bir olay olarak görebilirsiniz. Aşağıdaki sırayı göz önünde bulundurun:


  1. Aracınızın "düşük yakıt" göstergesi yanıyor
  2. Sonuç olarak, yakıt ikmali yapmak için bir sonraki akaryakıt istasyonunda durursunuz.
  3. Arabaya benzin pompaladığınızda, indirim almak için şirketin ödül kulübüne katılmanız isteniyor
  4. İçeri girip kayıt oluyorsunuz ve bir sonraki satın alma işleminiz için kredi alıyorsunuz


Bunu daha da uzatabiliriz ama ben şunu ifade ettim: Hayat bir olaylar dizisidir. Bu gerçek göz önüne alındığında, bugün yeni bir yazılım sistemini nasıl tasarlarsınız? Farklı sonuçları toplayıp bunları rastgele aralıklarla mı işlersiniz, yoksa bunları işlemek için günün sonuna kadar mı beklersiniz? Hayır, yapmazsın; her olayla ilgili, gerçekleşir gerçekleşmez harekete geçmek istersiniz. Elbette, bireysel koşullara anında yanıt veremediğiniz durumlar olabilir… Tek seferde bir günlük işlemlerin dökümünü almayı düşünün. Ama yine de, veriyi alır almaz harekete geçersiniz, eğer isterseniz büyük bir götürü meblağlı olay.


Peki olaylarla çalışmak için bir yazılım sistemini nasıl uygularsınız? Cevap akış işlemedir.


Akış İşleme Nedir?

Olay verileriyle ilgilenmek için fiili teknoloji haline gelen akış işleme, olayları bir uygulamanın birincil girdisi veya çıktısı olarak gören bir yazılım geliştirme yaklaşımıdır. Örneğin, bilgilere göre harekete geçmeyi veya potansiyel bir sahte kredi kartı satın alımına yanıt vermeyi beklemenin hiçbir anlamı yoktur. Diğer zamanlarda bu, bir mikro hizmette gelen kayıt akışının işlenmesini içerebilir ve bunları en verimli şekilde işlemek, uygulamanız için en iyisidir.

Kullanım durumu ne olursa olsun, olay akışı yaklaşımının olayları işlemek için en iyi yaklaşım olduğunu söylemek yanlış olmaz.


Bu blog yazısında, .NET üreticisi ve tüketici istemcileri olan Apache Kafka®'yı ve Microsoft'un Görev Paralel Kitaplığı'nı (TPL) kullanarak bir olay akışı uygulaması oluşturacağız. İlk bakışta, bunların üçünü otomatik olarak birlikte çalışacak adaylar olarak göremeyebilirsiniz. Elbette Kafka ve .NET istemcileri harika bir ikili, ancak TPL resmin neresinde yer alıyor?


Çoğu zaman, üretim önemli bir gerekliliktir ve Kafka'dan tüketim ile aşağı akış işleme arasındaki empedans uyumsuzluklarından kaynaklanan darboğazları önlemek için genellikle fırsat ortaya çıktığında süreç içi paralelleştirmeyi öneriyoruz.


Sağlam ve verimli bir olay akışı uygulaması oluşturmak için üç bileşenin hepsinin birlikte nasıl çalıştığını görmek için okumaya devam edin. En iyi yanı, Kafka istemcisinin ve TPL'nin ağır işlerin çoğunu halletmesidir; yalnızca iş mantığınıza odaklanmanız gerekir.


Uygulamaya geçmeden önce her bir bileşenin kısa bir tanımını verelim.

Apaçi Kafka

Akış işleme, olay akışlarını işlemek için fiili standartsa, Apache Kafka, olay akışı uygulamaları oluşturmak için fiili standarttır. Apache Kafka, yüksek düzeyde ölçeklenebilir, esnek, hataya dayanıklı ve güvenli bir şekilde sağlanan dağıtılmış bir günlüktür. Özetle Kafka, aracıları (sunucuları) ve istemcileri kullanır. Aracılar, Kafka kümesinin veri merkezlerini veya bulut bölgelerini kapsayabilen dağıtılmış depolama katmanını oluşturur. İstemciler, bir aracı kümesinden olay verilerini okuma ve yazma olanağı sağlar. Kafka kümeleri hataya dayanıklıdır: Herhangi bir aracı başarısız olursa, diğer aracılar sürekli operasyonları sağlamak için işi üstlenir.

Birleşik .NET istemcileri

Önceki paragrafta istemcilerin Kafka broker kümesine yazdıklarını veya buradan okuduklarını belirtmiştim. Apache Kafka, Java istemcileriyle birlikte gelir, ancak bu blog yazısında uygulamanın merkezinde yer alan .NET Kafka üreticisi ve tüketicisi gibi başka istemciler de mevcuttur. .NET üreticisi ve tüketicisi, Kafka ile olay akışının gücünü .NET geliştiricisine getiriyor. .NET istemcileri hakkında daha fazla bilgi için belgelere bakın.

Görev Paralel Kitaplığı

Görev Paralel Kitaplığı ( TPL ), eşzamanlı uygulamalar yazma işini basitleştiren "System.Threading ve System.Threading.Tasks ad alanlarındaki bir dizi ortak tür ve API'dir". TPL, aşağıdaki ayrıntıları ele alarak eşzamanlılık eklemeyi daha yönetilebilir bir görev haline getirir:


1. İşin bölümlendirilmesini yönetme 2. ThreadPool'da iş parçacıklarını zamanlama 3. İptal, durum yönetimi vb. gibi alt düzey ayrıntılar.


Sonuç olarak TPL'yi kullanmak, iş mantığına odaklanmanıza izin verirken uygulamanızın işlem performansını en üst düzeye çıkarabilir. Özellikle TPL'nin Veri Akışı Kitaplığı alt kümesini kullanacaksınız.


Veri Akışı Kitaplığı, süreç içi mesaj aktarımına ve ardışık düzen görevlerine olanak tanıyan aktör tabanlı bir programlama modelidir. Dataflow bileşenleri, TPL'nin türleri ve zamanlama altyapısı üzerine kuruludur ve C# diliyle sorunsuz bir şekilde entegre olur. Kafka'dan okumak genellikle oldukça hızlıdır ancak işleme (DB çağrısı veya RPC çağrısı) genellikle bir darboğazdır. Sipariş garantilerinden ödün vermeden daha yüksek verim elde edebilecek, kullanabileceğimiz herhangi bir paralelleştirme fırsatı değerlendirmeye değer.


Bu blog yazısında, verileri kullanılabilir hale geldikçe işleyecek bir akış işleme uygulaması oluşturmak için .NET Kafka istemcileriyle birlikte bu Dataflow bileşenlerinden yararlanacağız.

Veri akışı blokları

Yapacağınız uygulamaya geçmeden önce; TPL Veri Akışı Kitaplığını neyin oluşturduğuna dair bazı arka plan bilgileri vermeliyiz. Burada ayrıntıları verilen yaklaşım, yüksek verim gerektiren CPU ve G/Ç yoğun görevleriniz olduğunda en çok uygulanabilir. TPL Veri Akışı Kitaplığı, gelen verileri veya kayıtları arabelleğe alabilen ve işleyebilen bloklardan oluşur ve bloklar üç kategoriden birine girer:


  1. Kaynak blokları – Bir veri kaynağı görevi görür ve diğer bloklar ondan okuyabilir.

  2. Hedef bloklar – Diğer bloklar tarafından yazılabilen bir veri alıcısı veya havuz.

  3. Yayıcı bloklar – Hem kaynak hem de hedef blok gibi davranın.


Farklı blokları alıp bunları doğrusal bir işlem hattı veya daha karmaşık bir işlem grafiği oluşturacak şekilde bağlarsınız. Aşağıdaki çizimleri göz önünde bulundurun:



Grafikteki her düğüm farklı bir işleme veya hesaplama görevini temsil eder.



Veri Akışı Kitaplığı, üç kategoriye ayrılan önceden tanımlanmış çeşitli blok türleri sağlar: ara belleğe alma, yürütme ve gruplama. Bu blog yazısı için geliştirilen proje için ara belleğe alma ve yürütme türlerini kullanıyoruz. BufferBlock<T>, verileri arabelleğe alan genel amaçlı bir yapıdır ve üretici/tüketici uygulamalarında kullanım için idealdir. BufferBlock, gelen verileri işlemek için ilk giren ilk çıkar kuyruğunu kullanır.


BufferBlock (ve onu genişleten sınıflar), Veri Akışı Kitaplığı'nda mesajların doğrudan yazılmasını ve okunmasını sağlayan tek blok türüdür; diğer türler bloklardan mesaj almayı veya bloklara mesaj göndermeyi bekler. Bu nedenle, kaynak bloğu oluştururken ve ISourceBlock arayüzünü uygularken ve ITargetBlock arayüzünü uygulayan lavabo bloğunu uygularken temsilci olarak BufferBlock kullandık.


Uygulamamızda kullanılan diğer Veri Akışı blok türü TransformBlock <TInput, TOutput>' dır. Veri Akışı Kitaplığı'ndaki çoğu blok türü gibi, dönüşüm bloğunun aldığı her giriş kaydı için yürüttüğü bir temsilci görevi görecek bir Func<TInput, TOutput> sağlayarak TransformBlock'un bir örneğini oluşturursunuz.


Dataflow bloklarının iki temel özelliği, ara belleğe alacağı kayıt sayısını ve paralellik düzeyini kontrol edebilmenizdir.


Maksimum arabellek kapasitesi ayarladığınızda uygulamanız, işleme hattının bir noktasında uzun süreli bir beklemeyle karşılaştığında otomatik olarak karşı basınç uygulayacaktır. Bu karşı basınç, aşırı veri birikimini önlemek için gereklidir. Daha sonra sorun azaldığında ve arabellek boyutu azaldığında verileri tekrar tüketecektir.


Bir bloğun eşzamanlılığını ayarlama yeteneği performans açısından kritik öneme sahiptir. Bir blok CPU veya I/O yoğun bir görev gerçekleştiriyorsa, verimi artırmak için işi paralelleştirmeye yönelik doğal bir eğilim vardır. Ancak eşzamanlılığın eklenmesi bir soruna (işleme sırası) neden olabilir. Bir bloğun görevine iş parçacığı eklerseniz verilerin çıktı sırasını garanti edemezsiniz. Bazı durumlarda sipariş önemli olmayabilir, ancak önemli olduğunda dikkate alınması gereken ciddi bir ödünleşim söz konusudur: eşzamanlılık ile daha yüksek verim ve sipariş çıktısının işlenmesi. Neyse ki Dataflow Kitaplığı ile bu ödünü vermek zorunda değilsiniz.


Bir bloğun paralelliğini birden fazlaya ayarladığınızda, çerçeve giriş kayıtlarının orijinal sırasını koruyacağını garanti eder (paralellikle sıranın korunmasının, varsayılan değer doğru olacak şekilde yapılandırılabilir olduğunu unutmayın). Verilerin orijinal sırası A, B, C ise çıktı sırası A, B, C olacaktır. Şüpheci misiniz? Öyle olduğunu biliyorum, bu yüzden test ettim ve reklamı yapıldığı gibi çalıştığını keşfettim. Bu testten biraz sonra bu yazıda bahsedeceğiz. Paralelliği artırmanın yalnızca durum bilgisi olmayan işlemlerle veya ilişkisel ve değişmeli durum bilgisi olan işlemlerle yapılması gerektiğini unutmayın; bu, işlemlerin sırasını veya gruplandırmasını değiştirmenin sonucu etkilemeyeceği anlamına gelir.


Bu noktada işin nereye varacağını görebilirsiniz. Mümkün olan en hızlı şekilde ele almanız gereken olayları temsil eden bir Kafka konunuz var. Yani, .NET KafkaConsumer içeren bir kaynak bloktan, iş mantığını gerçekleştirmek için işleme bloklarından ve nihai sonuçları bir Kafka konusuna geri yazmak için .NET KafkaProducer içeren bir havuz bloğundan oluşan bir akış uygulaması oluşturacaksınız. Aşağıda uygulamanın üst düzey görünümünün bir örneği verilmiştir:




Uygulama aşağıdaki yapıya sahip olacaktır:


  1. Kaynak bloğu: .NET KafkaConsumer'ı ve BufferBlock temsilcisini sarmalama
  2. Dönüşüm bloğu: Seri durumdan çıkarma
  3. Dönüşüm bloğu: Gelen JSON verilerini satın alma nesnesine eşleme
  4. Dönüşüm bloğu: CPU yoğun görev (simüle edilmiş)
  5. Dönüşüm bloğu: Serileştirme
  6. Hedef blok: .NET KafkaProducer ve BufferBlock temsilcisini sarmalama


Daha sonra, uygulamanın genel akışının bir açıklaması ve güçlü bir olay akışı uygulaması oluşturmak için Kafka ve Veri Akışı Kitaplığı'ndan yararlanmaya ilişkin bazı kritik noktalar yer almaktadır.


Bir olay akışı uygulaması

Senaryomuz şöyle: Çevrimiçi mağazanızdan satın alma işlemlerinin kayıtlarını alan bir Kafka konunuz var ve gelen veri biçimi JSON'dur. Bu satın alma olaylarını, satın alma ayrıntılarına makine öğrenimi çıkarımı uygulayarak işlemek istiyorsunuz. Ayrıca, JSON kayıtlarını Protobuf biçimine dönüştürmek istiyorsunuz; çünkü bu, şirket genelindeki veri biçimidir. Tabii ki, uygulama için verim önemlidir. ML işlemleri CPU yoğun olduğundan uygulama verimini en üst düzeye çıkarmanın bir yoluna ihtiyacınız vardır, böylece uygulamanın bu bölümünü paralelleştirmenin avantajından yararlanırsınız.


Verileri ardışık düzende tüketme

Kaynak bloktan başlayarak akış uygulamasının kritik noktalarını gezelim. Daha önce ISourceBlock arayüzünün uygulanmasından bahsetmiştim ve BufferBlock aynı zamanda ISourceBlock da uyguladığından, onu tüm arayüz yöntemlerini karşılamak için bir temsilci olarak kullanacağız. Böylece kaynak blok uygulaması bir KafkaConsumer ve BufferBlock'u saracaktır. Kaynak bloğumuzun içinde, yegane sorumluluğu tüketicinin tükettiği kayıtları ara belleğe aktarması olan ayrı bir iş parçacığımız olacak. Buradan arabellek, kayıtları işlem hattındaki bir sonraki bloğa iletecektir.


Kaydı arabelleğe iletmeden önce ConsumeRecord ( Consumer.consume çağrısı tarafından döndürülür), anahtar ve değere ek olarak uygulama için kritik olan orijinal bölümü ve uzaklığı yakalayan bir Record soyutlaması ile sarılır ve Nedenini birazdan açıklayacağım. Ayrıca tüm işlem hattının Record soyutlaması ile çalıştığını da belirtmek gerekir; bu nedenle herhangi bir dönüşüm, anahtarı, değeri ve orijinal ofset gibi diğer önemli alanları saran yeni bir Record nesnesiyle sonuçlanır ve bunları tüm işlem hattı boyunca korur.


Blokları işleme

Uygulama, işlemeyi birkaç farklı bloğa ayırır. Her blok, işleme zincirindeki bir sonraki adıma bağlanır, böylece kaynak blok, seri durumdan çıkarma işlemini gerçekleştiren ilk bloğa bağlanır. .NET KafkaConsumer kayıtların seri durumdan çıkarılmasını gerçekleştirebilirken, tüketicinin serileştirilmiş veriyi aktarmasını ve bir Transform bloğunda seri durumdan çıkarma işlemini gerçekleştirmesini sağlıyoruz. Seri durumdan çıkarma, CPU yoğun olabilir, bu nedenle bunu işlem bloğuna koymak, gerekirse işlemi paralelleştirmemize olanak tanır.


Seri durumdan çıkarmanın ardından kayıtlar, JSON yükünü Protobuf formatında bir Purchase veri modeli nesnesine dönüştüren başka bir Transform bloğuna akar. Daha ilginç kısım, veriler bir sonraki bloğa geçtiğinde ortaya çıkıyor; bu, satın alma işlemini tam olarak tamamlamak için gereken CPU yoğun bir görevi temsil ediyor. Uygulama bu bölümü simüle eder ve sağlanan işlev, bir ila üç saniye arasında rastgele bir süre ile uyku moduna geçer.


Bu simüle edilmiş işleme bloğu, Veri Akışı blok çerçevesinin gücünden yararlandığımız yerdir. Bir Dataflow bloğunun örneğini oluşturduğunuzda, karşılaştığı her kayıt için geçerli olan bir temsilci Func örneği ve bir ExecutionDataflowBlockOptions örneği sağlarsınız. Dataflow bloklarını yapılandırmaktan daha önce bahsetmiştim ancak bunları burada tekrar hızlı bir şekilde inceleyeceğiz. ExecutionDataflowBlockOptions iki temel özelliği içerir: söz konusu blok için maksimum arabellek boyutu ve maksimum paralelleştirme derecesi.


İşlem hattındaki tüm bloklar için arabellek boyutu yapılandırmasını 10.000 kayıt olarak ayarlarken, yoğun CPU simülasyonumuz dışında varsayılan paralelleştirme düzeyi olan 1'e sadık kalıyoruz, burada bunu 4'e ayarlıyoruz. Varsayılan Veri Akışı arabellek boyutunun şu şekilde olduğunu unutmayın: sınırsız. Performans sonuçlarını bir sonraki bölümde tartışacağız ancak şimdilik uygulamaya genel bakışı tamamlayacağız.


Yoğun işlem bloğu, havuz bloğunu besleyen ve daha sonra bir .NET KafkaProducer'ı saran ve nihai sonuçları bir Kafka konusuna üreten bir serileştirme dönüşüm bloğuna iletir. Havuz bloğu aynı zamanda bir temsilci BufferBlock ve üretim için ayrı bir iş parçacığını kullanır. İş parçacığı arabellekten bir sonraki kullanılabilir kaydı alır. Daha sonra, DeliveryReport saran bir Action temsilcisinden geçen KafkaProducer.Produce yöntemini çağırır; üretici G/Ç iş parçacığı, üretim isteği tamamlandığında Action temsilcisini yürütür.


Bu, uygulamanın üst düzey izlenecek yolunu tamamlar. Şimdi, kurulumumuzun çok önemli bir bölümünü (taahhüt ofsetlerinin nasıl ele alınacağını) tartışalım; bu, tüketiciden gelen kayıtları ardı ardına sıraladığımız göz önüne alındığında hayati öneme sahiptir.


Ofsetlerin işlenmesi

Verileri Kafka ile işlerken, uygulamanızın belirli bir noktaya kadar başarıyla işlediği kayıtların uzaklıklarını (bir uzaklık, Kafka konusundaki bir kaydın mantıksal konumudur) periyodik olarak işlersiniz. Peki neden ofsetler yapılıyor? Bu cevaplaması kolay bir soru: Tüketiciniz kontrollü bir şekilde veya hata nedeniyle kapandığında, bilinen son taahhüt edilen ofsetten işleme devam edecektir. Denkleştirmelerin periyodik olarak gerçekleştirilmesiyle, tüketiciniz kayıtları yeniden işlemez veya uygulamanız birkaç kaydı işledikten sonra ancak işleme koymadan önce kapanırsa en azından minimum bir miktarı yeniden işlemez. Bu yaklaşım, en az bir kez işleme olarak bilinir; bu, kayıtların en az bir kez işlenmesini garanti eder ve hata durumunda belki bazıları yeniden işlenebilir, ancak alternatif veri kaybı riskini göze almak olduğunda bu harika bir seçenektir. Kafka aynı zamanda tam olarak bir kez işleme garantisi de sağlar ve bu blog yazısında işlemlere girmesek de, Kafka'daki işlemler hakkında daha fazla bilgiyi şurada bulabilirsiniz: bu blog yazısı .


Ofsetleri işlemenin birkaç farklı yolu olsa da en basit ve en temel olanı otomatik taahhüt yaklaşımıdır. Tüketici kayıtları okur ve uygulama bunları işler. Yapılandırılabilir bir süre geçtikten sonra (kayıt zaman damgalarına göre), tüketici halihazırda tüketilen kayıtların mahsuplarını gerçekleştirecektir. Genellikle otomatik taahhüt makul bir yaklaşımdır; Tipik bir tüketme-işleme döngüsünde, daha önce tüketilen tüm kayıtları başarıyla işleyene kadar tüketiciye geri dönmezsiniz. Beklenmeyen bir hata veya kapanma meydana gelmiş olsaydı, kod hiçbir zaman tüketiciye geri dönmez, dolayısıyla herhangi bir taahhüt gerçekleşmez. Ancak buradaki uygulamamızda ardışık düzen uyguluyoruz; tüketilen kayıtları alıp arabelleğe alıyoruz ve daha fazlasını tüketmek için geri dönüyoruz; başarılı bir işleme için beklemeye gerek yok.


Ardışık düzen yaklaşımıyla en az bir kez işlemeyi nasıl garanti ederiz? Tek bir parametreyi (bir TopicPartitionOffset ) görevlendiren ve onu (diğer ofsetlerle birlikte) bir sonraki işleme için saklayan IConsumer.StoreOffset yöntemini kullanacağız . Dengeleme yönetimine yönelik bu yaklaşımın, otomatik tamamlamanın Java API ile çalışma şekliyle çeliştiğini unutmayın.


Dolayısıyla taahhüt prosedürü şu şekilde çalışır: lavabo bloğu Kafka'ya üretmek için bir kayıt aldığında, bunu Eylem temsilcisine de sağlar. Üretici geri aramayı yürüttüğünde, orijinal uzaklığı tüketiciye (kaynak bloktaki aynı örnek) iletir ve tüketici StoreOffset yöntemini kullanır. Tüketici için hâlâ otomatik taahhüt etkinleştirildi, ancak tüketicinin bu noktaya kadar tükettiği en son denkleştirmeleri körü körüne işlemesini sağlamak yerine, taahhüt edilecek denkleştirmeleri sağlıyorsunuz.



Ofsetlerin işlenmesi


Dolayısıyla, uygulama ardışık düzen kullansa da, yalnızca aracıdan bir onay aldıktan sonra taahhütte bulunur; bu, aracının ve minimum kopya aracı kümesinin kaydı depoladığı anlamına gelir. Bu şekilde çalışmak, bloklar işlerini yaparken tüketici sürekli olarak boru hattını alıp besleyebildiğinden uygulamanın daha hızlı ilerlemesine olanak tanır. Bu yaklaşım mümkündür çünkü .NET tüketici istemcisi iş parçacığı açısından güvenlidir (bazı yöntemler bu şekilde değildir ve bu şekilde belgelenmiştir), böylece tek tüketicimizin hem kaynak hem de havuz blok iş parçacıklarında güvenli bir şekilde çalışmasını sağlayabiliriz.


Üretim aşamasında herhangi bir hata durumunda, uygulama hatayı günlüğe kaydeder ve kaydı tekrar iç içe geçmiş BufferBlock koyar, böylece üretici kaydı aracıya göndermeyi yeniden deneyecektir. Ancak bu yeniden deneme mantığı körü körüne yapılır ve pratikte muhtemelen daha sağlam bir çözüm isteyeceksiniz.

Performans etkileri

Artık uygulamanın nasıl çalıştığını anlattığımıza göre performans rakamlarına bakalım. Tüm testler yerel olarak bir macOS Big Sur (11.6) dizüstü bilgisayarda gerçekleştirildi; dolayısıyla bu senaryoda kat edeceğiniz mesafe farklılık gösterebilir. Performans testi kurulumu basittir:


  1. JSON formatında bir Kafka konusuna 1 milyon kayıt oluşturun. Bu adım önceden yapıldı ve test ölçümlerine dahil edilmedi.

  2. Kafka Veri Akışı özellikli uygulamayı başlatın ve tüm bloklar arasında paralelleştirmeyi 1 (varsayılan) olarak ayarlayın

  3. Uygulama, 1 milyon kaydı başarıyla işleyene kadar çalışır, ardından kapanır

  4. Tüm kayıtların işlenmesi için geçen süreyi kaydedin


İkinci turdaki tek fark, simüle edilmiş CPU yoğun blok için MaxDegreeOfParallelism'in dörde ayarlanmasıydı.

Sonuçlar burada:


Kayıt Sayısı

Eşzamanlılık Faktörü

Süre (dakika)

1 milyon

1

38

1 milyon

4

9


Böylece, yalnızca bir yapılandırma ayarlayarak, etkinlik sırasını korurken verimi önemli ölçüde artırdık. Böylece maksimum paralellik derecesini dörde etkinleştirerek, dörtten daha büyük bir faktörle beklenen hızlanmayı elde ederiz. Ancak bu performans iyileştirmesinin kritik kısmı, herhangi bir eşzamanlı kod yazmamış olmanızdır ve bunu doğru şekilde yapmak zor olacaktır.


Blog gönderisinin başlarında, Veri akışı bloklarıyla eşzamanlılığın olay sırasını koruduğunu doğrulamak için yapılan bir testten bahsetmiştim, o yüzden şimdi bunun hakkında konuşalım. Deneme aşağıdaki adımları içeriyordu:


  1. Kafka konusuna 1M tam sayı (0-999,999) üretin

  2. Tamsayı türleriyle çalışacak şekilde referans uygulamasını değiştirin

  3. Uygulamayı simüle edilmiş uzak işlem bloğu için bir eşzamanlılık düzeyiyle çalıştırın; bir Kafka konusu oluşturun

  4. Uygulamayı dört eşzamanlılık düzeyiyle yeniden çalıştırın ve sayıları başka bir Kafka konusuna üretin

  5. Her iki sonuç konusundaki tamsayıları tüketmek ve bunları bellekteki bir dizide depolamak için bir program çalıştırın

  6. Her iki diziyi karşılaştırın ve aynı sırada olduklarını doğrulayın


Bu testin sonucu, her iki dizinin de 0'dan 999.999'a kadar olan tam sayıları içermesiydi; bu, birden fazla paralellik düzeyine sahip bir Veri Akışı bloğunun kullanılmasının, gelen verilerin işlenme sırasını koruduğunu kanıtladı. Dataflow paralelliği hakkında daha ayrıntılı bilgiyi belgelerde bulabilirsiniz.

Özet

Bu yazıda , sağlam, yüksek verimli bir olay akışı uygulaması oluşturmak için .NET Kafka istemcilerinin ve Görev Paralel Kitaplığının nasıl kullanılacağını anlattık. Kafka, yüksek performanslı olay akışı sağlar ve Görev Paralel Kitaplığı, tüm ayrıntıları işlemek için arabelleğe alma özelliğiyle eşzamanlı uygulamalar oluşturmak için yapı taşlarını sağlayarak geliştiricilerin iş mantığına odaklanmasına olanak tanır. Uygulamanın senaryosu biraz yapmacık olsa da, umarım iki teknolojiyi birleştirmenin faydasını görebilirsiniz. Bir şans ver- işte GitHub deposu .



Burada da yayınlandı.