VKontakte'nin mimarisi ve çalışmaları hakkında SSS

VKontakte'nin yaratılış tarihi Wikipedia'da yer alıyor, Pavel'in kendisi tarafından anlatıldı. Görünüşe göre herkes onu zaten tanıyor. HighLoad++ Pavel'deki sitenin iç yapısı, mimarisi ve yapısı hakkında 2010 yılında bana söylemişti. O zamandan bu yana birçok sunucu sızdırıldı, bu yüzden bilgileri güncelleyeceğiz: parçalara ayıracağız, içlerini çıkaracağız, tartacağız ve VK cihazına teknik açıdan bakacağız.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Alexey Akulovich (AterCattus) VKontakte ekibindeki arka uç geliştiricisi. Bu raporun metni, platformun işleyişi, altyapı, sunucular ve bunlar arasındaki etkileşim hakkında sık sorulan sorulara kolektif bir yanıttır; ancak geliştirmeyle ilgili değildir; yani demir hakkında. Ayrı olarak, veritabanları ve bunun yerine VK'nın sahip olduğu şeyler hakkında, günlüklerin toplanması ve tüm projenin bir bütün olarak izlenmesi hakkında. Kesimin altındaki ayrıntılar.



Dört yıldan fazla bir süredir arka uçla ilgili her türlü görevle ilgileniyorum.

  • Medyayı yükleme, saklama, işleme, dağıtma: video, canlı yayın, ses, fotoğraflar, belgeler.
  • Altyapı, platform, geliştirici izleme, günlükler, bölgesel önbellekler, CDN, özel RPC protokolü.
  • Harici hizmetlerle entegrasyon: anında bildirimler, harici bağlantı ayrıştırma, RSS beslemesi.
  • Cevapları bilinmeyen kodlara dalmayı gerektiren çeşitli sorularda meslektaşlarımıza yardımcı olmak.

Bu süre zarfında sitenin birçok bileşeninde rol aldım. Bu deneyimi paylaşmak istiyorum.

Genel mimari

Her şey her zamanki gibi istekleri kabul eden bir sunucu veya sunucu grubuyla başlar.

Ön sunucu

Ön sunucu, istekleri HTTPS, RTMP ve WSS aracılığıyla kabul eder.

HTTPS - bunlar sitenin ana ve mobil web sürümlerine yönelik isteklerdir: vk.com ve m.vk.com ve API'mızın diğer resmi ve resmi olmayan istemcileri: mobil istemciler, mesajlaşma programları. Bir resepsiyonumuz var RTMP- Ayrı ön sunucularla Canlı yayın trafiği ve WSS- Akış API'si için bağlantılar.

Sunuculardaki HTTPS ve WSS için buna değer nginx. RTMP yayınları için yakın zamanda kendi çözümümüze geçtik kiveancak raporun kapsamı dışındadır. Hata toleransı için bu sunucular ortak IP adreslerini duyurur ve gruplar halinde hareket ederek sunuculardan birinde sorun olması durumunda kullanıcı isteklerinin kaybolmamasını sağlar. HTTPS ve WSS için aynı sunucular, CPU yükünün bir kısmını kendi üzerlerine almak amacıyla trafiği şifreler.

WSS ve RTMP hakkında daha fazla konuşmayacağız, yalnızca genellikle bir web projesiyle ilişkilendirilen standart HTTPS istekleri hakkında konuşacağız.

Backend

Ön tarafın arkasında genellikle arka uç sunucular bulunur. Ön sunucunun istemcilerden aldığı istekleri işlerler.

O kPHP sunucularıHTTPS'nin şifresi zaten çözülmüş olduğundan, HTTP arka plan programının çalıştığı yer. kPHP üzerinde çalışan bir sunucudur ön çatal modelleri: bir ana süreç başlatır, bir grup alt süreç başlatır, dinleme soketlerini onlara iletir ve onlar da isteklerini işler. Bu durumda, işlemler kullanıcıdan gelen her istek arasında yeniden başlatılmaz, ancak yeniden başlatmak yerine durumlarını orijinal sıfır değerli durumuna (istek üstüne istek) sıfırlar.

Yük dağılımı

Tüm arka uçlarımız, herhangi bir isteği işleyebilecek devasa bir makine havuzu değildir. Biz onları ayrı gruplara ayrıldı: genel, mobil, api, video, sahneleme... Ayrı bir makine grubundaki sorun diğerlerini etkilemeyecektir. Videoda sorun yaşanması durumunda müzik dinleyen kullanıcının sorunlardan haberi bile olmayacaktır. İsteğin hangi arka uca gönderileceği, yapılandırmaya göre ön taraftaki nginx tarafından belirlenir.

Metrik toplama ve yeniden dengeleme

Her grupta kaç arabaya ihtiyacımız olduğunu anlamak için QPS'e güvenmeyin. Arka uçlar farklıdır, farklı istekleri vardır ve her isteğin QPS hesaplamasında farklı bir karmaşıklığı vardır. Bu yüzden biz sunucu üzerindeki yük kavramıyla bir bütün olarak çalışıyoruz - CPU ve performans üzerinde.

Bu tür binlerce sunucumuz var. Her fiziksel sunucu, tüm çekirdekleri geri dönüştürmek için bir kPHP grubu çalıştırır (çünkü kPHP tek iş parçacıklıdır).

