FAQ tentang arsitektur dan karya VKontakte

Sejarah pembuatan VKontakte ada di Wikipedia, diceritakan oleh Pavel sendiri. Sepertinya semua orang sudah mengenalnya. Tentang internal, arsitektur, dan struktur situs di HighLoad++ Pavel memberitahuku pada tahun 2010. Banyak server yang bocor sejak saat itu, jadi kami akan memperbarui informasinya: kami akan membedahnya, mengeluarkan bagian dalamnya, menimbangnya, dan melihat perangkat VK dari sudut pandang teknis.

FAQ tentang arsitektur dan karya VKontakte

Alexei Akulovich (AterCattus) pengembang backend di tim VKontakte. Transkrip laporan ini adalah jawaban kolektif atas pertanyaan umum tentang pengoperasian platform, infrastruktur, server, dan interaksi di antara mereka, namun bukan tentang pengembangan, yaitu tentang besi. Secara terpisah, tentang database dan apa yang dimiliki VK, tentang pengumpulan log dan pemantauan keseluruhan proyek secara keseluruhan. Detail di bawah potongan.



Selama lebih dari empat tahun saya telah menangani segala macam tugas yang berkaitan dengan backend.

  • Mengunggah, menyimpan, mengolah, mendistribusikan media: video, live streaming, audio, foto, dokumen.
  • Infrastruktur, platform, pemantauan pengembang, log, cache regional, CDN, protokol RPC berpemilik.
  • Integrasi dengan layanan eksternal: pemberitahuan push, penguraian tautan eksternal, umpan RSS.
  • Membantu kolega dengan berbagai pertanyaan, yang jawabannya memerlukan penyelaman ke dalam kode yang tidak diketahui.

Selama waktu ini, saya terlibat dalam banyak komponen situs. Saya ingin berbagi pengalaman ini.

Arsitektur umum

Semuanya, seperti biasa, dimulai dengan server atau sekelompok server yang menerima permintaan.

Server depan

Server depan menerima permintaan melalui HTTPS, RTMP dan WSS.

HTTPS - ini adalah permintaan untuk versi web utama dan seluler dari situs: vk.com dan m.vk.com, dan klien resmi dan tidak resmi lainnya dari API kami: klien seluler, pengirim pesan. Kami mengadakan resepsi RTMP-lalu lintas untuk siaran langsung dengan server depan terpisah dan WSS- koneksi untuk Streaming API.

Untuk HTTPS dan WSS di server, ini sangat berharga nginx. Untuk siaran RTMP, kami baru-baru ini beralih ke solusi kami sendiri menyerah, namun hal ini berada di luar cakupan laporan. Untuk toleransi kesalahan, server ini mengiklankan alamat IP umum dan bertindak secara berkelompok sehingga jika ada masalah pada salah satu server, permintaan pengguna tidak hilang. Untuk HTTPS dan WSS, server yang sama ini mengenkripsi lalu lintas untuk mengambil bagian dari beban CPU sendiri.

Kami tidak akan berbicara lebih jauh tentang WSS dan RTMP, tetapi hanya tentang permintaan HTTPS standar, yang biasanya dikaitkan dengan proyek web.

Backend

Dibelakang depan biasanya terdapat server backend. Mereka memproses permintaan yang diterima server depan dari klien.

Itu server kPHP, tempat daemon HTTP berjalan, karena HTTPS sudah didekripsi. kPHP adalah server yang berjalan model garpu depan: memulai proses master, sekelompok proses anak, meneruskan soket pendengaran ke proses tersebut dan memproses permintaannya. Dalam hal ini, proses tidak dimulai ulang di antara setiap permintaan dari pengguna, tetapi hanya mengatur ulang statusnya ke status nilai nol asli - permintaan demi permintaan, alih-alih memulai ulang.

Distribusi beban

Semua backend kami bukanlah kumpulan mesin besar yang dapat memproses permintaan apa pun. Kami mereka dibagi menjadi beberapa kelompok terpisah: umum, seluler, api, video, pementasan... Masalah pada kelompok mesin tertentu tidak akan memengaruhi mesin lainnya. Jika terjadi masalah dengan video, pengguna yang mendengarkan musik bahkan tidak akan mengetahui masalahnya. Backend mana yang akan dikirimi permintaan ditentukan oleh nginx di depan sesuai dengan konfigurasi.

Pengumpulan dan penyeimbangan ulang metrik

Untuk memahami berapa banyak mobil yang perlu kita miliki di setiap kelompok, kita jangan mengandalkan QPS. Backendnya berbeda, permintaannya berbeda, setiap permintaan memiliki kompleksitas penghitungan QPS yang berbeda. Itu sebabnya kami kami beroperasi dengan konsep beban pada server secara keseluruhan - pada CPU dan kinerja.

Kami memiliki ribuan server seperti itu. Setiap server fisik menjalankan grup kPHP untuk mendaur ulang semua inti (karena kPHP adalah single threaded).

Server Konten

