Düzenle

Aracılığıyla paylaş


Azure Event Hubs ve Kafka'da bölümleme

Azure Blob Storage
Azure Event Hubs

Bu başvuru mimarisi, olay alma hizmetlerinin kullandığı bölümleme modeline yönelik stratejiler sağlar. Olay alma hizmetleri yüksek ölçekli olay akışı için çözümler sağladığından, olayları paralel olarak işlemeleri ve olay sırasını koruyabilmeleri gerekir. Ayrıca yükleri dengelemeleri ve ölçeklenebilirlik sunmaları gerekir. Bölümleme modelleri bu gereksinimlerin tümünü karşılar.

Mimari

Architecture diagram showing the flow of events in an ingestion pipeline. Events flow from producers to a cluster or namespace and then to consumers.

Diyagramın merkezinde Kafka Kümesi veya Olay Hub'ı Ad Alanı etiketli bir kutu bulunur. Kutunun içinde üç küçük kutu var. Her biri Konu veya Olay Hub'ı olarak etiketlenmiştir ve her biri Bölüm etiketli birden çok dikdörtgen içerir. Ana kutunun üstünde Üretici etiketli dikdörtgenler bulunur. Oklar yapımcılardan ana kutuya işaret eder. Ana kutunun altında Tüketici etiketli dikdörtgenler bulunur. Oklar ana kutudan tüketicilere işaret eder ve çeşitli uzaklık değerleriyle etiketlenir. Tüketici Grubu etiketli tek bir mavi çerçeve, tüketicilerden ikisini bir arada gruplandırarak çevreler.

Bu mimarinin bir Visio dosyasını indirin.

Veri akışı

  • Üreticiler verileri alma hizmetine veya işlem hattına yayımlar. Event Hubs işlem hatları ad alanlarını kapsar. Kafka eşdeğerleri kümelerdir.

  • İşlem hattı, gelen olayları bölümler arasında dağıtır. Her bölümde olaylar üretim düzeninde kalır. Ancak olaylar bölümler arasında sıralı olarak kalmaz. Bölüm sayısı aktarım hızını veya belirli bir süre içinde sistemden geçen veri miktarını etkileyebilir. İşlem hatları genellikle saniye başına bit (bps) ve bazen de saniye başına veri paketlerinde (pps) aktarım hızını ölçer.

  • Bölümler, olayların adlandırılmış akışlarında bulunur. Event Hubs bu akış olay hub'larını çağırır. Kafka'da bunlar konu başlıklarıdır.

  • Tüketiciler , konulara abone olan süreçler veya uygulamalardır. Her tüketici olay akışının belirli bir alt kümesini okur. Bu alt küme birden fazla bölüm içerebilir. Ancak işlem hattı, her bölümü bir kerede tüketici grubu başına yalnızca bir tüketiciye atayabilir.

  • Birden çok tüketici, tüketici gruplarını oluşturabilir. Bir grup bir konuya abone olduğunda, gruptaki her tüketici olay akışının ayrı bir görünümüne sahiptir. Uygulamalar birbirinden bağımsız olarak, kendi hızlarında çalışır. İşlem hattı, yük paylaşımı için tüketici gruplarını da kullanabilir.

  • Tüketiciler, abone oldukları yayımlanan olayların akışını işler. Tüketiciler ayrıca denetim noktası oluşturma işlemine de girer. Bu işlem aracılığıyla aboneler, bölüm olay dizisi içindeki konumlarını işaretlemek için uzaklıkları kullanır. Uzaklık, tüketicinin okuduğu son olayı tanımlamak için yer işareti gibi çalışan bir yer tutucudur.

Senaryo ayrıntıları

Bu belgede özellikle aşağıdaki stratejiler ele alınmaktadır:

  • Bölümlere olay atama.
  • Kaç bölüm kullanılacağı.
  • Yeniden dengeleme sırasında abonelere bölüm atama.

Aşağıdakiler dahil olmak üzere birçok olay alımı teknolojisi vardır:

Bölümleme stratejileri sunmanın yanı sıra, bu belge Event Hubs ile Kafka'da bölümleme arasındaki farklara da işaret eder.

Öneriler