İçerik Sunucusu

CS veya İçerik Sunucusu bir depolama alanıdır. CS, dosyaları depolayan ve aynı zamanda yüklenen dosyaları ve ana web ön ucunun kendisine atadığı her türlü arka planda senkronize görevi işleyen bir sunucudur.

Dosyaları saklayan on binlerce fiziksel sunucumuz var. Kullanıcılar dosya yüklemeyi sever, biz de bunları saklamayı ve paylaşmayı severiz. Bu sunucuların bir kısmı özel pu/pp sunucuları tarafından kapatılmıştır.

pu/pp

VK'da ağ sekmesini açtıysanız pu/pp'yi gördünüz.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

pu/pp nedir? Bir sunucuyu birbiri ardına kapatırsak, kapatılan sunucuya dosya yüklemek ve indirmek için iki seçenek vardır: doğrudan doğruya sayesinde http://cs100500.userapi.com/path veya ara sunucu aracılığıyla - http://pu.vk.com/c100500/path.

Pu, fotoğraf yüklemenin tarihsel adıdır ve pp, fotoğraf proxy'sidir. Yani, bir sunucu fotoğrafları yüklemek için, diğeri ise yüklemek içindir. Artık yalnızca fotoğraflar yüklenmekle kalmıyor, aynı zamanda ad da korunuyor.

Bu sunucular HTTPS oturumlarını sonlandırişlemci yükünü depolamadan kaldırmak için. Ayrıca kullanıcı dosyaları bu sunucularda işlendiğinden, bu makinelerde saklanan bilgiler ne kadar az hassassa o kadar iyidir. Örneğin, HTTPS şifreleme anahtarları.

Makineler diğer makinelerimiz tarafından kapatıldığı için onlara "beyaz" harici IP'ler vermemeyi göze alabiliriz ve "gri" ver. Bu şekilde IP havuzundan tasarruf ettik ve makineleri dışarıdan erişime karşı korumayı garanti ettik; içine girecek bir IP yok.

Paylaşılan IP'ler üzerinden esneklik. Hata toleransı açısından şema aynı şekilde çalışır; birkaç fiziksel sunucunun ortak bir fiziksel IP'si vardır ve önlerindeki donanım, isteğin nereye gönderileceğini seçer. Diğer seçeneklerden daha sonra bahsedeceğim.

Bu durumda tartışmalı olan nokta şudur. istemci daha az bağlantıyı sürdürüyor. Aynı ana bilgisayara sahip birden fazla makine için aynı IP varsa: pu.vk.com veya pp.vk.com, istemci tarayıcısının bir ana bilgisayara eşzamanlı istek sayısı konusunda bir sınırı vardır. Ancak HTTP/2'nin her yerde olduğu dönemde bunun artık o kadar da geçerli olmadığına inanıyorum.

Planın bariz dezavantajı, tüm trafiği pompala, başka bir sunucu aracılığıyla depoya gider. Trafiği makineler üzerinden pompaladığımız için, aynı şemayı kullanarak video gibi yoğun trafiği henüz pompalayamıyoruz. Bunu doğrudan iletiyoruz - özellikle video için ayrı depolar için ayrı bir doğrudan bağlantı. Daha hafif içeriği bir proxy aracılığıyla iletiyoruz.

Kısa bir süre önce proxy'nin geliştirilmiş bir sürümüne kavuştuk. Şimdi size sıradan olanlardan nasıl farklı olduklarını ve bunun neden gerekli olduğunu anlatacağım.

güneş

Eylül 2017'de daha önce Sun'ı satın alan Oracle, çok sayıda Sun çalışanını işten çıkardı. Şu anda şirketin varlığının sona erdiğini söyleyebiliriz. Yöneticilerimiz yeni sistem için isim seçerken bu şirketin anısına saygı duruşunda bulunmaya karar verdiler ve yeni sisteme Sun adını verdiler. Kendi aramızda ona sadece “güneşler” diyoruz.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

pp'nin birkaç sorunu vardı. Grup başına bir IP - etkisiz önbellek. Birçok fiziksel sunucu ortak bir IP adresini paylaşır ve isteğin hangi sunucuya gideceğini kontrol etmenin bir yolu yoktur. Dolayısıyla aynı dosya için farklı kullanıcılar gelirse, bu sunucularda önbellek varsa, dosya her sunucunun önbelleğinde biter. Bu çok verimsiz bir plan ama hiçbir şey yapılamadı.

Sonuç olarak - içeriği parçalayamıyoruz, çünkü bu grup için belirli bir sunucu seçemiyoruz - ortak bir IP'ye sahipler. Ayrıca bazı dahili nedenlerden dolayı bölgelere bu tür sunucuların kurulması mümkün değildi. Sadece St. Petersburg'da duruyorlardı.

Suns'la birlikte seçim sistemini değiştirdik. Şimdi elimizde her noktaya yayın yönlendirme: dinamik yönlendirme, her noktaya yayın, kendi kendini kontrol etme programı. Her sunucunun kendi bireysel IP'si vardır ancak ortak bir alt ağı vardır. Her şey, bir sunucunun arızalanması durumunda trafiğin aynı gruptaki diğer sunuculara otomatik olarak yayılacağı şekilde yapılandırılmıştır. Artık belirli bir sunucuyu seçmek mümkün, gereksiz önbelleğe alma yokve güvenilirlik etkilenmedi.