CS atau Content Server merupakan tempat penyimpanan. CS adalah server yang menyimpan file dan juga memproses file yang diunggah dan segala macam tugas sinkron latar belakang yang ditugaskan oleh frontend web utama padanya.

Kami memiliki puluhan ribu server fisik yang menyimpan file. Pengguna senang mengunggah file, dan kami senang menyimpan serta membagikannya. Beberapa server ini ditutup oleh server khusus pu/pp.

pu/hal

Jika Anda membuka tab jaringan di VK, Anda melihat pu/pp.

FAQ tentang arsitektur dan karya VKontakte

Apa itu pu/pp? Jika kita menutup server satu demi satu, maka ada dua pilihan untuk mengupload dan mendownload file ke server yang ditutup: secara langsung melalui http://cs100500.userapi.com/path или melalui server perantara - http://pu.vk.com/c100500/path.

Pu adalah nama historis untuk unggahan foto, dan pp adalah proksi foto. Artinya, satu server untuk mengunggah foto, dan satu lagi untuk mengunggah. Kini tidak hanya foto yang dimuat, tapi namanya tetap dipertahankan.

Server-server ini mengakhiri sesi HTTPSuntuk menghapus beban prosesor dari penyimpanan. Selain itu, karena file pengguna diproses di server ini, semakin sedikit informasi sensitif yang disimpan di mesin ini, semakin baik. Misalnya, kunci enkripsi HTTPS.

Karena mesin tersebut ditutup oleh mesin kami yang lain, kami tidak dapat memberikannya IP eksternal “putih”, dan berikan "abu-abu". Dengan cara ini kami menghemat kumpulan IP dan menjamin melindungi mesin dari akses luar - tidak ada IP untuk masuk ke dalamnya.

Ketahanan terhadap IP bersama. Dalam hal toleransi kesalahan, skemanya bekerja dengan cara yang sama - beberapa server fisik memiliki IP fisik yang sama, dan perangkat keras di depannya memilih ke mana akan mengirim permintaan. Saya akan membicarakan opsi lain nanti.

Poin kontroversialnya adalah dalam kasus ini klien menyimpan lebih sedikit koneksi. Jika ada IP yang sama untuk beberapa mesin - dengan host yang sama: pu.vk.com atau pp.vk.com, browser klien memiliki batasan jumlah permintaan simultan ke satu host. Namun dengan adanya HTTP/2 yang ada di mana-mana, saya yakin hal ini sudah tidak relevan lagi.

Kerugian nyata dari skema ini adalah bahwa hal itu harus dilakukan pompa semua lalu lintas, yang masuk ke penyimpanan, melalui server lain. Karena kami memompa lalu lintas melalui mesin, kami belum dapat memompa lalu lintas padat, misalnya video, menggunakan skema yang sama. Kami mengirimkannya secara langsung - koneksi langsung terpisah untuk penyimpanan terpisah khusus untuk video. Kami mengirimkan konten yang lebih ringan melalui proxy.

Belum lama ini kami mendapatkan versi proxy yang lebih baik. Sekarang saya akan memberi tahu Anda perbedaannya dari yang biasa dan mengapa hal ini perlu.

matahari

Pada bulan September 2017, Oracle, yang sebelumnya membeli Sun, memecat sejumlah besar karyawan Sun. Dapat dikatakan bahwa pada saat itu perusahaan tersebut sudah tidak ada lagi. Saat memilih nama untuk sistem baru, administrator kami memutuskan untuk mengenang perusahaan ini dan menamai sistem baru tersebut Sun. Di antara kami sendiri, kami hanya menyebutnya “matahari”.

FAQ tentang arsitektur dan karya VKontakte

pp punya beberapa masalah. Satu IP per grup - cache tidak efektif. Beberapa server fisik berbagi alamat IP yang sama, dan tidak ada cara untuk mengontrol server mana yang akan dituju permintaannya. Oleh karena itu, jika pengguna berbeda datang untuk file yang sama, maka jika ada cache di server ini, file tersebut akan berakhir di cache masing-masing server. Ini adalah skema yang sangat tidak efisien, tapi tidak ada yang bisa dilakukan.

Akibatnya - kami tidak dapat membagi konten, karena kami tidak dapat memilih server tertentu untuk grup ini - mereka memiliki IP yang sama. Juga karena beberapa alasan internal yang kami miliki tidak mungkin memasang server seperti itu di wilayah tertentu. Mereka hanya berdiri di St. Petersburg.

Dengan adanya matahari, kami mengubah sistem pemilihan. Sekarang kita punya perutean siaran apa pun: perutean dinamis, siaran apa pun, daemon pemeriksaan mandiri. Setiap server memiliki IP masing-masing, tetapi subnetnya sama. Semuanya dikonfigurasi sedemikian rupa sehingga jika satu server gagal, lalu lintas akan tersebar ke server lain dalam grup yang sama secara otomatis. Sekarang dimungkinkan untuk memilih server tertentu, tidak ada cache yang berlebihan, dan keandalan tidak terpengaruh.