Bölümleme stratejisi geliştirirken aşağıdaki önerileri göz önünde bulundurun.

Olayları bölümlere dağıtma

Bölümleme stratejisinin bir yönü atama ilkesidir. Bir alma hizmetine ulaşan bir olay bir bölüme gider. Atama ilkesi bu bölümü belirler.

Her olay içeriğini kendi değerinde depolar. Değerin yanı sıra, her olay aşağıdaki diyagramda gösterildiği gibi bir anahtar da içerir:

Architecture diagram showing the parts of an event. Each event, or message, consists of a key and a value. Together, multiple events form a stream.

Diyagramın merkezinde birden çok kutu çifti bulunur. Kutuların altındaki etiket, her çiftin bir iletiyi temsil ettiğini gösterir. Her iletide Anahtar etiketli mavi bir kutu ve Değer etiketli bir siyah kutu bulunur. İletiler yatay olarak düzenlenir. Soldan sağa doğru işaret eden iletiler arasındaki oklar, iletilerin bir sıra oluşturduğunu gösterir. İletilerin üstünde Stream etiketi yer alır. Köşeli ayraçlar, dizinin bir akış oluşturduğunu gösterir.

Bu mimarinin bir Visio dosyasını indirin.

Anahtar, olayla ilgili verileri içerir ve atama ilkesinde de rol oynayabilir.

Bölümlere olay atamak için birden çok yaklaşım vardır:

  • Varsayılan olarak, hizmetler olayları bölümler arasında hepsini bir kez deneme şeklinde dağıtır.
  • Üreticiler bir olayla bir bölüm kimliği belirtebilir. Olay daha sonra bu kimlikle bölüme gider.
  • Üreticiler olay anahtarı için bir değer sağlayabilir. Bunu yaptıklarında, karma tabanlı bölümleyici anahtardan bir karma değeri belirler. Olay daha sonra bu karma değerle ilişkili bölüme gider.

Atama ilkesi seçerken şu önerileri göz önünde bulundurun:

  • Tüketiciler yalnızca belirli olaylarla ilgilendiğinde bölüm kimliklerini kullanın. Bu olaylar tek bir bölüme aktığında, tüketici bu bölüme abone olarak bunları kolayca alabilir.
  • Tüketicilerin üretim sırasında olay alması gerektiğinde anahtarları kullanın. Aynı anahtara sahip tüm olaylar aynı bölüme gittiği için anahtar değerlerine sahip olaylar işleme sırasında sıralarını koruyabilir. Tüketiciler daha sonra bunları bu sırayla alır.
  • Kafka ile olay gruplandırma veya sıralama gerekli değilse anahtarlardan kaçının. Üretici, Kafka'daki hedef bölümün durumunu bilmiyor. Anahtar bir olayı devre dışı olan bir bölüme yönlendirirse gecikmeler veya kayıp olaylar oluşabilir. Event Hubs'da anahtarlara sahip olaylar bir bölüme geçmeden önce bir ağ geçidinden geçer. Bu yaklaşım, olayların kullanılamayan bölümlere geçmesini engeller.
  • Verilerin şekli bölümleme yaklaşımını etkileyebilir. Atamalara karar verirken aşağı akış mimarisinin verileri nasıl dağıtacağını düşünün.
  • Tüketiciler belirli bir öznitelikte veri toplarsa, bu öznitelikte de bölümlemeniz gerekir.
  • Depolama verimliliği önemli olduğunda, depolama işlemlerini hızlandırmaya yardımcı olmak için verileri yoğunlaştıran bir öznitelikte bölümleyin.
  • Alma işlem hatları bazen kaynak performans sorunlarıyla ilgili sorunları çözmek için verileri parçalar. Bu ortamlarda, bölümlemeyi parçaların veritabanında nasıl bölündüğüyle hizalayın.

Bölüm sayısını belirleme