Ağırlık desteği. Artık gerektiğinde farklı güçte makineler kurmayı göze alabiliyoruz ve ayrıca geçici sorunlar durumunda, çalışan "güneşlerin" ağırlıklarını değiştirerek üzerlerindeki yükü azaltabiliyoruz, böylece "dinleniyorlar" ve yeniden çalışmaya başlıyorlar.

İçerik kimliğine göre parçalama. Parçalamayla ilgili komik bir şey: Genellikle içeriği parçalıyoruz, böylece farklı kullanıcılar aynı dosyaya aynı "güneş" üzerinden gider, böylece ortak bir önbelleğe sahip olurlar.

Yakın zamanda “Yonca” uygulamasını hayata geçirdik. Bu, sunucunun sorular sorduğu ve kullanıcıların seçenekleri seçerek gerçek zamanlı olarak yanıtladığı, canlı yayındaki çevrimiçi bir sınavdır. Uygulamanın kullanıcıların sohbet edebileceği bir sohbeti var. Yayına aynı anda bağlanabilir 100 binden fazla kişi. Hepsi, tüm katılımcılara gönderilen mesajları yazıyor ve mesajla birlikte bir avatar da geliyor. Bir “güneşte” bir avatar için 100 bin kişi gelse, o zaman bazen bir bulutun arkasına yuvarlanabilir.

Aynı dosyaya yönelik çok sayıdaki taleplere dayanabilmek için, belirli bir içerik türü için, dosyaları bölgedeki mevcut tüm "güneşlere" yayan aptalca bir planı devreye sokuyoruz.

İçeriden güneş

Nginx'te ters proxy, RAM'de veya hızlı Optane/NVMe disklerinde önbelleğe alın. Örnek: http://sun4-2.userapi.com/c100500/path — dördüncü bölge olan ikinci sunucu grubunda bulunan “güneş”e bağlantı. Fiziksel olarak 100500 sunucusunda bulunan yol dosyasını kapatır.

Önbellek

Mimari şemamıza bir düğüm daha ekliyoruz: önbellekleme ortamı.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Aşağıda düzen diyagramı bulunmaktadır bölgesel önbellekleryaklaşık 20 tane var. Bunlar, trafiği kendi üzerinden önbelleğe alabilen önbelleklerin ve “güneşlerin” bulunduğu yerlerdir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bu, multimedya içeriğinin önbelleğe alınmasıdır; burada hiçbir kullanıcı verisi saklanmaz; yalnızca müzik, video, fotoğraflar.

Kullanıcının bölgesini belirlemek için bölgelerde duyurulan BGP ağ öneklerini topluyoruz. Geri dönüş durumunda, IP'yi öneklere göre bulamazsak geoip veritabanını da ayrıştırmamız gerekir. Bölgeyi kullanıcının IP'sine göre belirliyoruz. Kodda kullanıcının bir veya daha fazla bölgesine, yani coğrafi olarak en yakın olduğu noktalara bakabiliriz.

Nasıl çalışır?

Dosyaların popülerliğini bölgeye göre sayıyoruz. Kullanıcının bulunduğu bir dizi bölgesel önbellek ve bir dosya tanımlayıcı vardır - bu çifti alırız ve her indirmede derecelendirmeyi artırırız.

Aynı zamanda, iblisler - bölgelerdeki hizmetler - zaman zaman API'ye gelir ve şöyle der: “Ben falan önbelleğim, bana bölgemdeki henüz bende olmayan en popüler dosyaların bir listesini ver. ” API, derecelendirmeye göre sıralanmış bir grup dosya sunar, arka plan programı bunları indirir, bölgelere götürür ve dosyaları oradan teslim eder. Pu/pp ve Sun'ın önbelleklerden temel farkı budur: Bu dosya önbellekte olmasa bile dosyayı kendileri aracılığıyla hemen verirler ve önbellek dosyayı önce kendisine indirir ve ardından geri vermeye başlar.

Bu durumda elde ederiz kullanıcılara daha yakın içerik ve ağ yükünün yayılması. Örneğin, yoğun saatlerde yalnızca Moskova önbelleğinden 1 Tbit/s'den fazla dağıtım yapıyoruz.

Ama sorunlar var. önbellek sunucuları kauçuk değildir. Süper popüler içerik için bazen ayrı bir sunucu için yeterli ağ bulunmayabilir. Önbellek sunucularımız 40-50 Gbit/s ama böyle bir kanalı tamamen tıkayan içerikler var. Bölgedeki popüler dosyaların birden fazla kopyasının depolanmasını uygulamaya doğru ilerliyoruz. İnşallah yıl sonuna kadar hayata geçireceğiz.

Genel mimariye baktık.

  • İstekleri kabul eden ön sunucular.
  • İstekleri işleyen arka uçlar.
  • İki tür proxy tarafından kapatılan depolar.
  • Bölgesel önbellekler.

Bu diyagramda eksik olan ne? Tabii ki, verileri sakladığımız veritabanları.

Veritabanları veya motorlar