Dukungan berat badan. Sekarang kita mampu memasang mesin dengan daya berbeda sesuai kebutuhan, dan juga, jika terjadi masalah sementara, mengubah bobot “matahari” yang bekerja untuk mengurangi beban pada mesin tersebut, sehingga mereka “beristirahat” dan mulai bekerja kembali.

Pembagian berdasarkan id konten. Hal yang lucu tentang sharding: kami biasanya melakukan sharding konten sehingga pengguna yang berbeda membuka file yang sama melalui “matahari” yang sama sehingga mereka memiliki cache yang sama.

Kami baru saja meluncurkan aplikasi “Clover”. Ini adalah kuis online dalam siaran langsung, di mana pembawa acara mengajukan pertanyaan dan pengguna menjawab secara real time, memilih opsi. Aplikasi ini memiliki obrolan tempat pengguna dapat mengobrol. Dapat secara bersamaan terhubung ke siaran lebih dari 100 ribu orang. Mereka semua menulis pesan yang dikirimkan ke semua peserta, dan sebuah avatar disertakan bersama pesan tersebut. Jika 100 ribu orang datang untuk satu avatar di satu “matahari”, terkadang hal itu bisa berguling di balik awan.

Untuk menahan lonjakan permintaan terhadap file yang sama, untuk jenis konten tertentu kami mengaktifkan skema bodoh yang menyebarkan file ke semua “matahari” yang tersedia di wilayah tersebut.

Matahari dari dalam

Proksi terbalik di nginx, cache di RAM atau di disk Optane/NVMe cepat. Contoh: http://sun4-2.userapi.com/c100500/path — tautan ke “matahari”, yang terletak di wilayah keempat, grup server kedua. Ini menutup file jalur, yang secara fisik terletak di server 100500.

Cache

Kami menambahkan node lain ke skema arsitektur kami - lingkungan caching.

FAQ tentang arsitektur dan karya VKontakte

Di bawah ini adalah diagram tata letak cache regional, ada sekitar 20 di antaranya. Ini adalah tempat di mana cache dan "matahari" berada, yang dapat melakukan cache lalu lintas melalui dirinya sendiri.

FAQ tentang arsitektur dan karya VKontakte

Ini adalah cache konten multimedia; tidak ada data pengguna yang disimpan di sini - hanya musik, video, foto.

Untuk menentukan wilayah pengguna, kami kami mengumpulkan awalan jaringan BGP yang diumumkan di wilayah. Dalam kasus fallback, kita juga harus mengurai database geoip jika kita tidak dapat menemukan IP berdasarkan awalan. Kami menentukan wilayah berdasarkan IP pengguna. Dalam kode tersebut, kita dapat melihat satu atau lebih wilayah pengguna - titik-titik yang paling dekat secara geografis.

Bagaimana cara kerjanya?

Kami menghitung popularitas file berdasarkan wilayah. Ada nomor cache regional tempat pengguna berada, dan pengidentifikasi file - kami mengambil pasangan ini dan meningkatkan peringkat dengan setiap unduhan.

Pada saat yang sama, setan - layanan di wilayah - dari waktu ke waktu datang ke API dan berkata: “Saya cache ini dan itu, beri saya daftar file paling populer di wilayah saya yang belum ada pada saya. ” API mengirimkan banyak file yang diurutkan berdasarkan peringkat, daemon mendownloadnya, membawanya ke wilayah dan mengirimkan file dari sana. Inilah perbedaan mendasar antara pu/pp dan Sun dari cache: mereka segera memberikan file melalui dirinya sendiri, meskipun file ini tidak ada dalam cache, dan cache terlebih dahulu mendownload file tersebut ke dirinya sendiri, dan kemudian mulai mengembalikannya.

Dalam hal ini kita dapatkan konten lebih dekat dengan pengguna dan menyebarkan beban jaringan. Misalnya, hanya dari cache Moskow kami mendistribusikan lebih dari 1 Tbit/s selama jam sibuk.

Tapi ada masalah - server cache bukan karet. Untuk konten super populer, terkadang jaringan untuk server terpisah tidak cukup. Server cache kami memiliki kecepatan 40-50 Gbit/dtk, tetapi ada konten yang menyumbat saluran tersebut sepenuhnya. Kami bergerak menuju penerapan penyimpanan lebih dari satu salinan file populer di wilayah tersebut. Saya berharap kami bisa menerapkannya pada akhir tahun ini.

Kami melihat arsitektur umum.

  • Server depan yang menerima permintaan.
  • Backend yang memproses permintaan.
  • Penyimpanan yang ditutup oleh dua jenis proxy.
  • Cache regional.

Apa yang hilang dari diagram ini? Tentu saja database tempat kita menyimpan data.

Database atau mesin

Kami menyebutnya bukan database, tetapi mesin - Mesin, karena kami praktis tidak memiliki database dalam pengertian yang diterima secara umum.

FAQ tentang arsitektur dan karya VKontakte

