Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka

Küçük bir kitabın çevirisinin devamı:
Mesaj Aracılarını Anlamak
yazar: Jakub Korab, yayıncı: O'Reilly Media, Inc., yayın tarihi: Haziran 2017, ISBN: 9781492049296.

Önceki çevrilmiş kısım: Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 1 Giriş

BÖLÜM 3

Kafka

Kafka, LinkedIn'de, geleneksel mesaj aracılarının bazı sınırlamalarını aşmak ve bu kitapta "Yükseltme ve küçültme" sayfa 28 başlığı altında açıklanan farklı noktadan noktaya etkileşimler için birden fazla mesaj aracısı kurmak zorunda kalmamak için geliştirildi. Kullanım örnekleri LinkedIn, üreticilerin veya diğer tüketicilerin üretkenliğini etkilemeden bu verilerin birden fazla sistem tarafından kullanılmasına izin verirken, sayfa tıklamaları ve erişim günlükleri gibi çok büyük miktarda verinin tek yönlü alımına büyük ölçüde güvenmiştir. Aslında, Kafka'nın var olma nedeni, Universal Data Pipeline'ın tanımladığı türden bir mesajlaşma mimarisi elde etmektir.

Bu nihai hedef göz önüne alındığında, doğal olarak başka gereksinimler ortaya çıktı. Kafka'nın yapması gerekenler:

  • Son derece hızlı ol
  • Mesajlarla çalışırken daha fazla bant genişliği sağlayın
  • Yayıncı-Abone ve Noktadan Noktaya modelleri destekler
  • Tüketicileri eklerken yavaşlamayın. Örneğin, hedefteki tüketici sayısı arttıkça ActiveMQ'da hem kuyruğun hem de konunun performansı düşer.
  • Yatay olarak ölçeklenebilir olun; Mesajları sürdüren bir aracı, bunu yalnızca maksimum disk hızında yapabiliyorsa, performansı artırmak için tek bir aracı örneğinin ötesine geçmek mantıklıdır.
  • Mesajları depolamak ve yeniden almak için erişimi sınırlayın

Tüm bunları başarmak için Kafka, istemcilerin ve mesajlaşma aracılarının rollerini ve sorumluluklarını yeniden tanımlayan bir mimariyi benimsedi. JMS modeli, aracının mesajları dağıtmaktan sorumlu olduğu ve müşterilerin yalnızca mesaj gönderme ve alma konusunda endişelenmesi gereken, çok aracı odaklıdır. Öte yandan Kafka, son derece hızlı ve ölçeklenebilir bir aracı karşılığında ilgili mesajların tüketicilere adil bir şekilde dağıtılması gibi geleneksel bir aracının birçok özelliğini üstlenen müşteri merkezli müşteri merkezlidir. Geleneksel mesajlaşma sistemleriyle çalışmış kişiler için Kafka ile çalışmak temel bir fikir değişikliği gerektirir.
Bu mühendislik yönü, geleneksel bir komisyoncuya kıyasla verimi birçok büyüklük sırasına göre artırabilen bir mesajlaşma altyapısının oluşturulmasına yol açmıştır. Göreceğimiz gibi, bu yaklaşımın tavizleri vardır, bu da Kafka'nın belirli türdeki iş yükleri ve yüklü yazılımlar için uygun olmadığı anlamına gelir.

Birleşik Hedef Modeli

Yukarıda açıklanan gereksinimleri karşılamak için Kafka, yayınla-abone ol ve noktadan noktaya mesajlaşmayı tek bir hedef türü altında birleştirdi - başlık. Bu, "konu" kelimesinin (konudan) okumanın dayanılmaz olduğu bir yayın mekanizmasını ifade ettiği mesajlaşma sistemleriyle çalışan kişiler için kafa karıştırıcıdır. Kafka konuları, bu kitabın girişinde tanımlandığı gibi, karma bir hedef türü olarak düşünülmelidir.

Bu bölümün geri kalanında, aksini açıkça belirtmedikçe, "konu" terimi bir Kafka konusuna atıfta bulunacaktır.

Konuların nasıl davrandığını ve hangi garantileri sağladığını tam olarak anlamak için öncelikle Kafka'da nasıl uygulandıklarına bakmamız gerekir.
Kafka'daki her konunun kendi günlüğü vardır.
Kafka'ya mesaj gönderen üreticiler bu günlüğe yazar ve tüketiciler sürekli ileriye doğru hareket eden işaretçileri kullanarak günlükten okur. Kafka, günlüğün en eski bölümlerini, bu bölümlerdeki mesajlar okunmuş olsun veya olmasın, periyodik olarak siler. Kafka'nın tasarımının merkezi bir parçası, komisyoncunun mesajların okunup okunmadığını umursamamasıdır - bu müşterinin sorumluluğundadır.

"Günlük" ve "işaretçi" terimleri, Kafka belgeleri. Bu iyi bilinen terimler burada anlamaya yardımcı olmak için kullanılmaktadır.