Onlara veritabanları değil, motorlar - Motorlar diyoruz çünkü pratikte genel kabul görmüş anlamda veritabanlarımız yok.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bu gerekli bir önlemdir.. Bunun nedeni, VK'nın popülaritesinin hızla arttığı 2008-2009'da projenin tamamen MySQL ve Memcache üzerinde çalışması ve sorunların ortaya çıkmasıydı. MySQL dosyaları çökertmeyi ve bozmayı severdi, sonrasında kurtarılamazdı ve Memcache'in performansı yavaş yavaş düştü ve yeniden başlatılması gerekti.

Giderek daha popüler hale gelen projenin, verileri bozan kalıcı bir depolama alanına ve yavaşlayan bir önbelleğe sahip olduğu ortaya çıktı. Bu şartlarda büyüyen bir proje geliştirmek zordur. Projenin odaklandığı kritik noktaları kendi bisikletlerimiz üzerinden yeniden yazmaya çalışma kararı alındı.

Çözüm başarılı oldu. Bunu yapmak için hem bir fırsat vardı, hem de aşırı bir zorunluluk vardı çünkü o zamanlar başka ölçeklendirme yolları yoktu. Çok fazla veritabanı yoktu, NoSQL henüz mevcut değildi, yalnızca MySQL, Memcache, PostrgreSQL vardı - hepsi bu.

Evrensel çalışma. Geliştirme süreci C geliştiricilerinden oluşan ekibimiz tarafından yönetildi ve her şey tutarlı bir şekilde yapıldı. Motordan bağımsız olarak, hepsi diske yazılan yaklaşık olarak aynı dosya formatına, aynı başlatma parametrelerine, aynı şekilde işlenen sinyallere sahipti ve uç durumlar ve problemler durumunda yaklaşık olarak aynı şekilde davranıyordu. Motorların büyümesiyle birlikte yöneticilerin sistemi çalıştırması daha kolay hale geldi; bakımı gereken bir hayvanat bahçesi yok ve her yeni üçüncü taraf veri tabanını nasıl çalıştıracaklarını yeniden öğrenmek zorundalar; bu da veri tabanının hızlı ve rahat bir şekilde artırılmasını mümkün kıldı. onların numarası.

Motor türleri

Ekip epeyce motor yazdı. İşte bunlardan bazıları: arkadaş, ipuçları, resim, ipdb, mektuplar, listeler, günlükler, memcached, miyavdb, haberler, nostradamus, fotoğraf, çalma listeleri, pmemcached, sandbox, arama, depolama, beğeniler, görevler, …

Belirli bir veri yapısı gerektiren veya alışılmadık istekleri işleyen her görev için C ekibi yeni bir motor yazar. Neden.

Ayrı bir motorumuz var memcached, normal olana benzeyen, ancak bir sürü güzellik içeren ve yavaşlamayan. ClickHouse değil ama aynı zamanda işe yarıyor. Ayrı olarak mevcuttur önceden önbelleğe alınmış - Mı kalıcı memcached, ayrıca yeniden başlatma sırasında veri kaybını önlemek için verileri RAM'e sığdırmak yerine diskte de depolayabilir. Bireysel görevler için çeşitli motorlar vardır: kuyruklar, listeler, kümeler - projemizin gerektirdiği her şey.

kümeler

Kod açısından bakıldığında motorları veya veritabanlarını süreçler, varlıklar veya örnekler olarak düşünmeye gerek yoktur. Kod, özellikle motor gruplarıyla kümelerle çalışır - küme başına bir tür. Diyelim ki memcached bir küme var; bu sadece bir grup makine.

Kodun fiziksel konumunu, boyutunu veya sunucu sayısını bilmesine gerek yoktur. Belirli bir tanımlayıcıyı kullanarak kümeye gider.

Bunun çalışması için kod ile motorlar arasına bir varlık daha eklemeniz gerekir. vekil.

RPC proxy'si

vekil otobüs bağlantısı, neredeyse tüm sitenin üzerinde çalıştığı. Aynı zamanda elimizde hizmet keşfi yok — bunun yerine, bu proxy için tüm kümelerin ve bu kümenin tüm parçalarının konumunu bilen bir yapılandırma vardır. Yöneticilerin yaptığı budur.

Programcılar bunun ne kadar, nerede ve ne kadara mal olduğunu hiç umursamıyorlar - sadece kümeye gidiyorlar. Bu bize çok şey sağlıyor. Bir istek alındığında, proxy isteği nereye yönlendireceğini bilerek yönlendirir - bunu kendisi belirler.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bu durumda proxy, hizmet hatasına karşı bir koruma noktasıdır. Bazı motorlar yavaşlarsa veya çökerse, proxy bunu anlar ve istemci tarafına uygun şekilde yanıt verir. Bu, zaman aşımını kaldırmanıza olanak tanır - kod, motorun yanıt vermesini beklemez, ancak çalışmadığını ve bir şekilde farklı davranması gerektiğini anlar. Veritabanlarının her zaman çalışmayabileceği gerçeğine göre kod hazırlanmalıdır.

Özel uygulamalar

Bazen motor olarak hala gerçekten standart dışı bir çözüme sahip olmak istiyoruz. Aynı zamanda motorlarımız için özel olarak oluşturulan hazır rpc-proxy'mizin kullanılmamasına, görev için ayrı bir proxy yapılmasına karar verildi.

Hala burada ve orada bulunan MySQL için db-proxy kullanıyoruz ve ClickHouse için - Yavru kedi evi.