Kaç bölüm kullanılacağına karar vermek için şu yönergeleri kullanın:

  • Daha fazla aktarım hızı elde etmek için daha fazla bölüm kullanın. Her tüketici, atanan bölümünden okur. Bu nedenle daha fazla bölümle, daha fazla tüketici aynı anda bir konu başlığından olay alabilir.
  • Hedef aktarım hızınızın megabayt cinsinden değeri kadar en az sayıda bölüm kullanın.
  • Tüketicilerin açlıktan ölmesinden kaçınmak için en az tüketici kadar bölüm kullanın. Örneğin sekiz tüketiciye sekiz bölüm atandığı varsayılır. Abone olan tüm ek tüketicilerin beklemesi gerekir. Alternatif olarak, mevcut bir tüketici başarısız olduğunda bir veya iki tüketiciyi olayları almaya hazır tutabilirsiniz.
  • Bölümlerden daha fazla anahtar kullanın. Aksi takdirde, bazı bölümler herhangi bir olay almaz ve bu da dengesiz bölüm yüklemelerine neden olur.
  • Ayrılmış katman düzeyinde hem Kafka hem de Event Hubs'da, işletim sistemindeki bölüm sayısını değiştirebilirsiniz. Ancak, olay sıralamasını korumak için anahtarlar kullanıyorsanız bu değişikliği yapmaktan kaçının. Nedeni aşağıdaki olguları içerir:
    • Tüketiciler belirli bölümlere ve içerdikleri olayların sırasına güvenir.
    • Bölüm sayısı değiştiğinde, olayların bölümlere eşlemesi değişebilir. Örneğin, bölüm sayısı değiştiğinde bu formül farklı bir atama oluşturabilir: partition assignment = hash key % number of partitions
    • Kafka ve Event Hubs, karıştırmadan önce bölümlere gelen olayları yeniden dağıtmaya çalışmaz. Sonuç olarak, garanti artık olayların yayın sırasında belirli bir bölüme ulaşmasını tutmaz.

Bu yönergelerin yanı sıra, bölüm sayısını belirlemek için bu kaba formülü de kullanabilirsiniz:

max(t/p, t/c)

Aşağıdaki değerler kullanılır:

  • t: Hedef aktarım hızı.
  • p: Tek bir bölümdeki üretim aktarım hızı.
  • c: Tek bir bölümdeki tüketim aktarım hızı.

Örneğin, şu durumu göz önünde bulundurun:

  • İdeal aktarım hızı 2 MB/sn'dir. Formül t için 2 MBps'dir.
  • Bir üretici saniyede 1.000 olay hızında etkinlik göndererek 1 MBps sunar p .
  • Tüketici olayları saniyede 500 olay hızında alır ve bu da 0,5 MBps'ye ayarlanır c .

Bu değerlerle bölüm sayısı 4'tür:

max(t/p, t/c) = max(2/1, 2/0.5) = max(2, 4) = 4

Aktarım hızını ölçerken şu noktaları aklınızda bulundurun:

  • Tüketim aktarım hızını en yavaş tüketici belirler. Ancak, bazen aşağı akış tüketici uygulamaları hakkında bilgi sağlanabilir. Bu durumda, temel olarak bir bölümle başlayarak aktarım hızını tahmin edin. (Bu kurulumu üretim sistemlerinde değil yalnızca test ortamlarında kullanın). Standart katman fiyatlandırması ve bir bölüm içeren Event Hubs, 1 MB/sn ile 20 MB/sn arasında aktarım hızı üretmelidir.

  • Tüketiciler yalnızca üreticiler olayları benzer bir hızda gönderdiğinde alım işlem hattındaki olayları yüksek oranda kullanabilir. Alım işlem hattının toplam gerekli kapasitesini belirlemek için yalnızca tüketicinin değil üreticinin aktarım hızını ölçün.

Yeniden dengeleme sırasında tüketicilere bölüm atama

Tüketiciler abone olduğunda veya aboneliği kaldırdığında işlem hattı, bölümlerin tüketicilere atanma işlemini yeniden dengeler. Varsayılan olarak, Event Hubs ve Kafka yeniden dengeleme için hepsini bir kez deneme yaklaşımı kullanır. Bu yöntem, bölümleri üyeler arasında eşit olarak dağıtır.

Kafka ile işlem hattının atamaları otomatik olarak yeniden dengelemesini istemiyorsanız, tüketicilere statik olarak bölümler atayabilirsiniz. Ancak tüm bölümlerin aboneleri olduğundan ve yüklerin dengelenmiş olduğundan emin olmanız gerekir.