Bu model, tüm kuyruklardaki mesajların aynı günlükte saklandığı ve aracının mesajları okunduktan sonra silinmiş olarak işaretlediği ActiveMQ'dan tamamen farklıdır.
Şimdi biraz daha derine inelim ve konu günlüğüne daha detaylı bakalım.
Kafka günlüğü birkaç bölümden oluşur (Şekil 3-1). Kafka, her bölümde katı sıralamayı garanti eder. Bu, bölüme belirli bir sırayla yazılan mesajların aynı sırayla okunacağı anlamına gelir. Her bölüm, aşağıdakileri içeren hareketli bir günlük dosyası olarak uygulanır: alt küme (altküme) üreticiler tarafından konuya gönderilen tüm mesajların. Oluşturulan konu, varsayılan olarak bir bölüm içerir. Bölme fikri yatay ölçeklendirme için Kafka'nın ana fikridir.

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-1. Bölmeler Kafka

Bir yapımcı bir Kafka konusuna mesaj gönderdiğinde, mesajın hangi bölüme gönderileceğine karar verir. Buna daha sonra daha detaylı bakacağız.

mesajları okumak

İletileri okumak isteyen istemci, adı verilen adlandırılmış bir işaretçiyi yönetir. tüketici grubu, hangisine işaret ediyor telafi etmek bölümdeki mesajlar. Ofset, bir bölümün başlangıcında 0'dan başlayan artımlı bir konumdur. API'de kullanıcı tanımlı group_id aracılığıyla başvurulan bu tüketici grubu şuna karşılık gelir: tek bir mantıksal tüketici veya sistem.

Çoğu mesajlaşma sistemi, mesajları paralel olarak işlemek için birden fazla örnek ve iş parçacığı kullanarak hedeften veri okur. Bu nedenle, genellikle aynı tüketici grubunu paylaşan birçok tüketici örneği olacaktır.

Okuma sorunu şu şekilde temsil edilebilir:

  • Konunun birden çok bölümü var
  • Birden çok tüketici grubu bir konuyu aynı anda kullanabilir
  • Bir tüketici grubu birden fazla ayrı örneğe sahip olabilir

Bu önemsiz olmayan çoktan çoğa problemdir. Kafka'nın tüketici grupları, tüketici eşgörünümleri ve bölümler arasındaki ilişkileri nasıl ele aldığını anlamak için, giderek daha karmaşık hale gelen bir dizi okuma senaryosuna bakalım.

Tüketiciler ve tüketici grupları

Tek bölümlü bir konuyu başlangıç ​​noktası olarak alalım (Şekil 3-2).

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-2. Tüketici bölümden okur

Bir tüketici örneği kendi group_id'si ile bu konuya bağlandığında, bu bölüme bir okuma bölümü ve bir konum atanır. Bu ofsetin konumu, istemcide en son konuma (en yeni mesaj) veya en erken konuma (en eski mesaj) bir işaretçi olarak yapılandırılabilir. Tüketici, konudan mesajlar ister (anketler), bu da bunların günlükten sırayla okunmasına neden olur.
Ofset konumu, düzenli olarak Kafka'ya geri gönderilir ve dahili bir konuda mesaj olarak saklanır. _consumer_offsets. Normal bir aracının aksine, okunan mesajlar yine de silinmez ve müşteri, halihazırda görüntülenen mesajları yeniden işlemek için ofseti geri alabilir.

İkinci bir mantıksal tüketici, farklı bir group_id kullanarak bağlandığında, birinciden bağımsız ikinci bir işaretçiyi yönetir (Şekil 3-3). Bu nedenle, bir Kafka konusu, tek bir tüketicinin olduğu bir kuyruk gibi ve birden fazla tüketicinin abone olduğu normal bir yayınla-abone ol (pub-sub) konusu gibi davranır ve tüm mesajların saklanması ve birden çok kez işlenebilmesi gibi ek bir fayda sağlar.

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-3. Farklı tüketici gruplarındaki iki tüketici aynı bölümden okur

Bir tüketici grubundaki tüketiciler

Bir tüketici eşgörünümü bir bölümden veri okuduğunda, işaretçi üzerinde tam denetime sahiptir ve önceki bölümde açıklandığı gibi iletileri işler.
Birkaç tüketici örneği aynı group_id ile bir bölüme sahip bir konuya bağlandıysa, en son bağlanan örneğe işaretçi üzerinde kontrol verilir ve o andan itibaren tüm mesajları alır (Şekil 3-4).

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-4. Aynı tüketici grubundaki iki tüketici aynı bölümden okur

Tüketici örneklerinin sayısının bölüm sayısını aştığı bu işleme modu, bir tür münhasır tüketici olarak düşünülebilir. Birden fazla tüketiciyi paralel olarak çalıştırmak ("aktif-aktif" veya "sıcak-sıcak") çok daha tipik olsa da, tüketici bulut sunucularınızın "aktif-pasif" (veya "sıcak-sıcak") kümelenmesine ihtiyacınız varsa bu yararlı olabilir. tüketiciler Beklemede.