Genel olarak bu şekilde çalışıyor. Belirli bir sunucu var, kPHP, Go, Python'u çalıştırıyor - genel olarak RPC protokolümüzü kullanabilen herhangi bir kod. Kod yerel olarak bir RPC proxy'sinde çalışır; kodun bulunduğu her sunucu kendi yerel proxy'sini çalıştırır. Talep üzerine vekil nereye gideceğini anlar.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bir motor diğerine gitmek isterse, komşu olsa bile proxy üzerinden geçer çünkü komşu başka bir veri merkezinde olabilir. Motor kendisinden başka herhangi bir şeyin konumunu bilmeye güvenmemelidir; bu bizim standart çözümümüzdür. Ama istisnalar da var elbette :)

Tüm motorların çalıştığı TL şeması örneği.

memcache.not_found                                = memcache.Value;
memcache.strvalue	value:string flags:int = memcache.Value;
memcache.addOrIncr key:string flags:int delay:int value:long = memcache.Value;

tasks.task
    fields_mask:#
    flags:int
    tag:%(Vector int)
    data:string
    id:fields_mask.0?long
    retries:fields_mask.1?int
    scheduled_time:fields_mask.2?int
    deadline:fields_mask.3?int
    = tasks.Task;
 
tasks.addTask type_name:string queue_id:%(Vector int) task:%tasks.Task = Long;

Bu, en yakın analogu olan ikili bir protokoldür. protobuf. Şema, isteğe bağlı alanları, karmaşık türleri (yerleşik skalerlerin uzantılarını ve sorguları) önceden tanımlar. Her şey bu protokole göre çalışıyor.

TCP/UDP üzerinden TL üzerinden RPC… UDP?

TL şemasının üzerinde çalışan motor isteklerini yürütmek için bir RPC protokolümüz var. Bunların hepsi bir TCP/UDP bağlantısı üzerinden çalışır. TCP anlaşılabilir, ancak neden UDP'ye sıklıkla ihtiyacımız var?

UDP yardımcı olur sunucular arasında çok sayıda bağlantı sorununu önlemek. Her sunucunun bir RPC proxy'si varsa ve genel olarak herhangi bir motora gidebiliyorsa, sunucu başına on binlerce TCP bağlantısı vardır. Yük var ama faydası yok. UDP durumunda bu sorun mevcut değildir.

Yedekli TCP anlaşması yok. Bu tipik bir sorundur: Yeni bir motor veya yeni bir sunucu başlatıldığında aynı anda birçok TCP bağlantısı kurulur. UDP verisi gibi küçük ve hafif istekler için kod ile motor arasındaki tüm iletişim iki UDP paketi: biri bir yöne uçuyor, ikincisi diğer yöne. Bir gidiş-dönüş - ve kod, motordan el sıkışmadan bir yanıt aldı.

Evet, hepsi işe yarıyor çok küçük bir paket kaybı yüzdesiyle. Protokolün yeniden iletim ve zaman aşımı desteği var, ancak çok şey kaybedersek neredeyse TCP alacağız ve bu da pek faydalı değil. UDP'yi okyanusların ötesine taşımıyoruz.

Bu tür binlerce sunucumuz var ve şema aynı: her fiziksel sunucuya bir motor paketi kurulu. Çoğunlukla engelleme olmadan mümkün olduğunca hızlı çalışacak şekilde tek iş parçacıklıdırlar ve tek iş parçacıklı çözümler olarak parçalanırlar. Aynı zamanda elimizde bu motorlardan daha güvenilir bir şey yok ve kalıcı veri depolamaya çok dikkat ediliyor.

Kalıcı veri depolama

Motorlar binlog yazıyor. Binlog, sonuna durum veya veri değişikliğine ilişkin bir olayın eklendiği bir dosyadır. Farklı çözümlerde farklı şekilde adlandırılır: ikili günlük, WAL, AOFancak prensip aynıdır.

Motorun yeniden çalıştırıldığında uzun yıllar boyunca tüm binlog'u yeniden okumasını önlemek için motorlar şunu yazar: anlık görüntüler - mevcut durum. Gerekirse önce binlogdan okurlar, sonra da binlogdan okumayı bitirirler. Tüm binlog'lar TL şemasına göre aynı ikili formatta yazılır, böylece yöneticiler bunları araçlarıyla eşit şekilde yönetebilir. Anlık görüntülere böyle bir ihtiyaç yoktur. Kimin anlık görüntüsünün int olduğunu, motorun büyüsünü ve hangi gövdenin kimse için önemli olmadığını belirten genel bir başlık bulunmaktadır. Bu, anlık görüntüyü kaydeden motorla ilgili bir sorundur.

Çalışma prensibini hızlı bir şekilde anlatacağım. Motorun çalıştığı bir sunucu var. Yazmak için yeni bir boş binlog açar ve ona değişiklik yapmak için bir etkinlik yazar.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bir noktada ya kendisi anlık fotoğraf çekmeye karar verir ya da bir sinyal alır. Sunucu yeni bir dosya oluşturur, tüm durumunu bu dosyaya yazar, geçerli binlog boyutunu - ofset - dosyanın sonuna ekler ve yazmaya devam eder. Yeni bir binlog oluşturulmaz.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bir noktada, motor yeniden başlatıldığında diskte hem bir binlog hem de bir anlık görüntü olacaktır. Motor, anlık görüntünün tamamını okur ve durumunu belirli bir noktada yükseltir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Anlık görüntünün oluşturulduğu andaki konumu ve binlogun boyutunu okur.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Mevcut durumu almak için binlog'un sonunu okur ve daha fazla olay yazmaya devam eder. Bu basit bir şema; tüm motorlarımız buna göre çalışıyor.