Ini adalah tindakan yang perlu.. Hal ini terjadi karena pada tahun 2008-2009, ketika VK mengalami pertumbuhan popularitas yang eksplosif, proyek tersebut sepenuhnya bekerja pada MySQL dan Memcache dan terdapat masalah. MySQL suka membuat file crash dan rusak, setelah itu tidak dapat dipulihkan, dan kinerja Memcache secara bertahap menurun dan harus dimulai ulang.

Ternyata proyek yang semakin populer ini memiliki penyimpanan persisten yang merusak data dan cache yang melambat. Dalam kondisi seperti itu, sulit untuk mengembangkan proyek yang sedang berkembang. Diputuskan untuk mencoba menulis ulang hal-hal penting yang menjadi fokus proyek pada sepeda kami sendiri.

Solusinya berhasil. Ada peluang untuk melakukan hal ini, dan juga merupakan kebutuhan ekstrem, karena cara penskalaan lain belum ada pada saat itu. Tidak banyak database, NoSQL belum ada, yang ada hanya MySQL, Memcache, PostrgreSQL - dan hanya itu.

Operasi universal. Pengembangan dipimpin oleh tim pengembang C kami dan semuanya dilakukan secara konsisten. Terlepas dari mesinnya, semuanya memiliki format file yang kira-kira sama yang ditulis ke disk, parameter peluncuran yang sama, memproses sinyal dengan cara yang sama, dan berperilaku kira-kira sama jika terjadi situasi dan masalah edge. Dengan pertumbuhan mesin, akan lebih mudah bagi administrator untuk mengoperasikan sistem - tidak ada kebun binatang yang perlu dipelihara, dan mereka harus mempelajari kembali cara mengoperasikan setiap database pihak ketiga yang baru, yang memungkinkan untuk dengan cepat dan dengan mudah menambah jumlah mereka.

Jenis mesin

Tim menulis beberapa mesin. Berikut beberapa di antaranya: teman, petunjuk, gambar, ipdb, surat, daftar, log, memcached, meowdb, berita, nostradamus, foto, daftar putar, pmemcached, kotak pasir, pencarian, penyimpanan, suka, tugas,…

Untuk setiap tugas yang memerlukan struktur data tertentu atau memproses permintaan yang tidak lazim, tim C menulis mesin baru. Mengapa tidak.

Kami memiliki mesin terpisah memcached, yang mirip dengan yang biasa, tetapi dengan banyak barang, dan tidak melambat. Bukan ClickHouse, tapi juga berfungsi. Tersedia secara terpisah pmemcached - Apakah memcache persisten, yang juga bisa menyimpan data di disk, apalagi muat di RAM, agar tidak kehilangan data saat restart. Ada berbagai mesin untuk tugas individual: antrian, daftar, set - semua yang dibutuhkan proyek kami.

Cluster

Dari perspektif kode, tidak perlu menganggap mesin atau database sebagai proses, entitas, atau contoh. Kode ini bekerja secara khusus dengan cluster, dengan kelompok mesin - satu jenis per cluster. Katakanlah ada cluster memcached - itu hanya sekelompok mesin.

Kode tersebut tidak perlu mengetahui lokasi fisik, ukuran, atau jumlah server sama sekali. Dia pergi ke cluster menggunakan pengenal tertentu.

Agar ini berfungsi, Anda perlu menambahkan satu entitas lagi yang terletak di antara kode dan mesin - wakil.

proksi RPC

Proksi bus penghubung, tempat hampir seluruh situs dijalankan. Pada saat yang sama kita punya tidak ada penemuan layanan — sebagai gantinya, terdapat konfigurasi untuk proksi ini, yang mengetahui lokasi semua cluster dan semua pecahan cluster ini. Inilah yang dilakukan admin.

Pemrogram tidak peduli berapa banyak, di mana, dan berapa biayanya - mereka hanya pergi ke cluster. Ini memungkinkan kita melakukan banyak hal. Saat menerima permintaan, proxy mengalihkan permintaan tersebut, mengetahui di mana - ia menentukannya sendiri.

FAQ tentang arsitektur dan karya VKontakte

Dalam hal ini, proxy adalah titik perlindungan terhadap kegagalan layanan. Jika beberapa mesin melambat atau mogok, maka proxy memahami hal ini dan merespons sesuai dengan sisi klien. Hal ini memungkinkan Anda untuk menghapus batas waktu - kode tidak menunggu mesin merespons, tetapi memahami bahwa itu tidak berfungsi dan perlu berperilaku berbeda. Kode harus disiapkan untuk kenyataan bahwa database tidak selalu berfungsi.

Implementasi khusus

Terkadang kami masih sangat ingin memiliki solusi non-standar sebagai mesin. Pada saat yang sama, diputuskan untuk tidak menggunakan proxy rpc kami yang sudah jadi, yang dibuat khusus untuk mesin kami, tetapi untuk membuat proxy terpisah untuk tugas tersebut.

Untuk MySQL yang masih kami miliki di sana-sini, kami menggunakan db-proxy, dan untuk ClickHouse - rumah kucing.