Yukarıda açıklanan bu mesaj dağıtım davranışı, normal bir JMS kuyruğunun nasıl davrandığına kıyasla şaşırtıcı olabilir. Bu modelde, kuyruğa gönderilen mesajlar iki tüketici arasında eşit olarak dağıtılacaktır.

Çoğu zaman, birden fazla tüketici örneği oluşturduğumuzda, bunu ya mesajları paralel olarak işlemek ya da okuma hızını artırmak ya da okuma sürecinin kararlılığını artırmak için yaparız. Aynı anda yalnızca bir tüketici örneği bir bölümdeki verileri okuyabildiğinden, bu Kafka'da nasıl elde edilir?

Bunu yapmanın bir yolu, tüm mesajları okumak ve bunları iş parçacığı havuzuna iletmek için tek bir tüketici örneği kullanmaktır. Bu yaklaşım, işlem hacmini artırırken, tüketici mantığının karmaşıklığını artırır ve okuma sisteminin sağlamlığını artırmak için hiçbir şey yapmaz. Tüketicinin bir kopyası elektrik kesintisi veya benzeri bir nedenle bozulursa, çıkarma işlemi durur.

Kafka'da bu sorunu çözmenin kanonik yolu b kullanmaktır.Оdaha fazla bölüm.

bölümleme

Bölümler, okumayı paralel hale getirmek ve bir konuyu tek bir aracı örneğinin bant genişliğinin ötesinde ölçeklendirmek için ana mekanizmadır. Bunu daha iyi anlamak için, iki bölümlü bir konunun olduğu ve bir tüketicinin bu konuya abone olduğu bir durumu ele alalım (Şekil 3-5).

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-5. Bir tüketici birden çok bölümden okur

Bu senaryoda, tüketiciye her iki bölümdeki group_id'sine karşılık gelen işaretçiler üzerinde kontrol verilir ve her iki bölümden gelen mesajları okumaya başlar.
Bu konuya aynı group_id için ek bir tüketici eklendiğinde, Kafka bölümlerden birini birinci tüketiciden ikinci tüketiciye yeniden tahsis eder. Bundan sonra, tüketicinin her örneği konunun bir bölümünden okuyacaktır (Şekil 3-6).

Mesajların 20 iş parçacığında paralel olarak işlenmesini sağlamak için en az 20 bölüme ihtiyacınız vardır. Daha az bölüm varsa, daha önce münhasır tüketiciler tartışmasında açıklandığı gibi, üzerinde çalışacak hiçbir şeyi olmayan tüketicilerle kalırsınız.

Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 3. Kafka
Şekil 3-6. Aynı tüketici grubundaki iki tüketici farklı bölümlerden okur

Bu şema, JMS kuyruğunu korumak için gereken mesaj dağıtımına kıyasla Kafka aracısının karmaşıklığını büyük ölçüde azaltır. Burada aşağıdaki noktalar için endişelenmenize gerek yok:

  • Round-robin tahsisi, önceden getirme arabelleklerinin mevcut kapasitesi veya önceki mesajlara (JMS mesaj gruplarında olduğu gibi) dayalı olarak bir sonraki mesajı hangi tüketicinin alması gerektiği.
  • Hangi tüketicilere hangi mesajların gönderildiği ve arıza durumunda tekrar iletilmesi gerekip gerekmediği.

Kafka komisyoncusunun tek yapması gereken, tüketici talep ettiğinde mesajları sırayla ona iletmektir.

Bununla birlikte, redaksiyon okumasını paralelleştirme ve başarısız mesajları yeniden gönderme gereklilikleri ortadan kalkmaz - bunların sorumluluğu yalnızca aracıdan müşteriye geçer. Bu, kodunuzda dikkate alınmaları gerektiği anlamına gelir.

Mesaj gönderme

Bir mesajın hangi bölüme gönderileceğine karar vermek o mesajın üreticisinin sorumluluğundadır. Bunun yapıldığı mekanizmayı anlamak için öncelikle gerçekte tam olarak ne gönderdiğimizi düşünmemiz gerekir.

JMS'de meta veriler (başlıklar ve özellikler) içeren bir mesaj yapısı ve yükü içeren bir gövde (yük) kullanırken, Kafka'da mesaj şu şekildedir: "anahtar-değer" çifti. Mesaj yükü bir değer olarak gönderilir. Anahtar ise esas olarak bölümleme için kullanılır ve şunları içermelidir: iş mantığına özel anahtarilgili mesajları aynı bölüme koymak için.

Bölüm 2'de, ilgili etkinliklerin tek bir tüketici tarafından sırasıyla işlenmesi gereken çevrimiçi bahis senaryosunu ele aldık:

  1. Kullanıcı hesabı yapılandırılır.
  2. Para hesaba yatırılır.
  3. Hesaptan para çeken bir bahis yapılır.

Her olay bir konuya gönderilen bir mesajsa, doğal anahtar hesap kimliği olacaktır.
Kafka Üretici API'si kullanılarak bir mesaj gönderildiğinde, mesaj ve Kafka kümesinin mevcut durumu göz önüne alındığında, mesajın gönderilmesi gereken bölümün kimliğini döndüren bir bölüm işlevine iletilir. Bu özellik, Partitioner arayüzü aracılığıyla Java'da uygulanmaktadır.