Veri çoğaltma

Sonuç olarak, veri çoğaltma işlemimiz ifadeye dayalı — binlog'a herhangi bir sayfa değişikliği yazmıyoruz, yani istekleri değiştir. Ağ üzerinden gelenlere çok benzer, sadece biraz değiştirilmiş.

Aynı şema yalnızca kopyalama için değil aynı zamanda yedekler oluşturmak için. Binlog'a yazan bir yazma yöneticisi olan bir motorumuz var. Yöneticilerin ayarladığı herhangi bir yerde bu binlog kopyalanır ve işte bu kadar; bir yedeğimiz var.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Gerekirse kopya okumaCPU okuma yükünü azaltmak için, binlogun sonunu okuyan ve bu komutları yerel olarak yürüten okuma motoru basitçe başlatılır.

Buradaki gecikme çok küçük ve kopyanın ana makinenin gerisinde ne kadar kaldığını bulmak mümkün.

RPC proxy'sinde veri paylaşımı

Parçalama nasıl çalışır? Proxy hangi küme parçasının gönderileceğini nasıl anlıyor? Kod şunu söylemiyor: "15 parça gönder!" - hayır, bu vekil tarafından yapılır.

En basit şema ilktir — istekteki ilk numara.

get(photo100_500) => 100 % N.

Bu basit bir memcached metin protokolü örneğidir ancak elbette sorgular karmaşık ve yapılandırılmış olabilir. Örnek, sorgudaki ilk sayıyı ve geri kalanı küme boyutuna bölünerek alır.

Bu, tek bir varlığın veri konumuna sahip olmak istediğimizde kullanışlıdır. Diyelim ki 100 bir kullanıcı veya grup kimliğidir ve karmaşık sorgular için bir varlığın tüm verilerinin tek bir parçada olmasını istiyoruz.

İsteklerin kümeye nasıl yayıldığı umurumda değilse başka bir seçenek daha var: tüm parçayı karma haline getirme.

hash(photo100_500) => 3539886280 % N

Ayrıca hash'i, bölümün geri kalanını ve parça numarasını da alıyoruz.

Bu seçeneklerin her ikisi de yalnızca kümenin boyutunu artırdığımızda onu böleceğimiz veya birkaç kat artıracağımız gerçeğine hazırlıklı olduğumuzda işe yarar. Örneğin, 16 parçamız vardı, yeterli değil, daha fazlasını istiyoruz - kesinti olmadan güvenle 32 parça alabiliriz. Katları artırmak yerine artırmak istiyorsak kesintiler olacaktır çünkü kayıp olmadan her şeyi doğru şekilde bölemeyeceğiz. Bu seçenekler faydalıdır, ancak her zaman değil.

İsteğe bağlı sayıda sunucu eklememiz veya kaldırmamız gerekirse, şunu kullanırız: Ketama tarzı halkada tutarlı karma. Ancak aynı zamanda verinin yerelliğini de tamamen kaybederiz; her parçanın kendi küçük yanıtını döndürmesi için isteği kümeye birleştirmemiz ve ardından yanıtları proxy'ye birleştirmemiz gerekir.

Süper spesifik istekler var. Şuna benzer: RPC proxy'si isteği alır, hangi kümeye gidileceğini belirler ve parçayı belirler. Daha sonra ya yazma yöneticileri vardır ya da kümenin replika desteği varsa, talep üzerine bir replikaya gönderilir. Bütün bunları proxy yapıyor.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Kütükler

Günlükleri çeşitli şekillerde yazıyoruz. Bunlardan en açık ve basit olanı günlükleri memcache'e yaz.

ring-buffer: prefix.idx = line

Bir anahtar öneki var - günlüğün adı, bir satır ve bu günlüğün boyutu - satır sayısı var. 0'dan satır sayısı eksi 1'e kadar rastgele bir sayı alıyoruz. Memcache'deki anahtar, bu rastgele sayıyla birleştirilmiş bir önektir. Günlük satırını ve geçerli saati değere kaydediyoruz.

Günlükleri okumak gerektiğinde, Çoklu Al tüm anahtarlar zamana göre sıralanır ve böylece gerçek zamanlı bir üretim günlüğü elde edilir. Bu şema, üretimdeki bir şeyde gerçek zamanlı olarak, hiçbir şeyi bozmadan, durmadan veya diğer makinelere giden trafiğe izin vermeden hata ayıklamanız gerektiğinde kullanılır, ancak bu günlük uzun sürmez.

Günlüklerin güvenilir şekilde depolanması için bir motorumuz var günlük motoru. Tam olarak bu nedenle yaratıldı ve çok sayıda kümede yaygın olarak kullanılıyor. Bildiğim en büyük küme 600 TB'lik paketlenmiş günlükleri depoluyor.

