Netty ve Kafka'daki sıfır kopya teknolojisi ne kadar harika?

Sıfır kopya, verilerin ileri geri kopyalanmasına gerek olmadığı anlamına gelir, bu da sistemin performansını büyük ölçüde artırır. Java NIO, Netty, Kafka, RocketMQ ve diğer çerçevelerde sık sık sıfır kopya olduğunu duyuyoruz.Genellikle performans iyileştirmesinin bir vurgusu olarak kullanılır; aşağıdaki birkaç G / Ç kavramıyla başlar ve ardından sıfır kopya analiz eder.

I / O konsepti

Tampon

Tampon, tüm G / Ç'lerin temelidir.G / Ç, verileri arabelleğin içine veya dışına taşımaktan başka bir şey değildir; işlem, işletim sistemine arabellekteki verileri boşaltmasına izin vermek için bir istek gönderen G / Ç işlemlerini gerçekleştirir (Yaz) veya tamponu doldur (oku).

Verileri yüklemek için bir Okuma isteği başlatan bir Java işleminin genel akış şemasına bakalım:

İşlem Okuma isteğini başlattıktan sonra, çekirdek Okuma isteğini aldıktan sonra, önce işlemin ihtiyaç duyduğu verilerin çekirdek alanında zaten mevcut olup olmadığını kontrol edecek, zaten varsa, verileri doğrudan işlemin arabelleğine kopyalayacaktır.

Çekirdek hemen yoksa, disk denetleyicisine diskten veri okumak için bir komut gönderir ve disk denetleyicisi verileri doğrudan çekirdek Okuma arabelleğine yazar Bu adım DMA tarafından tamamlanır.

Bir sonraki adım, verileri çekirdek tarafından işlem arabelleğine kopyalamaktır; işlem bir Yazma isteği başlatırsa, kullanıcı arabelleğindeki verileri çekirdeğin Soket arabelleğine kopyalaması ve ardından verileri DMA aracılığıyla ağ kartına kopyalaması ve göndermesi gerekir. .

Bunun bir alan israfı olduğunu düşünebilirsiniz, her seferinde çekirdek uzayındaki verileri kullanıcı alanına kopyalamanız gerekir, yani sıfır kopyanın ortaya çıkması bu sorunu çözmektir.

Sıfır kopya sağlamanın iki yolu vardır:

  • mmap + yazma
  • Dosya Gönder

Sanal bellek

Tüm modern işletim sistemleri sanal bellek kullanır ve fiziksel adresler yerine sanal adresler kullanır.Bunun avantajları şunlardır:

  • Birden fazla sanal adres aynı fiziksel bellek adresini işaret edebilir.
  • Sanal bellek alanı, gerçek kullanılabilir fiziksel adresten daha büyük olabilir.

İlk özelliği kullanarak, çekirdek alanı adresi ve kullanıcı alanı sanal adresi aynı fiziksel adrese eşlenebilir, böylece DMA, çekirdek ve kullanıcı alanı işlemleri tarafından aynı anda görünen arabelleği doldurabilir.

Kabaca aşağıdaki şekilde gösterildiği gibidir:

Çekirdek ve kullanıcı alanı arasında kopyalama ihtiyacını ortadan kaldıran Java, performansı artırmak için işletim sisteminin bu özelliğini de kullanıyor.Java'nın sıfır kopyalama için desteklediklerine odaklanalım.

mmap + yazma yöntemi

Orijinal okuma + yazma yöntemi yerine mmap + yazma yöntemini kullanın. Mmap, bellek eşleme dosyalarının bir yöntemidir, yani bir dosya veya başka bir nesne, dosya disk adresini ve işlem sanal adres alanında bir sanal adresi gerçekleştirmek için işlemin adres alanına eşlenir Bire bir yazışma.

Bu şekilde, verileri kullanıcı arabelleğine kopyalamak için orijinal çekirdek Okuma arabelleğini kaydedebilirsiniz, ancak yine de verileri çekirdek Soket arabelleğine kopyalamak için çekirdek Okuma arabelleğine ihtiyacınız vardır.

Kabaca aşağıdaki şekilde gösterildiği gibidir:

Dosya gönderme yöntemi

Sendfile sistem çağrısı, ağ üzerinden iki kanal arasında veri aktarımı sürecini basitleştirmek için çekirdek sürüm 2.1'de tanıtıldı.