Bu arayüz şöyle görünür:

interface Partitioner {
    int partition(String topic,
        Object key, byte[] keyBytes, Object value, byte[] valueBytes, Cluster cluster);
}

Partitioner uygulaması, bölümü belirlemek için anahtar üzerinde varsayılan genel amaçlı karma algoritmayı veya herhangi bir anahtar belirtilmemişse hepsini bir kez deneme algoritmasını kullanır. Bu varsayılan değer çoğu durumda iyi çalışır. Ancak, gelecekte kendi yazmak isteyeceksiniz.

Kendi bölümleme stratejinizi yazma

Mesaj yüküyle birlikte meta verileri göndermek istediğiniz bir örneğe bakalım. Örneğimizdeki yük, oyun hesabına para yatırma talimatıdır. Bir talimat, iletim sırasında değiştirilmemesinin garanti edilmesini istediğimiz ve bu talimatı yalnızca güvenilir bir yukarı akış sisteminin başlatabileceğinden emin olmak istediğimiz bir şeydir. Bu durumda, gönderen ve alan sistemler, mesajı doğrulamak için bir imzanın kullanılması konusunda anlaşırlar.
Normal JMS'de, basitçe bir "mesaj imzası" özelliği tanımlar ve bunu mesaja ekleriz. Bununla birlikte, Kafka bize meta verileri iletmek için bir mekanizma sağlamaz, yalnızca bir anahtar ve bir değer sağlar.

Değer, bütünlüğünü korumak istediğimiz bir banka havalesi yükü olduğundan, anahtarda kullanılacak veri yapısını tanımlamaktan başka seçeneğimiz yoktur. Bir hesapla ilgili tüm mesajların sırayla işlenmesi gerektiğinden, bölümleme için bir hesap kimliğine ihtiyacımız olduğunu varsayarsak, aşağıdaki JSON yapısını bulacağız:

{
  "signature": "541661622185851c248b41bf0cea7ad0",
  "accountId": "10007865234"
}

İmzanın değeri yüke bağlı olarak değişeceğinden, Partitioner arabiriminin varsayılan karma oluşturma stratejisi, ilgili iletileri güvenilir bir şekilde gruplandırmaz. Bu nedenle, bu anahtarı ayrıştıracak ve hesap kimliği değerini bölümleyecek kendi stratejimizi yazmamız gerekecek.

Kafka, mağazadaki mesajların bozulmasını tespit etmek için sağlama toplamları içerir ve eksiksiz güvenlik özelliklerine sahiptir. Buna rağmen, bazen yukarıdaki gibi sektöre özgü gereksinimler ortaya çıkabilir.

Kullanıcının bölümleme stratejisi, ilgili tüm iletilerin aynı bölümde son bulmasını sağlamalıdır. Bu basit görünse de, ilgili mesajları sıralamanın önemi ve bir konudaki bölüm sayısının ne kadar sabit olduğu gereksinimi karmaşık hale getirebilir.

Bir konudaki bölümlerin sayısı, trafik başlangıçtaki beklentilerin ötesine geçerse eklenebileceğinden zamanla değişebilir. Bu nedenle, mesaj anahtarları orijinal olarak gönderildikleri bölümle ilişkilendirilebilir ve bu, üretici eşgörünümleri arasında paylaşılacak bir durum parçası anlamına gelir.

Dikkate alınması gereken bir diğer faktör, mesajların bölümler arasında eşit dağılımıdır. Tipik olarak, anahtarlar iletiler arasında eşit olarak dağıtılmaz ve hash işlevleri, küçük bir anahtar kümesi için iletilerin adil bir şekilde dağıtılmasını garanti etmez.
İletileri bölmeyi nasıl seçerseniz seçin, ayırıcının kendisinin yeniden kullanılması gerekebileceğini unutmamak önemlidir.

Farklı coğrafi konumlardaki Kafka kümeleri arasında veri çoğaltma gereksinimini göz önünde bulundurun. Bu amaçla Kafka, bir kümedeki mesajları okuyup diğerine aktarmak için kullanılan MirrorMaker adlı bir komut satırı aracıyla birlikte gelir.

MirrorMaker, kümeler arasında çoğaltma yaparken iletiler arasındaki göreli düzeni korumak için çoğaltılan konunun anahtarlarını anlamalıdır, çünkü o konu için bölüm sayısı iki kümede aynı olmayabilir.

Varsayılan karma veya hepsini bir kez deneme çoğu senaryoda iyi çalıştığından, özel bölümleme stratejileri nispeten nadirdir. Ancak, güçlü sipariş garantilerine ihtiyacınız varsa veya yüklerden meta verileri ayıklamanız gerekiyorsa, bölümleme daha yakından bakmanız gereken bir şeydir.

Kafka'nın ölçeklenebilirlik ve performans avantajları, geleneksel komisyoncunun bazı sorumluluklarını müşteriye kaydırmasından gelir. Bu durumda, potansiyel olarak ilgili mesajların paralel çalışan birkaç tüketici arasında dağıtılmasına karar verilir.