Secara umum cara kerjanya seperti ini. Ada server tertentu, menjalankan kPHP, Go, Python - secara umum, kode apa pun yang dapat menggunakan protokol RPC kami. Kode berjalan secara lokal pada proksi RPC - setiap server tempat kode tersebut berada menjalankan proksi lokalnya sendiri. Berdasarkan permintaan, proxy memahami ke mana harus pergi.

FAQ tentang arsitektur dan karya VKontakte

Jika satu mesin ingin berpindah ke mesin lain, meskipun itu mesin tetangga, mesin tersebut akan melalui proxy, karena tetangganya mungkin berada di pusat data lain. Mesin tidak boleh bergantung pada mengetahui lokasi apa pun selain dirinya sendiri - ini adalah solusi standar kami. Tapi tentu saja ada pengecualian :)

Contoh skema TL yang mengatur semua mesin beroperasi.

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;

Ini adalah protokol biner, analog terdekatnya adalah protobuf. Skema ini menjelaskan bidang opsional, tipe kompleks - ekstensi skalar bawaan, dan kueri. Semuanya bekerja sesuai dengan protokol ini.

RPC melalui TL melalui TCP/UDP… UDP?

Kami memiliki protokol RPC untuk mengeksekusi permintaan mesin yang berjalan di atas skema TL. Ini semua berfungsi melalui koneksi TCP/UDP. TCP bisa dimengerti, tapi mengapa kita sering membutuhkan UDP?

UDP membantu menghindari masalah banyaknya koneksi antar server. Jika setiap server memiliki proxy RPC dan, secara umum, dapat masuk ke mesin apa pun, maka terdapat puluhan ribu koneksi TCP per server. Ada beban, tapi percuma. Dalam kasus UDP masalah ini tidak ada.

Tidak ada jabat tangan TCP yang berlebihan. Ini adalah masalah umum: ketika mesin baru atau server baru diluncurkan, banyak koneksi TCP dibuat sekaligus. Untuk permintaan kecil dan ringan, misalnya payload UDP, semua komunikasi antara kode dan mesin dilakukan dua paket UDP: yang satu terbang ke satu arah, yang kedua terbang ke arah lain. Satu perjalanan pulang pergi - dan kode menerima respons dari mesin tanpa jabat tangan.

Ya, semuanya berhasil dengan persentase packet loss yang sangat kecil. Protokol ini memiliki dukungan untuk transmisi ulang dan batas waktu, tetapi jika kita kehilangan banyak, kita akan mendapatkan hampir TCP, yang tidak menguntungkan. Kami tidak mengarahkan UDP melintasi lautan.

Kami memiliki ribuan server seperti itu, dan skemanya sama: satu paket mesin dipasang di setiap server fisik. Mereka sebagian besar berupa thread tunggal untuk dijalankan secepat mungkin tanpa pemblokiran, dan di-sharding sebagai solusi thread tunggal. Pada saat yang sama, kami tidak memiliki mesin yang lebih andal daripada mesin ini, dan banyak perhatian diberikan pada penyimpanan data yang persisten.

Penyimpanan data yang persisten

Mesin menulis binlog. Binlog adalah file yang pada akhirnya ditambahkan peristiwa perubahan status atau data. Dalam solusi yang berbeda disebut berbeda: log biner, WAL, AOF, tapi prinsipnya sama.

Untuk mencegah mesin membaca ulang seluruh binlog selama bertahun-tahun saat restart, mesin menulis snapshot - keadaan saat ini. Jika perlu, mereka membacanya terlebih dahulu, lalu menyelesaikan membaca dari binlog. Semua binlog ditulis dalam format biner yang sama - sesuai dengan skema TL, sehingga admin dapat mengelolanya secara merata menggunakan alat mereka. Tidak diperlukan snapshot seperti itu. Ada header umum yang menunjukkan snapshot siapa yang int, keajaiban mesin, dan badan mana yang tidak penting bagi siapa pun. Ini adalah masalah dengan mesin yang merekam snapshot tersebut.

Saya akan segera menjelaskan prinsip operasinya. Ada server tempat mesin berjalan. Dia membuka binlog kosong baru untuk menulis dan menulis acara untuk mengubahnya.

FAQ tentang arsitektur dan karya VKontakte

Pada titik tertentu, dia memutuskan untuk mengambil gambar sendiri, atau dia menerima sinyal. Server membuat file baru, menulis seluruh statusnya ke dalamnya, menambahkan ukuran binlog saat ini - offset - ke akhir file, dan melanjutkan penulisan lebih lanjut. Binlog baru tidak dibuat.

FAQ tentang arsitektur dan karya VKontakte

Pada titik tertentu, ketika mesin dihidupkan ulang, akan ada binlog dan snapshot di disk. Mesin membaca keseluruhan snapshot dan meningkatkan statusnya pada titik tertentu.

FAQ tentang arsitektur dan karya VKontakte

Membaca posisi saat snapshot dibuat dan ukuran binlog.

FAQ tentang arsitektur dan karya VKontakte

Membaca akhir binlog untuk mendapatkan status saat ini dan terus menulis kejadian selanjutnya. Ini adalah skema sederhana; semua mesin kami bekerja sesuai dengan skema tersebut.