Kafka, varsayılan hepsini bir kez deneme stratejisinin yanı sıra otomatik yeniden dengeleme için iki strateji daha sunar:

  • Aralık atayıcı: Farklı konulardan bölümleri bir araya getirmek için bu yaklaşımı kullanın. Bu atama, aynı sayıda bölümü ve aynı anahtar bölümleme mantığını kullanan konuları tanımlar. Ardından tüketicilere atama yaparken bu konulardaki bölümleri birleştirir.
  • Yapışkan atayıcı: Bölüm hareketini en aza indirmek için bu atamayı kullanın. Hepsini bir kez deneme gibi bu strateji de tekdüzen dağıtım sağlar. Ancak, yeniden dengeleme sırasında mevcut atamaları da korur.

Dikkat edilmesi gereken noktalar

Bölümleme modeli kullanırken bu noktaları göz önünde bulundurun.

Ölçeklenebilirlik

Çok sayıda bölüm kullanmak ölçeklenebilirliği sınırlayabilir:

  • Kafka'da aracılar olay verilerini ve uzaklıkları dosyalarda depolar. Ne kadar çok bölüm kullanırsanız, o kadar açık dosya tanıtıcılarına sahip olursunuz. İşletim sistemi açık dosya sayısını sınırlarsa, bu ayarı yeniden yapılandırmanız gerekebilir.

  • Event Hubs'da kullanıcılar dosya sistemi sınırlamalarıyla karşılaşmaz. Ancak her bölüm kendi Azure blob dosyalarını yönetir ve arka planda iyileştirir. Çok sayıda bölüm, denetim noktası verilerinin bakımını pahalıya getirir. Bunun nedeni G/Ç işlemlerinin zaman alıcı olması ve depolama API'sinin çağrılarının bölüm sayısıyla orantılı olmasıdır.

  • Kafka ve Event Hubs için her üretici, büyük boyutlu bir toplu iş kullanılabilir olana kadar veya belirli bir süre geçene kadar olayları bir arabellekte depolar. Ardından üretici olayları alım işlem hattına gönderir. Üretici her bölüm için bir arabellek tutar. Bölüm sayısı arttığında, istemcinin bellek gereksinimi de genişler. Tüketiciler olayları toplu olarak alırsa, aynı sorunla da karşılaşabilirler. Tüketiciler çok sayıda bölüme abone olduğunda ancak arabelleğe alma için sınırlı belleğe sahip olduğunda sorunlar ortaya çıkabilir.

Kullanılabilirlik

Önemli sayıda bölüm kullanılabilirliği de olumsuz etkileyebilir:

  • Kafka genellikle bölümleri farklı aracılara konumlandırıyor. Bir aracı başarısız olduğunda Kafka, olayların kaybolmasını önlemek için bölümleri yeniden dengeler. Yeniden dengelemek için ne kadar çok bölüm varsa, yük devretme o kadar uzun sürer ve kullanılamazlığı artırır. Bu sorunu önlemek için bölüm sayısını düşük binlerle sınırlayın.

  • Ne kadar çok bölüm kullanırsanız, o kadar fazla fiziksel kaynak devreye alırsınız. İstemci yanıtına bağlı olarak daha fazla hata oluşabilir.

  • Daha fazla bölümle yük dengeleme işleminin daha hareketli parçalar ve daha fazla stresle çalışması gerekir. Geçici özel durumlar oluşabilir. Bu hatalar, ağ sorunları veya aralıklı İnternet hizmeti gibi geçici rahatsızlıklar olduğunda oluşabilir. Bunlar, Event Hubs bazen bölümleri farklı düğümlere taşırken yükseltme veya yük dengeleme sırasında görünebilir. Hataları en aza indirmek için yeniden denemeler ekleyerek geçici davranışı işleyebilir. Bu işlemi basitleştirmek için .NET ve Java SDK'larında EventProcessorClient veya Python ve JavaScript SDK'larında EventHubConsumerClient kullanın.

Performans