JMS komisyoncularının da bu tür gereksinimlerle ilgilenmesi gerekir. İlginç bir şekilde, JMS Mesaj Grupları (yapışkan yük dengeleme (SLB) stratejisinin bir varyasyonu) aracılığıyla uygulanan, aynı tüketiciye ilgili mesajları gönderme mekanizması, gönderenin mesajları ilgili olarak işaretlemesini de gerektirir. JMS söz konusu olduğunda, aracı, bu ilgili mesaj grubunu birçok tüketiciden birine göndermekten ve tüketici düşerse grubun sahipliğini devretmekten sorumludur.

Üretici Sözleşmeleri

Mesaj gönderirken dikkate alınması gereken tek şey bölümleme değildir. Java API'deki Producer sınıfının send() yöntemlerine bir göz atalım:

Future < RecordMetadata > send(ProducerRecord < K, V > record);
Future < RecordMetadata > send(ProducerRecord < K, V > record, Callback callback);

Her iki yöntemin de gönderme işleminin hemen gerçekleştirilmediğini gösteren Future'ı döndürdüğüne hemen dikkat edilmelidir. Sonuç olarak, her etkin bölüm için gönderme arabelleğine bir mesaj (ProducerRecord) yazılır ve aracıya Kafka istemci kitaplığında bir arka plan iş parçacığı olarak gönderilir. Bu, işleri inanılmaz derecede hızlı hale getirirken, deneyimsiz bir uygulamanın, işlemi durdurulursa mesajları kaybedebileceği anlamına gelir.

Her zaman olduğu gibi, performans pahasına gönderme işlemini daha güvenilir hale getirmenin bir yolu var. Bu arabelleğin boyutu 0 olarak ayarlanabilir ve gönderen uygulama iş parçacığı, aşağıdaki gibi aracıya mesaj aktarımı tamamlanana kadar beklemeye zorlanır:

RecordMetadata metadata = producer.send(record).get();

Mesajları okuma hakkında daha fazla bilgi

Mesajları okumak, hakkında spekülasyon yapılması gereken ek karmaşıklıklara sahiptir. Bir mesaja yanıt olarak bir mesaj dinleyicisi çalıştırabilen JMS API'sinden farklı olarak, Tüketici Kafka sadece anketler. Yönteme daha yakından bakalım anket()bu amaçla kullanılır:

ConsumerRecords < K, V > poll(long timeout);

Yöntemin dönüş değeri, birden çok nesne içeren bir kap yapısıdır. tüketici kaydı potansiyel olarak birkaç bölümden. tüketici kaydı kendisi, türetildiği bölüm gibi ilişkili meta verilerle bir anahtar/değer çifti için bir tutucu nesnedir.

Bölüm 2'de tartışıldığı gibi, örneğin müşteri mesajı işleyemezse veya iptal ederse, başarılı veya başarısız bir şekilde işlendikten sonra mesajlara ne olduğunu aklımızda tutmalıyız. JMS'de bu, bir onay modu aracılığıyla gerçekleştirildi. Aracı, başarıyla işlenen mesajı siler veya ham veya sahte mesajı yeniden teslim eder (işlemlerin kullanıldığını varsayarsak).
Kafka çok farklı çalışır. Mesajlar düzeltme okumasından sonra aracıda silinmez ve hata durumunda ne olacağı düzeltme okuması kodunun sorumluluğundadır.

Dediğimiz gibi, tüketici grubu logdaki ofset ile ilişkilendirilir. Bu ofsetle ilişkili günlük konumu, yanıt olarak verilecek bir sonraki mesaja karşılık gelir. anket(). Bu kaymanın arttığı zaman noktası, okuma için belirleyicidir.

Daha önce tartışılan okuma modeline dönersek, mesaj işleme üç aşamadan oluşur:

  1. Okumak için bir mesaj alın.
  2. Mesajı işle.
  3. Mesajı onaylayın.

Kafka tüketicisi bir yapılandırma seçeneğiyle birlikte gelir etkinleştirme.oto.taahhüt. Bu, "otomatik" kelimesini içeren ayarlarda olduğu gibi sık kullanılan bir varsayılan ayardır.

Kafka 0.10'dan önce, bu seçeneği kullanan bir müşteri bir sonraki çağrıda okunan son mesajın ofsetini gönderirdi. anket() işlendikten sonra. Bu, zaten alınmış olan tüm mesajların, müşteri tarafından zaten işlenmişse ancak çağrılmadan önce beklenmedik bir şekilde imha edilmişse yeniden işlenebileceği anlamına geliyordu. anket(). Aracı, bir mesajın kaç kez okunduğuna dair herhangi bir durum tutmadığından, bu mesajı alan bir sonraki tüketici kötü bir şey olduğunu bilmeyecektir. Bu davranış sözde işlemseldi. Fark, yalnızca mesaj başarılı bir şekilde işlendiyse taahhüt edildi, ancak müşteri iptal ederse, komisyoncu aynı mesajı başka bir müşteriye tekrar gönderecekti. Bu davranış, ileti teslim garantisiyle tutarlıydı "en azından bir kere".