Replikasi data

Hasilnya, replikasi data di kami berbasis pernyataan — kami menulis di binlog bukan perubahan halaman apa pun, tetapi yaitu perubahan permintaan. Sangat mirip dengan apa yang datang melalui jaringan, hanya sedikit dimodifikasi.

Skema yang sama digunakan tidak hanya untuk replikasi, tetapi juga untuk membuat cadangan. Kami memiliki mesin - ahli menulis yang menulis ke binlog. Di tempat lain tempat admin mengaturnya, binlog ini disalin, dan hanya itu - kami memiliki cadangan.

FAQ tentang arsitektur dan karya VKontakte

Jika diperlukan replika bacaanUntuk mengurangi beban pembacaan CPU, mesin pembaca diluncurkan, yang membaca akhir binlog dan menjalankan perintah ini secara lokal.

Keterlambatan di sini sangat kecil, dan dimungkinkan untuk mengetahui seberapa besar ketertinggalan replika di belakang master.

Pembagian data di proksi RPC

Bagaimana cara kerja sharding? Bagaimana proksi memahami pecahan cluster mana yang akan dikirim? Kode tidak mengatakan: “Kirim untuk 15 pecahan!” - tidak, ini dilakukan oleh proxy.

Skema paling sederhana adalah firstint — nomor pertama dalam permintaan.

get(photo100_500) => 100 % N.

Ini adalah contoh protokol teks memcached sederhana, namun, tentu saja, kueri bisa rumit dan terstruktur. Contohnya mengambil angka pertama dalam query dan sisanya bila dibagi dengan ukuran cluster.

Ini berguna ketika kita ingin memiliki lokalitas data dari satu entitas. Katakanlah 100 adalah ID pengguna atau grup, dan kita ingin semua data dari satu entitas berada dalam satu pecahan untuk kueri yang kompleks.

Jika kita tidak peduli bagaimana permintaan tersebar di seluruh cluster, ada pilihan lain - hashing seluruh pecahan.

hash(photo100_500) => 3539886280 % N

Kami juga mendapatkan hash, sisa pembagian, dan nomor pecahan.

Kedua opsi ini hanya berfungsi jika kita siap menghadapi kenyataan bahwa ketika kita menambah ukuran cluster, kita akan membaginya atau menambahnya beberapa kali lipat. Misalnya, kami memiliki 16 pecahan, kami tidak memiliki cukup, kami ingin lebih - kami dapat dengan aman mendapatkan 32 pecahan tanpa waktu henti. Jika kita ingin menambah bukan kelipatannya, akan ada downtime, karena kita tidak akan bisa membagi semuanya secara akurat tanpa kerugian. Opsi ini berguna, namun tidak selalu.

Jika kami perlu menambah atau menghapus sejumlah server, kami menggunakan Hashing yang konsisten pada ring ala Ketama. Namun pada saat yang sama, kita benar-benar kehilangan lokalitas data; kita harus menggabungkan permintaan ke cluster sehingga masing-masing bagian mengembalikan respons kecilnya sendiri, dan kemudian menggabungkan respons ke proxy.

Ada permintaan yang sangat spesifik. Tampilannya seperti ini: Proksi RPC menerima permintaan, menentukan cluster mana yang akan dituju, dan menentukan shard. Lalu ada master penulisan, atau, jika klaster memiliki dukungan replika, klaster akan mengirimkan ke replika sesuai permintaan. Ini semua dilakukan oleh proxy.

FAQ tentang arsitektur dan karya VKontakte

Log

Kami menulis log dengan beberapa cara. Yang paling jelas dan sederhana adalah menulis log ke memcache.

ring-buffer: prefix.idx = line

Ada awalan kunci - nama log, garis, dan ada ukuran log ini - jumlah baris. Kita ambil angka acak dari 0 hingga jumlah baris dikurangi 1. Kunci dalam memcache adalah awalan yang digabungkan dengan angka acak ini. Kami menyimpan baris log dan waktu saat ini ke nilainya.

Jika perlu membaca log, kami melaksanakannya Multi Dapatkan semua kunci, diurutkan berdasarkan waktu, dan dengan demikian mendapatkan log produksi secara real time. Skema ini digunakan ketika Anda perlu men-debug sesuatu dalam produksi secara real time, tanpa merusak apa pun, tanpa menghentikan atau mengizinkan lalu lintas ke mesin lain, namun log ini tidak bertahan lama.

Untuk penyimpanan log yang andal, kami memiliki mesin mesin log. Inilah sebabnya mengapa ini dibuat dan digunakan secara luas di sejumlah besar cluster. Cluster terbesar yang saya tahu menyimpan 600 TB log yang dikemas.

Mesinnya sudah sangat tua, ada cluster yang sudah berumur 6-7 tahun. Ada masalah yang kami coba selesaikan, misalnya, kami mulai aktif menggunakan ClickHouse untuk menyimpan log.

Mengumpulkan log di ClickHouse

Diagram ini menunjukkan bagaimana kita memasuki mesin kita.