Sendfile sistem çağrısının tanıtımı yalnızca veri kopyalamayı azaltmakla kalmaz, aynı zamanda aşağıdaki şekilde gösterildiği gibi bağlam anahtarlarının sayısını da azaltır:

Veri aktarımı yalnızca çekirdek alanında gerçekleşir, bu nedenle bağlam anahtarı azalır; ancak hala bir kopya var, bu kopya da atlanabilir mi?

Linux 2.4 çekirdeğinde iyileştirmeler yapıldı.Kernel arabelleğindeki ilgili veri açıklama bilgileri (bellek adresi, ofset) karşılık gelen Soket arabelleğine kaydedilir, böylece çekirdek alanındaki bir CPU Kopyası da kaydedilir.

Java sıfır kopya

MappedByteBuffer

Java NIO tarafından sağlanan FileChannel, açık bir dosya ile MappedByteBuffer arasında sanal bir bellek eşlemesi oluşturabilen map () yöntemini sağlar.

MappedByteBuffer, nesnenin veri öğelerinin diskteki bir dosyada saklanması dışında bellek tabanlı bir arabelleğe benzer olan ByteBuffer'dan miras alır.

Get () yönteminin çağrılması, dosyanın mevcut içeriğini yansıtan diskten verileri alır, put () yöntemini çağırmak diskteki dosyayı günceller ve dosyada yapılan değişiklikler diğer okuyucular tarafından görülebilir.

Basit bir okuma örneğine bakalım ve ardından MappedByteBuffer'ı analiz edelim:

public class MappedByteBufferTest { public static void main (String args) Exception {atar Dosya dosyası = yeni Dosya ("D: //db.txt"); uzun len = dosya.length (); bayt ds = yeni bayt; MappedByteBuffer mappedByteBuffer = yeni FileInputStream (dosya) .getChannel (). Eşleme (FileChannel.MapMode.READ_ONLY, 0, len); for (int offset = 0; offset < len; ofset ++) { bayt b = mappedByteBuffer.get (); ds = b; } Tarayıcı taraması = yeni Tarayıcı (yeni ByteArrayInputStream (ds)). UseDelimiter (""); while (scan.hasNext ()) { System.out.print (scan.next () + ""); } } }

Eşleme esas olarak FileChannel tarafından sağlanan map () aracılığıyla gerçekleştirilir. Map () yöntemi aşağıdaki gibidir:

genel özet MappedByteBuffer haritası (MapMode modu, uzun pozisyon, uzun boy) IOException oluşturur;

Sırasıyla şunları gösteren MapMode, Position ve Size olmak üzere üç parametre sağlanmıştır:

  • MapMode: Eşleştirme modu seçenekleri şunları içerir: READ_ONLY, READ_WRITE, PRIVATE.
  • Durum: Haritalamaya hangi konumdan başlanacağı, bayt sayısının konumu.
  • Boyut: Konumdan kaç bayt geriye doğru.

MapMode'a odaklanın.İlk ikisi sırasıyla salt okunur ve okunabilir ve yazılabilir temsil eder.Tabii ki, istenen eşleme modu Filechannel nesnesinin erişim izinleriyle sınırlıdır.Okuma izinleri olmayan bir dosyada READ_ONLY etkinleştirilirse, bir NonReadableChannelException atılır.

ÖZEL mod, yazarken kopyala eşlemesini temsil eder; bu, put () yöntemi tarafından yapılan herhangi bir değişikliğin özel bir veri kopyasıyla sonuçlanacağı ve kopyadaki verilerin yalnızca MappedByteBuffer örneği tarafından görülebileceği anlamına gelir.

Bu işlem, temeldeki dosyalarda herhangi bir değişiklik yapmaz ve arabellek çöp toplandıktan sonra bu değişiklikler kaybolur.

Map () yönteminin kaynak koduna kabaca bir göz atın:

genel MappedByteBuffer haritası (MapMode modu, uzun konum, uzun boyut) IOException oluşturur { ...Çıkarmak... int pagePosition = (int) (position% assignationGranularity); long mapPosition = position-pagePosition; long mapSize = boyut + pagePosition; Deneyin { // map0'dan hiçbir istisna atılmadıysa, adres geçerlidir addr = map0 (imode, mapPosition, mapSize); } catch (OutOfMemoryError x) { // Bir OutOfMemoryError hafızayı tükettiğimizi gösterebilir // böylece gc'yi zorla ve haritayı yeniden dene System.gc (); Deneyin { Thread.sleep (100); } catch (InterruptedException y) { Thread.currentThread (). İnterrupt (); } Deneyin { addr = map0 (imode, mapPosition, mapSize); } catch (OutOfMemoryError y) { // İkinci bir OOME'den sonra başarısız yeni IOException oluştur ("Harita başarısız oldu", y); } } // Windows'ta ve potansiyel olarak diğer platformlarda, açık // bazı eşleme işlemleri için dosya tanımlayıcı. FileDescriptor mfd; Deneyin { mfd = nd.duplicateForMapping (fd); } catch (IOException ioe) { unmap0 (adres, mapSize); ioe atmak; } assert (IOStatus.checkAll (addr)); assert (addr% assignationGranularity == 0); int isize = (int) boyut; Unmapper um = new Unmapper (addr, mapSize, isize, mfd); eğer ((! yazılabilir) || (imode == MAP_RO)) { Util.newMappedByteBufferR (isize, addr + pagePosition, mfd, um); } Başka { Util.newMappedByteBuffer (isize, addr + pagePosition, mfd, um); } }

Kaba anlam, bellek eşlemeli adresi Yerel yöntemle elde etmektir, başarısız olursa, manuel olarak tekrar GC eşlemeleri yapar.

Son olarak, MappedByteBuffer, bellek eşlemeli adres aracılığıyla somutlaştırılır.MappedByteBuffer'ın kendisi soyut bir sınıftır.Aslında, burada gerçekten örneklenen DirectByteBuffer'dır.

DirectByteBuffer

DirectByteBuffer, MappedByteBuffer'dan miras alır ve doğrudan belleğin bir bölümünü açtığı ve JVM'nin bellek alanını işgal etmediği adından tahmin edilebilir.

Önceki bölümde Filechannel tarafından eşlenen MappedByteBuffer, aslında DirectByteBuffer'dır. Elbette, bu yönteme ek olarak, manuel olarak da bir alan açabilirsiniz:

ByteBuffer directByteBuffer = ByteBuffer.allocateDirect (100);

Yukarıdakiler 100 baytlık doğrudan bellek alanı açar.

Kanaldan Kanala iletim

Dosyaları bir konumdan diğerine aktarmak genellikle gereklidir. FileChannel, aktarımın verimliliğini artırmak için transferTo () yöntemini sağlar. İlk olarak basit bir örneğe bakalım:

public class ChannelTransfer { public static void main (String argv) Exception {atar Dize dosyaları = yeni Dize; dosyalar = "D: //db.txt"; catFiles (Channels.newChannel (System.out), dosyalar); } özel statik void catFiles (WritableByteChannel hedefi, String dosyaları) İstisna atar { for (int i = 0; i < files.length; i ++) { FileInputStream fis = new FileInputStream (dosyalar ); FileChannel kanalı = fis.getChannel (); channel.transferTo (0, channel.size (), hedef); channel.close (); fis.close (); } } }

Dosya verileri, FileChannel'in transferTo () yöntemi ile System.out kanalına aktarılır. Arayüz aşağıdaki gibi tanımlanır:

public abstract long transferTo (uzun pozisyon, uzun sayım, WritableByteChannel hedefi) IOException oluşturur;

Birkaç parametrenin anlaşılması da kolaydır. Bunlar, aktarımı başlatmak için konum, aktarılacak bayt sayısı ve hedef kanaldır; transferTo (), verileri aktarmak için ara tampon olmadan bir kanalı diğerine çapraz bağlamayı sağlar.

Not: Ara tampona ihtiyaç duymamanın iki anlamı vardır: ilk katman, çekirdek tamponunu kopyalamak için bir kullanıcı alanı tamponuna ihtiyaç duymaz, diğer katman iki kanal için kendi çekirdek tamponuna sahiptir ve iki çekirdek tamponu da kullanılabilir. Verileri kopyalamaya gerek yok.

Netty sıfır kopya

Netty, sıfır kopya arabelleği sağlar. Veri iletirken, son işlenen verilerin birleştirilmesi ve tek bir iletilen mesaj için bölünmesi gerekir. NIO'nun yerel ByteBuffer'ı bunu yapamaz. Netty, sağlanan Bileşik (kombinasyon) ve Slice'ı kullanır (Böl) Sıfır kopya elde etmek için iki Tampon.

Aşağıdaki resme bakın daha net olacak:

TCP katmanı HTTP mesajı iki ChannelBuffers'a bölünmüştür.Bu iki tampon, bizim üst düzey mantığımız (HTTP işleme) için anlamsızdır.

Fakat iki ChannelBuffers birleştirilerek anlamlı bir HTTP mesajı haline gelir.Bu mesaja karşılık gelen ChannelBuffer, "Mesaj" olarak adlandırılabilir. "Sanal Tampon" kelimesi burada kullanılır.

Netty tarafından sağlanan CompositeChannelBuffer kaynak koduna bir göz atabilirsiniz:

public class CompositeChannelBuffer AbstractChannelBuffer'ı genişletir { özel son ByteOrder sırası; özel ChannelBuffer bileşenleri; özel int endeksleri; private int lastAccessedComponentId; özel son boole toplantısı; genel bayt getByte (int dizin) { int componentId = componentId (dizin); dönüş bileşenleri.getByte (dizin indisleri); } ...Çıkarmak...

Bileşenler, alınan tüm Arabellekleri depolamak için kullanılır.

CompositeChannelBuffer yeni bellek açmaz ve tüm ChannelBuffer içeriklerini doğrudan kopyalar, ancak tüm ChannelBuffer referanslarını doğrudan kaydeder ve sıfır kopya elde ederek ChannelBuffer alt kanalında okur ve yazar.

Diğer sıfır kopya

RocketMQ mesajları, commitlog dosyasına sırayla yazılır ve ardından tüketim kuyruğu dosyası bir indeks olarak kullanılır.

RocketMQ, Tüketici isteklerine yanıt vermek için sıfır kopya mmap + yazma kullanır.

Benzer şekilde Kafka'da büyük miktarda ağ verisi diske saklanır ve disk dosyaları ağ üzerinden gönderilir.Kafka Sendfile sıfır kopyalama yöntemini kullanır.

sonuç olarak

Sıfır kopya basitçe Java'daki nesnelerin olasılığıyla anlaşılırsa, aslında nesne referanslarını kullanır ve başvurulan her nesnede yapılan değişiklikler nesneyi değiştirebilir ve her zaman yalnızca bir nesne olacaktır.

B2B e-ticaret Yiding'i örnek olarak alın, veri gömme noktalarından ürün yöneticilerinin bakış açısından bahsedin
önceki
Xu Dongdong'un çıplak bir omuz resmi olduğu sürece, çılgınca yayılacak! Straplez kırmızı elbise ile retro örgülü saç, güzel resim
Sonraki
"He Fanxing" in kayınbiraderi aşkıyla karşılaştırıldığında, Song Qian'ın gardırobu benim istediğim şey, ikisinin de uzun bacakları var.
Zhang Tianai balık kuyruğu eteği giyen gerçekten şeytan! Zhuangshan Kendall, retro ve modern oyun tarzıyla hiç kaybetmiyor
Kaz yumurtasının sarısı sarkan renkte olmalı ve onu giyer giymez yüz kez çekecek, ceket ve bel daha odaklanmış olacak
Maskelerini çıkardıklarında pek çok kişi sıkıntılı olmalı ama kimse onun çirkin olduğunu düşünmez.
Bu grup insan uzun saçlarını kestirdi ama Çin'deki en güzel insanlar oldu! Beyaz meleklere haraç, kısa saç aslında güzel
İşlem dört haneyi aştı ve 30'dan fazla disk ivme kazanıyordu. Hangzhou Sıçan Yılı'nın ilk açılış dalgası geliyor.
Yeni (iyileştirilmiş, genişletilmiş) 60 ilk ve orta okul ve anaokulu! Ningbo Education'ın 2020'deki başlıca olaylarını anlamak için bir resim
Kuru mallar: String'in hashCode'u neden çarpan olarak 31'i seçer?
Bu günlerde evden çalışırken derlenen Kafka bilgi noktalarının tam listesi
Alıştırma: SpringBoot, zamanlama görevlerinin dinamik olarak eklenmesini, silinmesini, başlatılmasını ve durdurulmasını gerçekleştirir
Bu makale, mikro hizmetler altında işlem tutarlılığının nasıl sağlanacağından bahsediyor
Mikro hizmetler için birleşik oturum açma kimlik doğrulaması nasıl yapılır? JWT?
To Top