Kafka 0.10'da, istemci kodu değiştirildi, böylece taahhüt, yapılandırıldığı gibi istemci kitaplığı tarafından düzenli aralıklarla tetiklenir. auto.commit.interval.ms. Bu davranış, JMS AUTO_ACKNOWLEDGE ve DUPS_OK_ACKNOWLEDGE modları arasında bir yerdedir. Otomatik taahhüt kullanılırken, iletiler gerçekten işlenip işlenmediklerine bakılmaksızın işlenebilir - bu, yavaş bir tüketici durumunda olabilir. Bir tüketici iptal ederse, iletiler bir sonraki tüketici tarafından taahhüt edilen konumdan başlayarak getirilecek ve bu da kaçırılan bir iletiyle sonuçlanabilecektir. Bu durumda Kafka mesajları kaybetmedi, sadece okuma kodu onları işlemedi.

Bu mod, sürüm 0.9'daki ile aynı vaatlere sahiptir: mesajlar işlenebilir, ancak başarısız olursa, ofset kaydedilmeyebilir ve potansiyel olarak teslimatın iki katına çıkmasına neden olur. Çalıştırırken aldığınız daha fazla mesaj anket(), daha fazla bu sorun.

"Kuyruktan Mesaj Okuma" sayfa 21'de tartışıldığı gibi, hata modları dikkate alındığında, bir mesajlaşma sisteminde bir kerelik mesaj teslimi diye bir şey yoktur.

Kafka'da, bir ofseti (offset) işlemek (teslim etmek) için iki yol vardır: otomatik ve manüel. Her iki durumda da, mesaj işlendiyse ancak taahhütten önce başarısız olduysa, mesajlar birden çok kez işlenebilir. Ayrıca, işlem arka planda gerçekleştiyse ve kodunuz işlenmeden tamamlandıysa (belki Kafka 0.9 ve önceki sürümlerde) iletiyi hiç işlememeyi de seçebilirsiniz.

Parametreyi ayarlayarak Kafka tüketici API'sinde manuel ofset tamamlama işlemini kontrol edebilirsiniz. etkinleştirme.oto.taahhüt false yapmak ve aşağıdaki yöntemlerden birini açıkça çağırmak:

void commitSync();
void commitAsync();

Mesajı "en az bir kez" işlemek istiyorsanız, ofseti manuel olarak işlemeniz gerekir. commitSync()mesajları işledikten hemen sonra bu komutu yürüterek.

Bu yöntemler, mesajların işlenmeden önce onaylanmasına izin vermez, ancak işlemsel bir görünüm verirken potansiyel işleme gecikmelerini ortadan kaldırmak için hiçbir şey yapmazlar. Kafka'da işlem yoktur. İstemci aşağıdakileri yapma yeteneğine sahip değildir:

  • Sahte bir mesajı otomatik olarak geri alın. İletileri yeniden teslim etme konusunda aracıya güvenemeyecekleri için, sorunlu yükler ve arka uç kesintilerinden kaynaklanan istisnaları tüketicilerin kendileri halletmelidir.
  • Tek bir atomik işlemle birden fazla konuya mesaj gönderin. Birazdan göreceğimiz gibi, farklı konular ve bölümler üzerindeki kontrol, gönderildiğinde işlemleri koordine etmeyen Kafka kümesindeki farklı makinelerde bulunabilir. Bu yazının yazıldığı sırada, KIP-98 ile bunu mümkün kılmak için bazı çalışmalar yapılmıştır.
  • Bir konudaki bir mesajı okumayı başka bir konuya başka bir mesaj göndermekle ilişkilendirin. Yine, Kafka mimarisi tek bir veri yolu olarak çalışan birçok bağımsız makineye bağlıdır ve bunu gizlemek için hiçbir girişimde bulunulmaz. Örneğin, bağlantı kurmanıza izin verecek hiçbir API bileşeni yoktur. tüketici и Продюсер bir işlemde. JMS'de bu, nesne tarafından sağlanır. oturumyaratılanlardan Mesaj Yapımcıları и Mesaj Tüketicileri.

İşlemlere güvenemezsek, geleneksel mesajlaşma sistemleri tarafından sağlananlara daha yakın semantiği nasıl sağlayabiliriz?

Bir tüketici çökmesi sırasında olduğu gibi, mesaj işlenmeden önce tüketicinin ofsetinin artma olasılığı varsa, o zaman tüketicinin, kendisine bir bölüm atandığında tüketici grubunun mesajı kaçırıp kaçırmadığını bilmesinin hiçbir yolu yoktur. Bu nedenle, bir strateji ofseti önceki konuma geri sarmaktır. Kafka tüketici API'si bunun için aşağıdaki yöntemleri sağlar:

void seek(TopicPartition partition, long offset);
void seekToBeginning(Collection < TopicPartition > partitions);

yöntem aramak() yöntemi ile kullanılabilir
ofsetlerForTimes(Harita zaman damgalarıAramak için) geçmişte belirli bir noktada bir duruma geri sarmak için.