FAQ tentang arsitektur dan karya VKontakte

Ada kode yang masuk secara lokal melalui RPC ke proxy RPC, dan kode tersebut memahami ke mana harus pergi ke mesin. Jika kita ingin menulis log di ClickHouse, kita perlu mengubah dua bagian dalam skema ini:

  • ganti beberapa mesin dengan ClickHouse;
  • ganti proxy RPC yang tidak bisa mengakses ClickHouse, dengan beberapa solusi yang bisa, dan melalui RPC.

Mesinnya sederhana - kami menggantinya dengan server atau sekelompok server dengan ClickHouse.

Dan untuk pergi ke ClickHouse, kami melakukannya Rumah Kucing. Jika kita langsung dari KittenHouse ke ClickHouse, itu tidak akan berhasil. Bahkan tanpa permintaan, itu bertambah dari koneksi HTTP dari sejumlah besar mesin. Agar skema berfungsi, di server dengan ClickHouse proksi terbalik lokal dimunculkan, yang ditulis sedemikian rupa sehingga dapat menahan volume koneksi yang diperlukan. Ia juga dapat melakukan buffering data di dalamnya dengan relatif andal.

FAQ tentang arsitektur dan karya VKontakte

Terkadang kita tidak ingin mengimplementasikan skema RPC dalam solusi non-standar, misalnya di nginx. Oleh karena itu, KittenHouse memiliki kemampuan untuk menerima log melalui UDP.

FAQ tentang arsitektur dan karya VKontakte

Jika pengirim dan penerima log bekerja pada mesin yang sama, maka kemungkinan kehilangan paket UDP dalam host lokal cukup rendah. Sebagai kompromi antara kebutuhan untuk mengimplementasikan RPC dalam solusi pihak ketiga dan keandalan, kami cukup menggunakan pengiriman UDP. Kami akan kembali ke skema ini nanti.

Pemantauan

Kami memiliki dua jenis log: log yang dikumpulkan oleh administrator di server mereka dan log yang ditulis oleh pengembang dari kode. Mereka sesuai dengan dua jenis metrik: sistem dan produk.

Metrik sistem

Ini berfungsi di semua server kami data bersih, yang mengumpulkan statistik dan mengirimkannya ke Karbon Grafit. Oleh karena itu, ClickHouse digunakan sebagai sistem penyimpanan, dan bukan Whisper, misalnya. Jika perlu, Anda bisa langsung membaca dari ClickHouse, atau menggunakan grafana untuk metrik, grafik, dan laporan. Sebagai developer, kami mempunyai akses yang cukup terhadap Netdata dan Grafana.

Metrik produk

Untuk kenyamanan, kami telah menulis banyak hal. Misalnya, ada sekumpulan fungsi biasa yang memungkinkan Anda menulis nilai Hitungan, Jumlah Unik ke dalam statistik, yang dikirim ke suatu tempat lebih jauh.

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

$stats = statlogsStatData($params)

Selanjutnya, kita dapat menggunakan filter pengurutan dan pengelompokan dan melakukan semua yang kita inginkan dari statistik - membuat grafik, mengonfigurasi Watchdogs.

Kami menulis sangat banyak metrik jumlah kejadiannya dari 600 miliar hingga 1 triliun per hari. Namun, kami ingin mempertahankannya setidaknya beberapa tahununtuk memahami tren dalam metrik. Menyatukan semuanya adalah masalah besar yang belum kita selesaikan. Saya akan memberi tahu Anda cara kerjanya selama beberapa tahun terakhir.

Kami memiliki fungsi yang menulis metrik ini ke memcache lokaluntuk mengurangi jumlah entri. Sekali dalam waktu singkat diluncurkan secara lokal statistik-daemon mengumpulkan semua catatan. Selanjutnya, iblis menggabungkan metrik menjadi dua lapisan server pengumpul kayu, yang mengumpulkan statistik dari sejumlah mesin kami sehingga lapisan di belakangnya tidak mati.

FAQ tentang arsitektur dan karya VKontakte

Jika perlu, kita bisa menulis langsung ke pengumpul kayu.

FAQ tentang arsitektur dan karya VKontakte

Namun menulis dari kode langsung ke kolektor, melewati stas-daemom, adalah solusi yang skalabelnya buruk karena menambah beban pada kolektor. Solusinya hanya cocok jika karena alasan tertentu kami tidak dapat menaikkan daemon statistik memcache di mesin, atau rusak dan kami langsung melanjutkan.

Selanjutnya, pengumpul log menggabungkan statistik menjadi meowDB - ini adalah database kami, yang juga dapat menyimpan metrik.

FAQ tentang arsitektur dan karya VKontakte

Kemudian kita dapat membuat pilihan biner “near-SQL” dari kode tersebut.

FAQ tentang arsitektur dan karya VKontakte

Percobaan

Pada musim panas 2018, kami mengadakan hackathon internal, dan muncul ide untuk mencoba mengganti bagian merah diagram dengan sesuatu yang dapat menyimpan metrik di ClickHouse. Kami memiliki log di ClickHouse - mengapa tidak mencobanya?