Kafka'da olaylar, işlem hattı tüm eşitlenen çoğaltmalar arasında çoğaltıldıktan sonra işlenir . Bu yaklaşım, olayların yüksek oranda kullanılabilir olmasını sağlar. Tüketiciler yalnızca kaydedilmiş olayları aldığından, çoğaltma işlemi gecikme süresine eklenir. Alma işlem hatlarında bu terim, üreticinin bir olayı yayımlaması ile tüketicinin bunu okuması arasındaki süreyi ifade eder. Confluent'in çalıştırıldığı denemelere göre, bir aracıdan diğerine 1.000 bölüm çoğaltmak yaklaşık 20 milisaniye sürebilir. Uçtan uca gecikme süresi en az 20 milisaniyedir. Bölüm sayısı daha da arttığında gecikme süresi de artar. Bu dezavantaj Event Hubs için geçerli değildir.

Güvenlik

Event Hubs'da yayımcılar kendilerini tanımlamak için Paylaşılan Erişim İmzası (SAS) belirteci kullanır. Tüketiciler bir AMQP 1.0 oturumu aracılığıyla bağlanır. Durum bilgisi olan bu çift yönlü iletişim kanalı, iletileri aktarmak için güvenli bir yol sağlar. Kafka ayrıca şifreleme, yetkilendirme ve kimlik doğrulama özellikleri de sunar, ancak bunları kendiniz uygulamanız gerekir.

Bu senaryoyu dağıtın

Aşağıdaki kod örnekleri aktarım hızını korumayı, belirli bir bölüme dağıtmayı ve olay sırasını korumayı gösterir.

Aktarım hızını koruma

Bu örnek günlük toplamayı içerir. Amaç olayları sırayla işlemek değil, belirli bir aktarım hızını korumaktır.

Kafka istemcisi üretici ve tüketici yöntemlerini uygular. Sipariş önemli olmadığından kod belirli bölümlere ileti göndermez. Bunun yerine, varsayılan bölümleme atamasını kullanır:

public static void RunProducer(string broker, string connectionString, string topic)
{
    // Set the configuration values of the producer.
    var producerConfig = new ProducerConfig
    {
        BootstrapServers = broker,
        SecurityProtocol = SecurityProtocol.SaslSsl,
        SaslMechanism = SaslMechanism.Plain,
        SaslUsername = "$ConnectionString",
        SaslPassword = connectionString,
    };

    // Set the message key to Null since the code does not use it.
    using (var p = new ProducerBuilder<Null, string>(producerConfig).Build())
    {
        try
        {
            // Send a fixed number of messages. Use the Produce method to generate
            // many messages in rapid succession instead of the ProduceAsync method.
            for (int i=0; i < NumOfMessages; i++)
            {
                string value = "message-" + i;
                Console.WriteLine($"Sending message with key: not-specified," +
                    $"value: {value}, partition-id: not-specified");
                p.Produce(topic, new Message<Null, string> { Value = value });
            }

            // Wait up to 10 seconds for any in-flight messages to be sent.
            p.Flush(TimeSpan.FromSeconds(10));
        }
        catch (ProduceException<Null, string> e)
        {
            Console.WriteLine($"Delivery failed with error: {e.Error.Reason}");
        }
    }
}

public static void RunConsumer(string broker, string connectionString, string consumerGroup, string topic)
{
    var consumerConfig = new ConsumerConfig
    {
        BootstrapServers = broker,
        SecurityProtocol = SecurityProtocol.SaslSsl,
        SocketTimeoutMs = 60000,
        SessionTimeoutMs = 30000,
        SaslMechanism = SaslMechanism.Plain,
        SaslUsername = "$ConnectionString",
        SaslPassword = connectionString,
        GroupId = consumerGroup,
        AutoOffsetReset = AutoOffsetReset.Earliest
    };

    using (var c = new ConsumerBuilder<string, string>(consumerConfig).Build())
    {
        c.Subscribe(topic);

        CancellationTokenSource cts = new CancellationTokenSource();
        Console.CancelKeyPress += (_, e) =>
        {
            e.Cancel = true;
            cts.Cancel();
        };

        try
        {
            while (true)
            {
                try
                {
                    var message = c.Consume(cts.Token);
                    Console.WriteLine($"Consumed - key: {message.Message.Key}, "+
                        $"value: {message.Message.Value}, " +
                        $"partition-id: {message.Partition}," +
                        $"offset: {message.Offset}");
                }
                catch (ConsumeException e)
                {
                    Console.WriteLine($"Error occurred: {e.Error.Reason}");
                }
            }
        }
        catch(OperationCanceledException)
        {
            // Close the consumer to ensure that it leaves the group cleanly
            // and that final offsets are committed.
            c.Close();
        }
    }
}