Örtülü olarak, bu yaklaşımın kullanılması, daha önce işlenen bazı iletilerin okunup yeniden işlenme olasılığının çok yüksek olduğu anlamına gelir. Bundan kaçınmak için, Bölüm 4'te açıklandığı gibi, daha önce görüntülenen mesajları takip etmek ve kopyaları ortadan kaldırmak için idempotent okumayı kullanabiliriz.

Alternatif olarak, mesaj kaybı veya tekrarı kabul edilebilir olduğu sürece tüketici kodunuz basit tutulabilir. Günlük olaylarını işleme, ölçümler, tıklama izleme vb. gibi Kafka'nın yaygın olarak kullanıldığı kullanım durumlarını düşündüğümüzde, bireysel iletilerin kaybının çevredeki uygulamalar üzerinde önemli bir etkisinin olma ihtimalinin düşük olduğunu anlıyoruz. Bu gibi durumlarda, varsayılan değerler tamamen kabul edilebilir. Öte yandan, uygulamanızın ödeme göndermesi gerekiyorsa, her bir mesajla ayrı ayrı ilgilenmelisiniz. Her şey içeriğe bağlı.

Kişisel gözlemler, mesajların yoğunluğu arttıkça her bir mesajın değerinin azaldığını göstermektedir. Büyük mesajlar toplu halde görüntülendiğinde değerli olma eğilimindedir.

Yüksek kullanılabilirlik

Kafka'nın yüksek kullanılabilirliğe yaklaşımı, ActiveMQ'nun yaklaşımından çok farklıdır. Kafka, tüm aracı örneklerinin aynı anda ileti alıp dağıttığı ölçeklenebilir kümeler etrafında tasarlanmıştır.

Bir Kafka kümesi, farklı sunucularda çalışan birden çok aracı örneğinden oluşur. Kafka, her düğümün kendi ayrılmış depolama alanına sahip olduğu sıradan bağımsız donanım üzerinde çalışacak şekilde tasarlanmıştır. Ağa bağlı depolama (SAN) kullanımı, birden fazla bilgi işlem düğümü zaman için rekabet edebileceğinden önerilmez.Ыe depolama aralıkları ve çakışmalar oluşturun.

Kafka her zaman sistem. Birçok büyük Kafka kullanıcısı kümelerini asla kapatmaz ve yazılım her zaman sıralı bir yeniden başlatmayla güncellenir. Bu, aracılar arasındaki iletiler ve etkileşimler için önceki sürümle uyumluluğu garanti ederek elde edilir.

Bir sunucu kümesine bağlı aracılar hayvan bakıcısıbir yapılandırma veri kaydı işlevi gören ve her aracının rollerini koordine etmek için kullanılan . ZooKeeper'ın kendisi, kurarak bilgilerin çoğaltılması yoluyla yüksek kullanılabilirlik sağlayan dağıtılmış bir sistemdir. çoğunluk.

Temel durumda, aşağıdaki özelliklere sahip bir Kafka kümesinde bir konu oluşturulur:

  • Bölüm sayısı. Daha önce tartışıldığı gibi, burada kullanılan kesin değer, istenen paralel okuma düzeyine bağlıdır.
  • Çoğaltma faktörü (faktör), kümedeki kaç tane aracı kurumunun bu bölüm için günlük içermesi gerektiğini belirler.

Koordinasyon için ZooKeepers'ı kullanan Kafka, kümedeki aracılar arasında yeni bölümleri adil bir şekilde dağıtmaya çalışır. Bu, Denetleyici görevi gören tek bir örnek tarafından yapılır.

İşlem esnasında her konu bölümü için Kontrolör aracıya roller atama lider (lider, usta, sunucu) ve takipçiler (takipçiler, köleler, astlar). Bu bölüm için lider konumunda olan broker, üreticiler tarafından kendisine gönderilen tüm mesajları almaktan ve mesajları tüketicilere dağıtmaktan sorumludur. Mesajlar bir konu bölümüne gönderildiğinde, bu bölüm için takipçi görevi gören tüm aracı düğümlere kopyalanır. Bir bölüm için günlükleri içeren her düğüme denir. çoğaltma. Bir komisyoncu, bazı bölümler için lider ve diğerleri için takipçi olarak hareket edebilir.

Lider tarafından tutulan tüm mesajları içeren bir takipçi çağrılır. senkronize çoğaltma (senkronize durumda olan bir replika, senkronize edilmiş replika). Bir bölüm için lider olarak görev yapan bir aracı devre dışı kalırsa, o bölüm için güncel veya senkronize olan herhangi bir aracı lider rolünü üstlenebilir. İnanılmaz derecede sürdürülebilir bir tasarım.

Üretici konfigürasyonunun bir kısmı parametredir. öksürmek, uygulama iş parçacığı göndermeye devam etmeden önce kaç kopyanın bir mesajın alındığını onaylaması (onaylaması) gerektiğini belirler: 0, 1 veya tümü. olarak ayarlanırsa herşey, ardından bir mesaj alındığında lider, konu ayarı tarafından tanımlanan birkaç ipucundan (kendisi dahil) kaydın onaylarını (teşekkürlerini) alır almaz yapımcıya bir onay geri gönderecektir. min.insync.replikalar (varsayılan 1). Mesaj başarılı bir şekilde kopyalanamazsa, üretici bir uygulama istisnası atar (Yeterli DeğilKopyalar veya Yeterli DeğilKopyalarEkledikten Sonra).