Motor çok eski, zaten 6-7 yıllık kümeler var. Bununla ilgili çözmeye çalıştığımız sorunlar var, örneğin günlükleri depolamak için ClickHouse'u aktif olarak kullanmaya başladık.

ClickHouse'da günlükleri toplama

Bu diyagram motorlarımıza nasıl girdiğimizi gösterir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Yerel olarak RPC aracılığıyla RPC proxy'sine giden bir kod vardır ve motorun nereye gideceğini anlar. ClickHouse'da log yazmak istiyorsak bu şemadaki iki parçayı değiştirmemiz gerekiyor:

  • bazı motorları ClickHouse ile değiştirin;
  • ClickHouse'a erişemeyen RPC proxy'sini RPC aracılığıyla erişebilen bir çözümle değiştirin.

Motor basittir; onu ClickHouse ile bir sunucuyla veya bir sunucu kümesiyle değiştiriyoruz.

ClickHouse'a gitmek için şunları yaptık: Yavru Kedi Evi. Doğrudan KittenHouse'dan ClickHouse'a gidersek baş edemez. Talep olmasa bile çok sayıda makinenin HTTP bağlantılarından toplanır. Planın çalışması için ClickHouse'un bulunduğu bir sunucuda yerel ters proxy yükseltildigerekli bağlantı hacimlerine dayanabilecek şekilde yazılmıştır. Ayrıca verileri kendi içinde nispeten güvenilir bir şekilde arabelleğe alabilir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Bazen RPC şemasını standart olmayan çözümlerde, örneğin nginx'te uygulamak istemeyiz. Bu nedenle KittenHouse, UDP üzerinden günlük alma özelliğine sahiptir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Logların göndericisi ve alıcısı aynı makinede çalışıyorsa, yerel ana bilgisayardaki UDP paketini kaybetme olasılığı oldukça düşüktür. RPC'yi üçüncü taraf bir çözümde uygulama ihtiyacı ile güvenilirlik arasında bir uzlaşma olarak, yalnızca UDP gönderimini kullanıyoruz. Bu şemaya daha sonra döneceğiz.

İzleme

İki tür günlüğümüz var: yöneticiler tarafından sunucularında toplananlar ve geliştiriciler tarafından koddan yazılanlar. İki tür metriğe karşılık gelirler: sistem ve ürün.

Sistem metrikleri

Tüm sunucularımızda çalışır ağ verileriİstatistikleri toplayan ve bunları gönderen Grafit Karbon. Bu nedenle, örneğin Whisper değil, ClickHouse bir depolama sistemi olarak kullanılır. Gerekirse doğrudan ClickHouse'dan okuyabilir veya kullanabilirsiniz. grafana ölçümler, grafikler ve raporlar için. Geliştiriciler olarak Netdata ve Grafana'ya yeterli erişimimiz var.

Ürün metrikleri

Kolaylık sağlamak için birçok şey yazdık. Örneğin, daha ileri bir yere gönderilen Counts, UniqueCounts değerlerini istatistiklere yazmanıza izin veren bir dizi sıradan işlev vardır.

statlogsCountEvent   ( ‘stat_name’,            $key1, $key2, …)
statlogsUniqueCount ( ‘stat_name’, $uid,    $key1, $key2, …)
statlogsValuetEvent  ( ‘stat_name’, $value, $key1, $key2, …)

$stats = statlogsStatData($params)

Daha sonra, sıralama ve gruplandırma filtrelerini kullanabilir ve istatistiklerden istediğimiz her şeyi yapabiliriz - grafikler oluşturabilir, Watchdog'ları yapılandırabiliriz.

Çok yazıyoruz birçok ölçüm olay sayısı günde 600 milyardan 1 trilyona kadardır. Ancak onları korumak istiyoruz. en azından birkaç yılMetriklerdeki eğilimleri anlamak. Hepsini bir araya getirmek henüz çözemediğimiz büyük bir sorun. Size son birkaç yıldır nasıl çalıştığını anlatacağım.

Bu metrikleri yazan fonksiyonlarımız var yerel memcache'eGiriş sayısını azaltmak için. Kısa bir süre içinde yerel olarak piyasaya sürüldü istatistik-arka plan programı tüm kayıtları toplar. Daha sonra iblis, ölçümleri iki sunucu katmanında birleştiriyor günlük toplayıcılar, arkalarındaki katmanın ölmemesi için bir grup makinemizden istatistikleri toplayan.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Gerekirse doğrudan günlük toplayıcılara yazabiliriz.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Ancak stas-daemom'u atlayarak kodu doğrudan toplayıcılara yazmak, toplayıcı üzerindeki yükü arttırdığı için ölçeklenebilirliği zayıf bir çözümdür. Çözüm, yalnızca herhangi bir nedenden dolayı makinedeki memcache istatistik-arka plan programını yükseltemediğimizde veya çöktüğünde ve doğrudan gittiğimizde uygundur.

Daha sonra, günlük toplayıcılar istatistikleri birleştirir miyavDB - bu aynı zamanda metrikleri de saklayabilen veritabanımızdır.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Daha sonra koddan ikili “SQL'e yakın” şekilde seçimler yapabiliriz.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Deney

2018 yazında dahili bir hackathon düzenledik ve diyagramın kırmızı kısmını, metrikleri ClickHouse'da depolayabilecek bir şeyle değiştirme fikri ortaya çıktı. ClickHouse'da günlüklerimiz var; neden denemiyorsunuz?

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