Bu kod örneği aşağıdaki sonuçları üretir:

Screenshot showing producer and consumer logs. Events arrived out of order, used a random pattern for partition assignment, and contained no keys.

Bu durumda, konu dört bölüme sahiptir. Aşağıdaki olaylar gerçekleşti:

  • Üretici, her birinde bölüm anahtarı olmayan 10 ileti gönderdi.
  • İletiler bölümlere rastgele bir sırada geldi.
  • Tek bir tüketici dört bölümü de dinledi ve iletileri sıra dışı olarak aldı.

Kod tüketicinin iki örneğini kullansaydı, her örnek dört bölümden ikisine abone olurdu.

Belirli bir bölüme dağıtma

Bu örnekte hata iletileri yer alır. Bazı uygulamaların hata iletilerini işlemesi gerektiğini, ancak diğer tüm iletilerin ortak bir tüketiciye gidebileceğini varsayalım. Bu durumda, üretici belirli bir bölüme hata iletileri gönderir. Hata iletileri almak isteyen tüketiciler bu bölümü dinler. Aşağıdaki kodda bu senaryonun nasıl uygulandığı gösterilmektedir:

// Producer code.
var topicPartition = new TopicPartition(topic, partition);

...

p.Produce(topicPartition, new Message<Null, string> { Value = value });

// Consumer code.
// Subscribe to one partition.
c.Assign(new TopicPartition(topic, partition));

// Use this code to subscribe to a list of partitions.
c.Assign(new List<TopicPartition> {
  new TopicPartition(topic, partition1),
  new TopicPartition(topic, partition2)
});

Bu sonuçların gösterdiği gibi, üretici tüm iletileri bölüm 2'ye gönderdi ve tüketici yalnızca bölüm 2'den iletileri okuyor:

Screenshot showing producer and consumer logs. All events went to partition 2. They arrived in production order, and none contained a key.

Bu senaryoda, bu konuyu dinlemek için başka bir tüketici örneği eklerseniz işlem hattı bölüm atamaz. Yeni tüketici, mevcut tüketici kapanana kadar açlıktan ölecektir. İşlem hattı daha sonra bölümden okumak için farklı, etkin bir tüketici atar. Ancak işlem hattı bu atamayı yalnızca yeni tüketici başka bir bölüme ayrılmış değilse yapar.

Olay sırasını koru

Bu örnek, tüketicinin sırayla işlemesi gereken banka işlemlerini içerir. Bu senaryoda, anahtar olarak her olayın müşteri kimliğini kullanabilirsiniz. Olay değeri için işlemin ayrıntılarını kullanın. Aşağıdaki kod, bu durumun basitleştirilmiş bir uygulamasını gösterir:

Producer code
// This code assigns the key an integer value. You can also assign it any other valid key value.
using (var p = new ProducerBuilder<int, string>(producerConfig).Build())
...
p.Produce(topic, new Message<int, string> { Key = i % 2, Value = value });

Bu kod aşağıdaki sonuçları üretir:

Screenshot showing producer and consumer logs. Events had keys that determined the partition they went to. Within partitions, events arrived in order.

Bu sonuçlarda gösterildiği gibi üretici yalnızca iki benzersiz anahtar kullanmıştı. İletiler daha sonra dört bölüm yerine yalnızca iki bölüme gitti. İşlem hattı, aynı anahtara sahip iletilerin aynı bölüme gitmesini garanti eder.

Katkıda Bulunanlar

Bu makale Microsoft tarafından yönetilir. Başlangıçta aşağıdaki katkıda bulunanlar tarafından yazılmıştır.

Asıl yazar:

Genel olmayan LinkedIn profillerini görmek için LinkedIn'de oturum açın.

Sonraki adımlar