Tipik bir yapılandırma, çoğaltma faktörü 3 (bölüm başına 1 lider, 2 takipçi) ve parametresi olan bir konu oluşturur. min.insync.replikalar 2'ye ayarlanır. Bu durumda, küme, konu bölümünü yöneten aracılardan birinin istemci uygulamalarını etkilemeden aşağı inmesine izin verecektir.

Bu, bizi performans ve güvenilirlik arasındaki zaten bilinen dengeye geri getiriyor. Çoğaltma, takipçilerden gelen onaylar (teşekkürler) için ek bekleme süresi pahasına gerçekleşir. Bununla birlikte, paralel olarak çalıştığı için, en az üç düğüme çoğaltma, iki ile aynı performansa sahiptir (ağ bant genişliği kullanımındaki artış göz ardı edilir).

Bu çoğaltma şemasını kullanarak Kafka, işlemle her mesajı fiziksel olarak diske yazma ihtiyacını akıllıca ortadan kaldırır. senkronizasyon(). Üretici tarafından gönderilen her mesaj bölüm günlüğüne yazılacaktır, ancak Bölüm 2'de tartışıldığı gibi, bir dosyaya yazma başlangıçta işletim sisteminin arabelleğinde yapılır. Bu mesaj başka bir Kafka örneğine kopyalanırsa ve hafızasındaysa, liderin kaybı mesajın kendisinin de kaybolduğu anlamına gelmez - senkronize edilmiş bir kopya tarafından devralınabilir.
İşlemi gerçekleştirmeyi reddetme senkronizasyon() Kafka'nın mesajları hafızaya yazabildiği kadar hızlı alabildiği anlamına gelir. Tersine, belleği diske boşaltmaktan ne kadar uzun süre kaçınırsanız o kadar iyidir. Bu nedenle, Kafka aracılarına 64 GB veya daha fazla bellek tahsis edilmesi alışılmadık bir durum değildir. Bu bellek kullanımı, tek bir Kafka örneğinin, geleneksel bir mesaj aracısından binlerce kat daha yüksek hızlarda kolayca çalışabileceği anlamına gelir.

Kafka, işlemi uygulamak için de yapılandırılabilir. senkronizasyon() mesaj paketleri için. Kafka'daki her şey paket yönelimli olduğundan, aslında birçok kullanım durumu için oldukça iyi çalışıyor ve çok güçlü garantiler isteyen kullanıcılar için kullanışlı bir araç. Kafka'nın saf performansının çoğu aracıya paketler halinde gönderilen mesajlardan gelir ve bu mesajlar aracıdan sıralı bloklar kullanılarak okunur. sıfır kopya işlemler (verileri bir bellek alanından diğerine kopyalama görevinin gerçekleştirilmediği işlemler). İkincisi, büyük bir performans ve kaynak kazancıdır ve yalnızca bölüm şemasını tanımlayan temel bir günlük veri yapısının kullanılmasıyla mümkündür.

Bir Kafka kümesinde, tek bir Kafka aracısına göre çok daha iyi performans mümkündür, çünkü konu bölümleri birçok ayrı makinede ölçeklenebilir.

sonuçlar

Bu bölümde, Kafka mimarisinin, geleneksel bir mesaj komisyoncusununkinden çok daha fazla verimle inanılmaz derecede sağlam bir mesajlaşma boru hattı sağlamak için müşteriler ve aracılar arasındaki ilişkiyi nasıl yeniden tasarladığını inceledik. Bunu başarmak için kullandığı işlevsellikten bahsettik ve bu işlevi sağlayan uygulamaların mimarisine kısaca baktık. Bir sonraki bölümde mesajlaşma tabanlı uygulamaların çözmesi gereken yaygın sorunlara bakacağız ve bunlarla başa çıkma stratejilerini tartışacağız. Kullanım durumlarınıza uygunluklarını değerlendirebilmeniz için genel olarak mesajlaşma teknolojilerinden nasıl bahsedeceğinizi ana hatlarıyla açıklayarak bölümü bitireceğiz.

Önceki çevrilmiş kısım: Mesaj simsarlarını anlamak. ActiveMQ ve Kafka ile mesajlaşma mekaniğini öğrenmek. Bölüm 1

Çeviri yapıldı: tele.gg/middle_java

Devam edecek ...

Ankete sadece kayıtlı kullanıcılar katılabilir. Giriş yapLütfen.

Kuruluşunuzda Kafka kullanılıyor mu?

  • Evet

  • Hayır

  • Daha önce kullanıldı, şimdi değil

  • kullanmayı planlıyoruz

38 kullanıcı oy kullandı. 8 kişi çekimser kaldı.

Kaynak: habr.com

Yorum ekle