FAQ tentang arsitektur dan karya VKontakte

Kami memiliki skema yang menulis log melalui KittenHouse.

FAQ tentang arsitektur dan karya VKontakte

Kami memutuskan tambahkan “*Rumah” lainnya ke diagram, yang akan menerima metrik dalam format persis seperti yang ditulis kode kita melalui UDP. Kemudian *House ini mengubahnya menjadi sisipan, seperti log, yang dipahami KittenHouse. Dia dapat dengan sempurna mengirimkan log ini ke ClickHouse, yang seharusnya dapat membacanya.

FAQ tentang arsitektur dan karya VKontakte

Skema dengan database memcache, stats-daemon dan logs-collectors diganti dengan yang ini.

FAQ tentang arsitektur dan karya VKontakte

Skema dengan database memcache, stats-daemon dan logs-collectors diganti dengan yang ini.

  • Ada pengiriman dari kode di sini, yang ditulis secara lokal di StatsHouse.
  • StatsHouse menulis metrik UDP, yang telah diubah menjadi sisipan SQL, ke KittenHouse secara batch.
  • KittenHouse mengirimkannya ke ClickHouse.
  • Jika kita ingin membacanya, maka kita membacanya melewati StatsHouse - langsung dari ClickHouse menggunakan SQL biasa.

Apakah masih? percobaan, tapi kami menyukai hasilnya. Jika kami memperbaiki masalah pada skema ini, mungkin kami akan beralih ke skema tersebut sepenuhnya. Secara pribadi, saya berharap demikian.

Skema itu tidak menghemat zat besi. Lebih sedikit server yang diperlukan, daemon statistik lokal dan pengumpul log tidak diperlukan, tetapi ClickHouse memerlukan server yang lebih besar daripada yang ada dalam skema saat ini. Dibutuhkan lebih sedikit server, namun harus lebih mahal dan lebih kuat.

Menyebarkan

Pertama, mari kita lihat penerapan PHP. Kami sedang berkembang di git: menggunakan GitLab и TeamCity untuk penempatan. Cabang pengembangan digabungkan menjadi cabang master, dari master untuk pengujian digabungkan menjadi staging, dan dari staging menjadi produksi.

Sebelum penerapan, cabang produksi saat ini dan yang sebelumnya diambil, dan file-file berbeda dipertimbangkan di dalamnya - perubahan: dibuat, dihapus, diubah. Perubahan ini dicatat dalam binlog mesin copyfast khusus, yang dapat dengan cepat mereplikasi perubahan ke seluruh armada server kami. Yang digunakan di sini bukan menyalin secara langsung, melainkan replikasi gosip, ketika satu server mengirimkan perubahan ke tetangga terdekatnya, ke tetangganya, dan seterusnya. Hal ini memungkinkan Anda memperbarui kode dalam waktu puluhan dan satuan detik di seluruh armada. Saat perubahan mencapai replika lokal, patch ini akan diterapkan pada replika lokalnya sistem file lokal. Rollback juga dilakukan sesuai dengan skema yang sama.

Kami juga banyak menerapkan kPHP dan juga memiliki pengembangannya sendiri git sesuai dengan diagram di atas. Sejak ini Biner server HTTP, maka kita tidak dapat menghasilkan diff - biner rilis berbobot ratusan MB. Oleh karena itu, ada opsi lain di sini - versinya ditulis binlog copyfast. Dengan setiap build, itu meningkat, dan selama rollback, itu juga meningkat. Versi: kapan direplikasi ke server. Peniru lokal melihat bahwa versi baru telah memasuki binlog, dan dengan replikasi gosip yang sama mereka mengambil sendiri versi biner terbaru, tanpa melelahkan server master kami, tetapi dengan hati-hati menyebarkan beban ke seluruh jaringan. Berikut ini peluncuran kembali dengan anggun untuk versi baru.

Untuk mesin kami, yang pada dasarnya juga biner, skemanya sangat mirip:

  • cabang master git;
  • biner di deb;
  • versinya ditulis ke binlog copyfast;
  • direplikasi ke server;
  • server mengeluarkan .dep baru;
  • dpkg -i;
  • peluncuran kembali dengan anggun ke versi baru.

Bedanya, biner kita dikemas dalam arsip deb, dan saat memompa keluar mereka dpkg -i ditempatkan pada sistem. Mengapa kPHP diterapkan sebagai biner, dan mesin diterapkan sebagai dpkg? Itu terjadi seperti itu. Berhasil - jangan sentuh.

Tautan yang bermanfaat:

Alexei Akulovich adalah salah satu dari mereka yang, sebagai bagian dari Komite Program, membantu PHP Rusia pada tanggal 17 Mei akan menjadi acara terbesar bagi pengembang PHP belakangan ini. Lihat betapa kerennya PC yang kita miliki, apa speaker (dua di antaranya sedang mengembangkan inti PHP!) - sepertinya sesuatu yang tidak boleh Anda lewatkan jika Anda menulis PHP.

Sumber: www.habr.com

Tambah komentar