KittenHouse aracılığıyla günlükler yazan bir planımız vardı.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Karar verdik Diyagrama başka bir “*Ev” ekleyin, metrikleri tam olarak kodumuzun UDP aracılığıyla yazdığı formatta alacaktır. Daha sonra bu *House, onları KittenHouse'un anladığı kütükler gibi eklere dönüştürür. Bu günlükleri, onları okuyabilmesi gereken ClickHouse'a mükemmel bir şekilde teslim edebilir.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Memcache, stats-daemon ve logs-collectors veritabanından oluşan şema bu şemayla değiştirildi.

VKontakte'nin mimarisi ve çalışmaları hakkında SSS

Memcache, stats-daemon ve logs-collectors veritabanından oluşan şema bu şemayla değiştirildi.

  • Burada StatsHouse'da yerel olarak yazılan koddan bir gönderi var.
  • StatsHouse, halihazırda SQL eklentilerine dönüştürülmüş olan UDP metriklerini gruplar halinde KittenHouse'a yazar.
  • KittenHouse bunları ClickHouse'a gönderir.
  • Bunları okumak istiyorsak, StatsHouse'u atlayarak, normal SQL kullanarak doğrudan ClickHouse'dan okuruz.

hala mı deneme, ama ortaya çıkmasını seviyoruz. Şemadaki sorunları çözersek belki tamamen ona geçeceğiz. Kişisel olarak ben de öyle umuyorum.

düzen demir tasarrufu sağlamaz. Daha az sayıda sunucuya ihtiyaç vardır, yerel istatistik-arka plan programlarına ve günlük toplayıcılara ihtiyaç yoktur, ancak ClickHouse mevcut şemadakilerden daha büyük bir sunucu gerektirir. Daha az sayıda sunucuya ihtiyaç vardır ancak bunların daha pahalı ve daha güçlü olması gerekir.

Dağıtmak

Öncelikle PHP dağıtımına bakalım. içinde gelişiyoruz git: kullanmak GitLab и TeamCity dağıtım için. Geliştirme dalları ana dalla birleştirilir, test için ana daldan aşamalandırmaya ve aşamalandırmadan üretime birleştirilir.

Dağıtımdan önce, mevcut üretim şubesi ve bir önceki şube alınır ve bunlarda fark dosyaları dikkate alınır - değişiklikler: oluşturuldu, silindi, değiştirildi. Bu değişiklik, değişiklikleri hızlı bir şekilde tüm sunucu filomuza kopyalayabilen özel bir kopyalama hızlı motorun binlog'una kaydedilir. Burada kullanılan doğrudan kopyalama değil, dedikodunun kopyalanması, bir sunucu değişiklikleri en yakın komşularına, değişiklikleri komşularına vb. gönderdiğinde. Bu, kodu tüm filo genelinde onlarca ve saniyeler içinde güncellemenize olanak tanır. Değişiklik yerel replikaya ulaştığında bu yamaları kendi yerel dosya sistemi. Geri alma da aynı şemaya göre gerçekleştirilir.

Ayrıca kPHP'yi de çok sık kullanıyoruz ve onun da kendi geliştirmesi var. git yukarıdaki diyagrama göre. Bundan beri HTTP sunucusu ikili dosyası, o zaman diff üretemeyiz - sürüm ikili dosyası yüzlerce MB ağırlığındadır. Bu nedenle burada başka bir seçenek daha var - sürüm şuraya yazılıyor: binlog kopyalama hızlı. Her yapıda artar ve geri alma sırasında da artar. Sürüm sunuculara çoğaltıldı. Yerel copyfast'lar binlog'a yeni bir sürümün girdiğini görüyor ve aynı dedikodu kopyalamasıyla, ana sunucumuzu yormadan, yükü ağ üzerinden dikkatli bir şekilde dağıtarak ikili dosyanın en son sürümünü kendileri için alıyorlar. Bundan sonra ne olacak zarif yeniden başlatma yeni sürüm için.

Temel olarak ikili olan motorlarımız için şema çok benzer:

  • git ana şubesi;
  • ikili giriş . Deb;
  • sürüm binlog copyfast'e yazılmıştır;
  • sunuculara kopyalanır;
  • sunucu yeni bir .dep dosyası çıkarıyor;
  • dpkg -i;
  • yeni sürüme zarif bir şekilde yeniden başlatma.

Aradaki fark, ikili dosyamızın arşivlerde paketlenmiş olmasıdır. . Debve dışarı pompalarken dpkg -i sisteme yerleştirilir. Neden kPHP ikili olarak dağıtılıyor ve motorlar dpkg olarak dağıtılıyor? Bu şekilde oldu. Çalışıyor; dokunmayın.

Yararlı linkler:

Alexey Akulovich, Program Komitesinin bir parçası olarak yardım edenlerden biri PHP Rusya 17 Mayıs'ta PHP geliştiricileri için son zamanların en büyük etkinliği olacak. Bakın ne kadar harika bir bilgisayarımız var, ne hoparlörler (bunlardan ikisi PHP çekirdeğini geliştiriyor!) - PHP yazarsanız gözden kaçırmayacağınız bir şey gibi görünüyor.

Kaynak: habr.com

Yorum ekle