Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Artem Denisov ( bo0rsh201, Badoo)

Badoo ialah tapak janji temu terbesar di dunia. Kami kini mempunyai kira-kira 330 juta pengguna berdaftar di seluruh dunia. Tetapi apa yang lebih penting dalam konteks perbualan kami hari ini ialah kami menyimpan kira-kira 3 petabait foto pengguna. Setiap hari pengguna kami memuat naik kira-kira 3,5 juta foto baharu, dan beban bacaan adalah lebih kurang 80 ribu permintaan sesaat. Ini agak banyak untuk bahagian belakang kami, dan kadangkala terdapat kesukaran dengan ini.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Saya akan bercakap tentang reka bentuk sistem ini, yang menyimpan dan menghantar foto secara umum, dan saya akan melihatnya dari sudut pandangan pembangun. Akan ada retrospektif ringkas tentang cara ia berkembang, di mana saya akan menggariskan pencapaian utama, tetapi saya hanya akan bercakap dengan lebih terperinci tentang penyelesaian yang sedang kami gunakan.

Sekarang mari kita mulakan.


Seperti yang saya katakan, ini akan menjadi retrospektif, dan untuk memulakannya di suatu tempat, mari kita ambil contoh yang paling biasa.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami mempunyai tugas yang sama, kami perlu menerima, menyimpan dan menghantar foto pengguna. Dalam bentuk ini, tugas adalah umum, kita boleh menggunakan apa sahaja:

  • penyimpanan awan moden,
  • penyelesaian berkotak-kotak, yang mana terdapat juga banyak sekarang;
  • Kami boleh menyediakan beberapa mesin di pusat data kami dan meletakkan cakera keras yang besar padanya dan menyimpan foto di sana.

Badoo dari segi sejarah - kedua-dua sekarang dan dahulu (pada masa ia baru di peringkat awal) - tinggal di pelayannya sendiri, di dalam DC kita sendiri. Oleh itu, pilihan ini adalah optimum untuk kami.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami hanya mengambil beberapa mesin, memanggilnya "foto", dan kami mendapat gugusan yang menyimpan foto. Tapi macam ada yang kurang. Agar semua ini berfungsi, entah bagaimana kita perlu menentukan mesin mana yang akan kita simpan foto mana. Dan di sini juga, tidak perlu membuka Amerika.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami menambahkan beberapa medan pada storan kami dengan maklumat tentang pengguna. Ini akan menjadi kunci sharding. Dalam kes kami, kami memanggilnya place_id, dan id tempat ini menghala ke tempat di mana foto pengguna disimpan. Kami membuat peta.

Pada peringkat pertama, ini juga boleh dilakukan secara manual - kami mengatakan bahawa foto pengguna ini dengan tempat sedemikian akan mendarat di pelayan sedemikian. Terima kasih kepada peta ini, kami sentiasa tahu apabila pengguna memuat naik foto, tempat untuk menyimpannya dan kami tahu dari mana hendak memberikannya.

Ini adalah skim yang sangat remeh, tetapi ia mempunyai kelebihan yang agak ketara. Yang pertama ialah ia mudah, seperti yang saya katakan, dan yang kedua ialah dengan pendekatan ini kita boleh dengan mudah menskala secara mendatar dengan hanya menghantar kereta baharu dan menambahkannya pada peta. Anda tidak perlu melakukan apa-apa lagi.

Begitulah keadaan kami untuk beberapa waktu.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini sekitar tahun 2009. Mereka menghantar kereta, menghantar...

Dan pada satu ketika kita mula menyedari bahawa skim ini mempunyai kelemahan tertentu. Apakah keburukan?

Pertama sekali, kapasiti terhad. Kami tidak boleh menjejalkan seberapa banyak pemacu keras pada satu pelayan fizikal seperti yang kami mahu. Dan ini telah menjadi masalah tertentu dari semasa ke semasa dan dengan pertumbuhan set data.

Dan kedua. Ini adalah konfigurasi mesin yang tidak tipikal, kerana mesin sedemikian sukar untuk digunakan semula dalam beberapa kelompok lain; ia agak khusus, i.e. mereka sepatutnya lemah dalam prestasi, tetapi pada masa yang sama dengan cakera keras yang besar.

Ini semua untuk tahun 2009, tetapi, pada dasarnya, keperluan ini masih relevan hari ini. Kami mempunyai retrospektif, jadi pada tahun 2009 semuanya benar-benar buruk dengan ini.

Dan perkara terakhir ialah harga.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Harganya sangat mahal pada masa itu, dan kami perlu mencari beberapa alternatif. Itu. kami perlu menggunakan lebih baik kedua-dua ruang dalam pusat data dan pelayan fizikal di mana semua ini berada. Dan jurutera sistem kami memulakan kajian besar di mana mereka menyemak sekumpulan pilihan yang berbeza. Mereka juga melihat sistem fail berkelompok seperti PolyCeph dan Lustre. Terdapat masalah prestasi dan operasi yang agak sukar. Mereka menolak. Kami cuba memasang keseluruhan set data melalui NFS pada setiap kereta untuk meningkatkannya. Pembacaan juga gagal, kami mencuba penyelesaian yang berbeza daripada vendor yang berbeza.

Dan pada akhirnya, kami memutuskan menggunakan apa yang dipanggil Rangkaian Kawasan Penyimpanan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini adalah SHD besar yang direka khusus untuk menyimpan sejumlah besar data. Ia adalah rak dengan cakera yang dipasang pada mesin output optik akhir. Itu. kami mempunyai beberapa jenis kumpulan mesin, agak kecil, dan SHD ini, yang telus kepada logik penghantaran kami, i.e. untuk nginx kami atau sesiapa sahaja untuk menyampaikan permintaan untuk foto ini.

Keputusan ini mempunyai kelebihan yang jelas. Ini adalah SHD. Ia bertujuan untuk menyimpan foto. Ini berfungsi lebih murah daripada sekadar melengkapkan mesin dengan cakera keras.

Tambah kedua.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini adalah bahawa kapasiti telah menjadi lebih besar, i.e. kami boleh menampung lebih banyak storan dalam jumlah yang lebih kecil.

Tetapi terdapat juga kelemahan yang muncul dengan cepat. Apabila bilangan pengguna dan beban pada sistem ini bertambah, masalah prestasi mula timbul. Dan masalahnya di sini agak jelas - mana-mana SHD yang direka untuk menyimpan banyak foto dalam jumlah yang kecil, sebagai peraturan, mengalami pembacaan intensif. Ini sebenarnya benar untuk sebarang storan awan atau apa-apa lagi. Kini kami tidak mempunyai storan ideal yang boleh berskala tidak terhingga, anda boleh memasukkan apa sahaja ke dalamnya, dan ia akan bertolak ansur dengan bacaan dengan baik. Terutama bacaan santai.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Seperti halnya dengan foto kami, kerana foto diminta secara tidak konsisten, dan ini akan sangat mempengaruhi prestasi mereka.

Malah mengikut angka hari ini, jika kita mendapatkan lebih daripada 500 RPS untuk foto pada mesin yang storan disambungkan, masalah sudah bermula. Dan ia sudah cukup buruk untuk kami, kerana bilangan pengguna semakin meningkat, keadaan akan menjadi lebih teruk. Ini perlu dioptimumkan entah bagaimana.

Untuk mengoptimumkan, kami memutuskan pada masa itu, jelas, untuk melihat profil beban - apa, secara umum, sedang berlaku, apa yang perlu dioptimumkan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan di sini semuanya bermain di tangan kita.

Saya sudah berkata dalam slaid pertama: kami mempunyai 80 ribu permintaan membaca sesaat dengan hanya 3,5 juta muat naik setiap hari. Iaitu, ini adalah perbezaan tiga urutan magnitud. Adalah jelas bahawa membaca perlu dioptimumkan dan secara praktikalnya jelas caranya.

Ada satu lagi titik kecil. Spesifik perkhidmatan adalah sedemikian rupa sehingga seseorang mendaftar, memuat naik foto, kemudian mula aktif melihat orang lain, seperti mereka, dan secara aktif ditunjukkan kepada orang lain. Kemudian dia mencari pasangan atau tidak menjumpai pasangan, ia bergantung bagaimana ia ternyata, dan berhenti menggunakan perkhidmatan itu untuk seketika. Pada masa ini, apabila dia menggunakannya, fotonya sangat panas - mereka mendapat permintaan, ramai orang melihatnya. Sebaik sahaja dia berhenti melakukan ini, dengan cepat dia melepaskan pendedahan kepada orang lain seperti yang dia lakukan sebelum ini, dan fotonya hampir tidak pernah diminta.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Itu. Kami mempunyai set data panas yang sangat kecil. Tetapi pada masa yang sama terdapat banyak permintaan untuknya. Dan penyelesaian yang benar-benar jelas di sini ialah menambah cache.

Cache dengan LRU akan menyelesaikan semua masalah kita. Apa yang kita buat?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami menambah satu lagi yang agak kecil di hadapan kelompok besar kami dengan storan, yang dipanggil photocaches. Ini pada asasnya hanyalah proksi caching.

Bagaimana ia berfungsi dari dalam? Inilah pengguna kami, inilah storan. Semuanya sama seperti dahulu. Apa yang kita tambah di antaranya?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ia hanya mesin dengan cakera tempatan fizikal, yang pantas. Ini adalah dengan SSD, sebagai contoh. Dan beberapa jenis cache tempatan disimpan pada cakera ini.

Bagaimana rupanya? Pengguna menghantar permintaan untuk foto. NGINX mencarinya dahulu dalam cache tempatan. Jika tidak, maka hanya proxy_pass ke storan kami, muat turun foto dari sana dan berikan kepada pengguna.

Tetapi yang ini sangat cetek dan tidak jelas apa yang berlaku di dalamnya. Ia berfungsi seperti ini.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Cache secara logik dibahagikan kepada tiga lapisan. Apabila saya menyebut "tiga lapisan", ini tidak bermakna terdapat beberapa jenis sistem yang kompleks. Tidak, ini hanya bersyarat tiga direktori dalam sistem fail:

  1. Ini ialah penimbal di mana foto yang baru dimuat turun daripada proksi pergi.
  2. Ini ialah cache panas yang menyimpan foto yang diminta secara aktif pada masa ini.
  3. Dan cache sejuk, tempat foto ditolak secara beransur-ansur daripada cache panas apabila lebih sedikit permintaan datang kepada mereka.

Untuk ini berfungsi, kami perlu menguruskan cache ini, kami perlu menyusun semula foto di dalamnya, dsb. Ini juga merupakan proses yang sangat primitif.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Nginx hanya menulis ke RAMDisk access.log untuk setiap permintaan, di mana ia menunjukkan laluan ke foto yang telah disiarkan pada masa ini (laluan relatif, sudah tentu), dan partition mana ia telah disampaikan. Itu. ia mungkin menyebut "foto 1" dan kemudian sama ada penimbal, atau cache panas, atau cache sejuk, atau proksi.

Bergantung pada ini, kita perlu memutuskan apa yang perlu dilakukan dengan foto itu.

Kami mempunyai daemon kecil yang berjalan pada setiap mesin yang sentiasa membaca log ini dan menyimpan statistik mengenai penggunaan gambar tertentu dalam ingatannya.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dia hanya mengumpul di sana, menyimpan kaunter dan secara berkala melakukan perkara berikut. Dia mengalihkan foto yang diminta secara aktif, yang mana terdapat banyak permintaan, ke cache panas, di mana sahaja mereka berada.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Foto yang jarang diminta dan kurang kerap diminta ditolak secara beransur-ansur keluar dari cache panas ke dalam yang sejuk.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan apabila kami kehabisan ruang dalam cache, kami hanya mula memadamkan segala-galanya dari cache sejuk secara sembarangan. Dan dengan cara ini, ini berfungsi dengan baik.

Untuk foto disimpan serta-merta apabila memproksikannya ke penimbal, kami menggunakan arahan proxy_store dan penimbal juga ialah RAMDisk, i.e. untuk pengguna ia berfungsi dengan sangat cepat. Ini melibatkan dalaman pelayan caching itu sendiri.

Soalan selebihnya ialah cara mengedarkan permintaan ke seluruh pelayan ini.

Katakan terdapat sekumpulan dua puluh mesin storan dan tiga pelayan caching (beginilah ia berlaku).

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Entah bagaimana kita perlu menentukan permintaan untuk foto yang mana dan tempat untuk mendapatkannya.

Pilihan yang paling biasa ialah Round Robin. Atau buat secara tidak sengaja?

Ini jelas mempunyai beberapa kelemahan kerana kami akan menggunakan cache dengan sangat tidak cekap dalam keadaan sedemikian. Permintaan akan mendarat pada beberapa mesin rawak: di sini ia telah dicache, tetapi pada yang seterusnya ia tidak lagi di sana. Dan jika semua ini berfungsi, ia akan menjadi sangat buruk. Walaupun dengan sebilangan kecil mesin dalam kelompok.

Entah bagaimana kita perlu menentukan dengan jelas pelayan mana untuk mendapatkan permintaan yang mana.

Terdapat cara yang cetek. Kami mengambil cincang daripada URL atau cincang daripada kunci sharding kami, yang terdapat dalam URL, dan membahagikannya dengan bilangan pelayan. Akan bekerja? akan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Itu. Kami mempunyai permintaan 2%, sebagai contoh, untuk beberapa "example_url" ia akan sentiasa mendarat pada pelayan dengan indeks "XNUMX", dan cache akan sentiasa dilupuskan sebaik mungkin.

Tetapi terdapat masalah dengan penyusunan semula dalam skim sedemikian. Resharding - Maksud saya menukar bilangan pelayan.

Andaikan kluster caching kami tidak lagi dapat mengatasi dan kami memutuskan untuk menambah mesin lain.

Jom tambah.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Sekarang semuanya boleh dibahagikan bukan dengan tiga, tetapi dengan empat. Oleh itu, hampir semua kunci yang pernah kami miliki, hampir semua URL kini hidup di pelayan lain. Keseluruhan cache telah tidak sah untuk seketika. Semua permintaan jatuh pada kelompok storan kami, ia menjadi tidak sihat, kegagalan perkhidmatan dan pengguna tidak berpuas hati. Saya tidak mahu berbuat demikian.

Pilihan ini juga tidak sesuai dengan kami.

Itu. apa yang patut kita buat? Entah bagaimana kita mesti menggunakan cache dengan cekap, menghantar permintaan yang sama pada pelayan yang sama berulang kali, tetapi tahan terhadap pengerasan semula. Dan ada penyelesaian sedemikian, ia tidak begitu rumit. Ia dipanggil pencincangan konsisten.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Apa rupa ini?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami mengambil beberapa fungsi dari kekunci sharding dan menyebarkan semua nilainya pada bulatan. Itu. pada titik 0, nilai minimum dan maksimumnya berkumpul. Seterusnya, kami meletakkan semua pelayan kami pada bulatan yang sama dengan lebih kurang cara ini:

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Setiap pelayan ditakrifkan oleh satu titik, dan sektor yang pergi mengikut arah jam kepadanya, sewajarnya, disampaikan oleh hos ini. Apabila permintaan datang kepada kami, kami segera melihat bahawa, sebagai contoh, permintaan A - ia mempunyai cincang di sana - dan ia disampaikan oleh pelayan 2. Permintaan B - oleh pelayan 3. Dan seterusnya.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Apakah yang berlaku dalam situasi ini semasa pengelasan semula?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami tidak membatalkan keseluruhan cache, seperti sebelum ini, dan tidak mengalihkan semua kunci, tetapi kami mengalihkan setiap sektor dalam jarak yang singkat supaya, secara relatifnya, pelayan keenam kami, yang ingin kami tambahkan, sesuai dengan ruang kosong, dan kami menambahnya di sana.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Sudah tentu, dalam keadaan sedemikian kunci juga bergerak keluar. Tetapi mereka keluar jauh lebih lemah daripada sebelumnya. Dan kami melihat bahawa dua kunci pertama kami kekal pada pelayan mereka, dan pelayan caching berubah hanya untuk kunci terakhir. Ini berfungsi dengan cekap, dan jika anda menambah hos baharu secara berperingkat, maka tiada masalah besar di sini. Anda menambah dan menambah sedikit demi sedikit, tunggu sehingga cache penuh semula dan semuanya berfungsi dengan baik.

Satu-satunya soalan kekal dengan penolakan. Mari kita anggap bahawa beberapa jenis kereta rosak.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan kami tidak akan benar-benar mahu menjana semula peta ini pada masa ini, membatalkan sebahagian daripada cache, dan seterusnya, jika, sebagai contoh, mesin telah but semula, dan kami perlu meminta perkhidmatan. Kami hanya menyimpan satu cache foto sandaran di setiap tapak, yang bertindak sebagai pengganti bagi mana-mana mesin yang sedang tidak berfungsi. Dan jika tiba-tiba salah satu pelayan kami tidak tersedia, trafik akan pergi ke sana. Sememangnya, kami tidak mempunyai sebarang cache di sana, i.e. ia sejuk, tetapi sekurang-kurangnya permintaan pengguna sedang diproses. Jika ini adalah selang yang singkat, maka kita mengalaminya sepenuhnya dengan tenang. Terdapat lebih banyak beban pada storan. Jika selang ini panjang, maka kita sudah boleh membuat keputusan - untuk mengalih keluar pelayan ini daripada peta atau tidak, atau mungkin menggantikannya dengan yang lain.

Ini mengenai sistem caching. Jom tengok hasilnya.

Nampaknya tidak ada yang rumit di sini. Tetapi kaedah menguruskan cache ini memberi kami kadar helah kira-kira 98%. Itu. daripada 80 ribu permintaan sesaat ini, hanya 1600 mencapai storan, dan ini adalah beban biasa, mereka dengan tenang menanggungnya, kami sentiasa mempunyai simpanan.

Kami meletakkan pelayan ini di tiga DC kami, dan menerima tiga tempat kehadiran - Prague, Miami dan Hong Kong.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Itu. mereka lebih kurang terletak secara tempatan untuk setiap pasaran sasaran kami.

Dan sebagai bonus yang bagus, kami mendapat proksi caching ini, di mana CPU sebenarnya melahu, kerana ia tidak begitu diperlukan untuk menyampaikan kandungan. Dan di sana, menggunakan NGINX+ Lua, kami melaksanakan banyak logik utilitarian.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Contohnya, kita boleh bereksperimen dengan webp atau jpeg progresif (ini adalah format moden yang berkesan), lihat cara ia mempengaruhi trafik, membuat beberapa keputusan, membolehkannya untuk negara tertentu, dsb.; buat ubah saiz dinamik atau pangkas foto dengan cepat.

Ini adalah kes penggunaan yang baik apabila, sebagai contoh, anda mempunyai aplikasi mudah alih yang memaparkan foto, dan aplikasi mudah alih tidak mahu membazirkan CPU pelanggan untuk meminta foto besar dan kemudian mengubah saiznya kepada saiz tertentu untuk menolaknya ke dalam. pandangan. Kami hanya boleh menentukan secara dinamik beberapa parameter dalam URL bersyarat UPort dan cache foto akan mengubah saiz foto itu sendiri. Sebagai peraturan, ia akan memilih saiz yang kita ada secara fizikal pada cakera, sedekat mungkin dengan yang diminta, dan menjualnya ke bawah dalam koordinat tertentu.

Ngomong-ngomong, kami telah membuat rakaman video tersedia untuk umum selama lima tahun terakhir persidangan pembangun sistem beban tinggi HighLoad ++. Tonton, pelajari, kongsi dan langgan saluran YouTube.

Kami juga boleh menambah banyak logik produk di sana. Sebagai contoh, kita boleh menambah tera air yang berbeza menggunakan parameter URL, kita boleh mengaburkan, mengaburkan atau mengaburkan foto. Ini adalah apabila kita ingin menunjukkan foto seseorang, tetapi kita tidak mahu menunjukkan wajahnya, ini berfungsi dengan baik, semuanya dilaksanakan di sini.

Apa yang kami dapat? Kami mendapat tiga mata kehadiran, kadar helah yang baik, dan pada masa yang sama kami tidak mempunyai CPU terbiar pada mesin ini. Dia kini telah menjadi, sudah tentu, lebih penting daripada sebelumnya. Kita perlu memberikan diri kita kereta yang lebih kuat, tetapi ia berbaloi.

Ini menyangkut pemulangan gambar. Segala-galanya di sini agak jelas dan jelas. Saya rasa saya tidak menemui Amerika, hampir mana-mana CDN berfungsi dengan cara ini.

Dan, kemungkinan besar, pendengar yang canggih mungkin mempunyai soalan: mengapa tidak menukar semuanya kepada CDN? Ia akan menjadi lebih kurang sama; semua CDN moden boleh melakukan ini. Dan terdapat beberapa sebab.

Yang pertama ialah gambar.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini adalah salah satu perkara utama infrastruktur kami, dan kami memerlukan kawalan sebanyak mungkin ke atasnya. Jika ini adalah sejenis penyelesaian daripada vendor pihak ketiga, dan anda tidak mempunyai sebarang kuasa ke atasnya, agak sukar untuk anda menjalaninya apabila anda mempunyai set data yang besar dan apabila anda mempunyai aliran yang sangat besar daripada permintaan pengguna.

Biar saya berikan satu contoh. Sekarang, dengan infrastruktur kami, kami boleh, sebagai contoh, dalam kes beberapa masalah atau ketukan bawah tanah, pergi ke mesin dan kacau di sana, secara relatifnya. Kita boleh menambah koleksi beberapa metrik yang hanya kita perlukan, kita boleh mencuba entah bagaimana, melihat cara ini mempengaruhi graf dan sebagainya. Kini banyak statistik sedang dikumpulkan pada kluster caching ini. Dan kami melihatnya secara berkala dan menghabiskan masa yang lama meneroka beberapa anomali. Jika ia berada di sebelah CDN, ia akan menjadi lebih sukar untuk dikawal. Atau, sebagai contoh, jika beberapa jenis kemalangan berlaku, kita tahu apa yang berlaku, kita tahu bagaimana untuk menjalaninya dan bagaimana untuk mengatasinya. Ini adalah kesimpulan pertama.

Kesimpulan kedua juga agak bersejarah, kerana sistem ini telah lama dibangunkan, dan terdapat banyak keperluan perniagaan yang berbeza pada peringkat yang berbeza, dan ia tidak selalunya sesuai dengan konsep CDN.

Dan perkara yang berikut dari yang sebelumnya ialah

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini kerana pada cache foto kami mempunyai banyak logik khusus, yang tidak boleh sentiasa ditambah atas permintaan. Tidak mungkin mana-mana CDN akan menambahkan beberapa perkara tersuai kepada anda atas permintaan anda. Contohnya, menyulitkan URL jika anda tidak mahu pelanggan dapat mengubah sesuatu. Adakah anda ingin menukar URL pada pelayan dan menyulitkannya, kemudian hantar beberapa parameter dinamik ke sini.

Apakah kesimpulan yang dicadangkan ini? Dalam kes kami, CDN bukanlah alternatif yang sangat baik.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan dalam kes anda, jika anda mempunyai sebarang keperluan perniagaan khusus, maka anda boleh dengan mudah melaksanakan apa yang saya tunjukkan kepada anda sendiri. Dan ini akan berfungsi dengan sempurna dengan profil beban yang serupa.

Tetapi jika anda mempunyai beberapa jenis penyelesaian umum, dan tugasnya tidak begitu spesifik, anda boleh mengambil CDN dengan selamat. Atau jika masa dan sumber lebih penting kepada anda daripada kawalan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan CDN moden mempunyai hampir semua yang saya beritahu anda sekarang. Dengan pengecualian tambah atau tolak beberapa ciri.

Ini mengenai pemberian foto.

Sekarang mari kita bergerak ke hadapan sedikit dalam retrospektif kita dan bercakap tentang penyimpanan.

2013 telah berlalu.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Pelayan caching telah ditambahkan, masalah prestasi telah hilang. Semuanya baik-baik sahaja. Set data semakin berkembang. Sehingga 2013, kami mempunyai kira-kira 80 pelayan yang disambungkan ke storan, dan kira-kira 40 pelayan cache dalam setiap DC. Ini ialah 560 terabait data pada setiap DC, i.e. kira-kira satu petabyte secara keseluruhan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan dengan pertumbuhan set data, kos operasi mula meningkat dengan ketara. Apakah maksud ini?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dalam rajah ini yang dilukis - dengan SAN, dengan mesin dan cache yang disambungkan kepadanya - terdapat banyak titik kegagalan. Sekiranya kami telah menangani kegagalan pelayan caching sebelum ini, segala-galanya lebih kurang boleh diramal dan difahami, tetapi dari segi penyimpanan semuanya lebih teruk.

Pertama, Rangkaian Kawasan Penyimpanan (SAN) itu sendiri, yang boleh gagal.

Kedua, ia disambungkan melalui optik ke mesin akhir. Mungkin terdapat masalah dengan kad optik dan palam pencucuh.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Sudah tentu, jumlahnya tidak sebanyak SAN itu sendiri, tetapi, bagaimanapun, ini juga merupakan titik kegagalan.

Seterusnya ialah mesin itu sendiri, yang disambungkan ke storan. Ia juga boleh gagal.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Secara keseluruhan, kami mempunyai tiga mata kegagalan.

Selanjutnya, sebagai tambahan kepada titik kegagalan, terdapat penyelenggaraan berat storan itu sendiri.

Ini adalah sistem berbilang komponen yang kompleks, dan jurutera sistem mungkin mengalami kesukaran untuk bekerja dengannya.

Dan yang terakhir, perkara yang paling penting. Jika kegagalan berlaku pada mana-mana tiga titik ini, kami mempunyai peluang bukan sifar untuk kehilangan data pengguna kerana sistem fail mungkin ranap.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Katakan sistem fail kita rosak. Pertama, pemulihannya mengambil masa yang lama - ia boleh mengambil masa seminggu dengan jumlah data yang besar. Dan kedua, pada akhirnya kita kemungkinan besar akan berakhir dengan sekumpulan fail yang tidak dapat difahami yang perlu digabungkan ke dalam foto pengguna. Dan kami berisiko kehilangan data. Risikonya agak tinggi. Dan semakin kerap situasi sedemikian berlaku, dan semakin banyak masalah timbul dalam keseluruhan rantaian ini, semakin tinggi risiko ini.

Sesuatu perlu dilakukan mengenai perkara ini. Dan kami memutuskan bahawa kami hanya perlu membuat sandaran data. Ini sebenarnya penyelesaian yang jelas dan bagus. Apa yang telah kita lakukan?

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Inilah rupa pelayan kami apabila ia disambungkan ke storan sebelum ini. Ini adalah satu bahagian utama, ia hanyalah peranti blok yang sebenarnya mewakili pelekap untuk storan jauh melalui optik.

Kami baru menambah bahagian kedua.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami meletakkan storan kedua di sebelahnya (nasib baik, ia tidak begitu mahal dari segi wang), dan memanggilnya sebagai partition sandaran. Ia juga disambungkan melalui optik dan terletak pada mesin yang sama. Tetapi kita perlu menyegerakkan data antara mereka.

Di sini kita hanya membuat baris gilir tak segerak berdekatan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dia tak sibuk sangat. Kami tahu kami tidak mempunyai rekod yang mencukupi. Baris gilir hanyalah jadual dalam MySQL di mana baris seperti "anda perlu menyandarkan foto ini" ditulis. Dengan sebarang perubahan atau muat naik, kami menyalin daripada partition utama ke sandaran menggunakan tak segerak atau hanya sejenis pekerja latar belakang.

Dan dengan itu kita sentiasa mempunyai dua bahagian yang konsisten. Walaupun satu bahagian sistem ini gagal, kami sentiasa boleh menukar partition utama dengan sandaran, dan semuanya akan terus berfungsi.

Tetapi kerana ini, beban bacaan meningkat dengan banyak, kerana... sebagai tambahan kepada pelanggan yang membaca dari bahagian utama, kerana mereka mula-mula melihat foto di sana (ia lebih baru di sana), dan kemudian mencarinya pada sandaran, jika mereka tidak menemuinya (tetapi NGINX hanya melakukan ini), sistem kami juga sandaran tambah kini dibaca dari partition utama. Bukannya ini halangan, tetapi saya tidak mahu menambah beban, pada asasnya, begitu sahaja.

Dan kami menambah cakera ketiga, iaitu SSD kecil, dan memanggilnya penimbal.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Bagaimana ia berfungsi sekarang.

Pengguna memuat naik foto ke penimbal, kemudian acara dilemparkan ke dalam baris gilir yang menunjukkan bahawa ia perlu disalin ke dalam dua bahagian. Ia disalin, dan foto itu tinggal di penimbal untuk beberapa lama (katakan, sehari), dan hanya kemudian dibersihkan dari sana. Ini sangat meningkatkan pengalaman pengguna, kerana pengguna memuat naik foto, sebagai peraturan, permintaan segera mula diikuti, atau dia sendiri mengemas kini halaman dan menyegarkannya. Tetapi semuanya bergantung kepada aplikasi yang membuat muat naik.

Atau, sebagai contoh, orang lain yang dia mula menunjukkan dirinya dengan serta-merta menghantar permintaan selepas foto ini. Ia belum lagi dalam cache; permintaan pertama berlaku dengan cepat. Pada asasnya sama seperti dari cache foto. Penyimpanan perlahan tidak terlibat sama sekali dalam hal ini. Dan apabila sehari kemudian ia dibersihkan, ia sudah sama ada dicache pada lapisan caching kami, atau, kemungkinan besar, tiada siapa yang memerlukannya lagi. Itu. Pengalaman pengguna di sini telah berkembang dengan baik disebabkan oleh manipulasi mudah tersebut.

Nah, dan yang paling penting: kami berhenti kehilangan data.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Katakan sahaja kita berhenti berpotensi kehilangan data, kerana kami tidak benar-benar kehilangannya. Tetapi ada bahaya. Kami melihat bahawa penyelesaian ini, sudah tentu, baik, tetapi ia sedikit seperti melicinkan gejala masalah, bukannya menyelesaikannya sepenuhnya. Dan beberapa masalah kekal di sini.

Pertama, ini adalah titik kegagalan dalam bentuk hos fizikal itu sendiri di mana semua jentera ini berjalan; ia tidak hilang.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kedua, masih terdapat masalah dengan SAN, penyelenggaraan berat mereka, dll. Bukannya ia adalah faktor kritikal, tetapi saya mahu mencuba untuk hidup tanpanya.

Dan kami membuat versi ketiga (sebenarnya, yang kedua sebenarnya) - versi tempahan. Apakah rupanya?

Inilah yang berlaku -

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Masalah utama kami adalah dengan fakta bahawa ini adalah hos fizikal.

Pertama, kami mengalih keluar SAN kerana kami ingin mencuba, kami mahu mencuba hanya pemacu keras tempatan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini sudah 2014-2015, dan pada masa itu keadaan dengan cakera dan kapasiti mereka dalam satu hos menjadi lebih baik. Kami memutuskan mengapa tidak mencubanya.

Dan kemudian kami hanya mengambil partition sandaran kami dan memindahkannya secara fizikal ke mesin yang berasingan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Oleh itu, kita mendapat rajah ini. Kami mempunyai dua kereta yang menyimpan set data yang sama. Mereka menyandarkan satu sama lain sepenuhnya dan menyegerakkan data melalui rangkaian melalui baris gilir tak segerak dalam MySQL yang sama.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Mengapa ini berfungsi dengan baik adalah kerana kami mempunyai sedikit rekod. Itu. jika menulis adalah setanding dengan membaca, mungkin kita akan mempunyai beberapa jenis overhed rangkaian dan masalah. Terdapat sedikit penulisan, banyak bacaan - kaedah ini berfungsi dengan baik, i.e. Kami jarang menyalin foto antara dua pelayan ini.

Bagaimana ini berfungsi, jika anda melihat sedikit lebih terperinci.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Muat naik. Pengimbang hanya memilih hos rawak dengan pasangan dan memuat naik kepadanya. Pada masa yang sama, dia secara semula jadi melakukan pemeriksaan kesihatan dan memastikan kereta itu tidak jatuh. Itu. dia memuat naik foto hanya ke pelayan langsung, dan kemudian melalui baris gilir tak segerak semuanya disalin kepada jirannya. Dengan muat naik semuanya sangat mudah.

Tugasnya lebih sukar sedikit.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Lua membantu kami di sini, kerana sukar untuk membuat logik sedemikian pada vanila NGINX. Kami mula-mula membuat permintaan kepada pelayan pertama, lihat jika foto itu ada, kerana ia berpotensi boleh dimuat naik, sebagai contoh, kepada jiran, tetapi belum tiba di sini. Jika gambar itu ada, itu bagus. Kami segera memberikannya kepada pelanggan dan, mungkin, menyimpannya di cache.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Jika tiada, kami hanya membuat permintaan kepada jiran kami dan dijamin akan menerimanya dari sana.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Itu. sekali lagi kita boleh katakan: mungkin terdapat masalah dengan prestasi, kerana terdapat perjalanan pergi dan balik yang berterusan - foto itu telah dimuat naik, ia tidak ada di sini, kami membuat dua permintaan dan bukannya satu, ini harus berfungsi dengan perlahan.

Dalam keadaan kita, ini tidak berfungsi dengan perlahan.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kami mengumpulkan banyak metrik pada sistem ini, dan kadar pintar bersyarat bagi mekanisme sedemikian ialah kira-kira 95%. Itu. Kelewatan sandaran ini adalah kecil, dan disebabkan ini kami hampir dijamin, selepas gambar dimuat naik, kami akan mengambilnya buat kali pertama dan tidak perlu pergi ke mana-mana dua kali.

Jadi apa lagi yang kita ada yang sangat hebat?

Sebelum ini, kami mempunyai partition sandaran utama, dan kami membaca daripadanya secara berurutan. Itu. Kami sentiasa mencari pada yang utama dahulu, dan kemudian pada sandaran. Ia adalah satu langkah.

Kini kami menggunakan bacaan daripada dua mesin sekaligus. Kami mengedarkan permintaan menggunakan Round Robin. Dalam peratusan kecil kes kami membuat dua permintaan. Tetapi secara keseluruhan, kami kini mempunyai stok bacaan dua kali lebih banyak berbanding sebelum ini. Dan beban telah dikurangkan dengan banyak pada mesin penghantar dan secara langsung pada mesin penyimpanan, yang juga kami miliki pada masa itu.

Bagi toleransi kesalahan. Sebenarnya, inilah yang kami perjuangkan. Dengan toleransi kesalahan, semuanya ternyata hebat di sini.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Satu kereta rosak.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Tiada masalah! Seorang jurutera sistem mungkin tidak bangun pada waktu malam, dia akan menunggu sehingga pagi, tiada perkara buruk akan berlaku.

Jika walaupun mesin ini gagal, baris gilir tidak teratur, tidak ada masalah sama ada, log hanya akan terkumpul dahulu pada mesin hidup, dan kemudian ia akan ditambah ke baris gilir, dan kemudian ke kereta yang akan mula beroperasi selepas beberapa lama.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Perkara yang sama dengan penyelenggaraan. Kami hanya mematikan salah satu mesin, mengeluarkannya secara manual daripada semua kolam, ia berhenti menerima trafik, kami melakukan beberapa jenis penyelenggaraan, kami mengedit sesuatu, kemudian kami mengembalikannya kepada perkhidmatan, dan sandaran ini dapat dicapai dengan cepat. Itu. setiap hari, masa henti bagi sebuah kereta berlaku dalam masa beberapa minit. Ini sebenarnya sangat sedikit. Dengan toleransi kesalahan, saya katakan sekali lagi, semuanya sejuk di sini.

Apakah kesimpulan yang boleh dibuat daripada skim redundansi ini?

Kami mendapat toleransi kesalahan.

Mudah untuk digunakan. Memandangkan mesin mempunyai pemacu keras tempatan, ini adalah lebih mudah dari sudut pandangan operasi untuk jurutera yang bekerja dengannya.

Kami menerima elaun bacaan berganda.

Ini adalah bonus yang sangat baik sebagai tambahan kepada toleransi kesalahan.

Tetapi terdapat juga masalah. Kini kami mempunyai pembangunan yang lebih kompleks bagi beberapa ciri yang berkaitan dengan ini, kerana sistem telah menjadi 100% akhirnya konsisten.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Kita mesti, katakan, dalam beberapa kerja latar belakang, sentiasa berfikir: "Pelayan apa yang sedang kita jalankan sekarang?", "Adakah benar-benar ada foto semasa di sini?" dan lain-lain. Ini, sudah tentu, semuanya dibungkus, dan bagi pengaturcara yang menulis logik perniagaan, ia adalah telus. Tetapi, bagaimanapun, lapisan kompleks yang besar ini telah muncul. Tetapi kami bersedia untuk bersabar dengan ini sebagai pertukaran untuk kebaikan yang kami terima daripadanya.

Dan di sini sekali lagi beberapa konflik timbul.

Saya berkata pada mulanya bahawa menyimpan segala-galanya pada pemacu keras tempatan adalah buruk. Dan sekarang saya katakan bahawa kami menyukainya.

Ya, sememangnya, dari masa ke masa keadaan telah banyak berubah, dan kini pendekatan ini mempunyai banyak kelebihan. Pertama, kami mendapat operasi yang lebih mudah.

Kedua, ia lebih produktif, kerana kami tidak mempunyai pengawal automatik ini atau sambungan ke rak cakera.

Terdapat sejumlah besar jentera di sana, dan ini hanyalah beberapa cakera yang dipasang di sini pada mesin untuk serbuan.

Tetapi ada juga kekurangannya.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Ini adalah kira-kira 1,5 kali lebih mahal daripada menggunakan SAN walaupun pada harga hari ini. Oleh itu, kami memutuskan untuk tidak berani menukar keseluruhan kluster besar kami kepada kereta dengan pemacu keras tempatan dan memutuskan untuk meninggalkan penyelesaian hibrid.

Separuh daripada mesin kami berfungsi dengan cakera keras (baik, bukan separuh - mungkin 30 peratus). Dan selebihnya adalah kereta lama yang pernah mempunyai skim tempahan pertama. Kami hanya memasang semula mereka, kerana kami tidak memerlukan data baharu atau apa-apa lagi, kami hanya mengalihkan pelekap daripada satu hos fizikal kepada dua.

Dan kami kini mempunyai stok bacaan yang besar, dan kami mengembangkannya. Jika sebelum ini kami memasang satu storan pada satu mesin, kini kami memasang empat, sebagai contoh, pada satu pasangan. Dan ia berfungsi dengan baik.

Mari kita ambil ringkasan ringkas tentang apa yang kita capai, apa yang kita perjuangkan, dan sama ada kita berjaya.

Keputusan

Kami mempunyai pengguna - seramai 33 juta.

Kami mempunyai tiga mata kehadiran - Prague, Miami, Hong Kong.

Ia mengandungi lapisan caching, yang terdiri daripada kereta dengan cakera tempatan pantas (SSD), di mana jentera mudah daripada NGINX, access.log dan daemon Python dijalankan, yang memproses semua ini dan menguruskan cache.

Jika anda mahu, anda berada dalam projek anda, jika foto tidak begitu kritikal untuk anda seperti untuk kami, atau jika kawalan tukar ganti berbanding kelajuan pembangunan dan kos sumber berada dalam arah lain untuk anda, maka anda boleh menggantikannya dengan selamat dengan CDN, CDN moden berfungsi dengan baik.

Seterusnya ialah lapisan storan, di mana kami mempunyai kelompok pasangan mesin yang menyokong satu sama lain, fail disalin secara tidak segerak dari satu ke satu sama lain apabila ia berubah.

Selain itu, beberapa mesin ini berfungsi dengan pemacu keras tempatan.

Beberapa mesin ini disambungkan kepada SAN.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Dan, di satu pihak, ia lebih mudah digunakan dan sedikit lebih produktif, sebaliknya, ia mudah dari segi ketumpatan peletakan dan harga setiap gigabait.

Ini adalah gambaran ringkas tentang seni bina yang kami perolehi dan bagaimana ia berkembang.

Beberapa lagi petua dari kapten, yang sangat mudah.

Pertama, jika anda tiba-tiba memutuskan bahawa anda perlu menambah baik segala-galanya dalam infrastruktur foto anda dengan segera, ukur dahulu, kerana mungkin tiada apa yang perlu diperbaiki.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Biar saya berikan satu contoh. Kami mempunyai sekumpulan mesin yang menghantar foto daripada lampiran dalam sembang, dan skim itu telah berfungsi di sana sejak 2009, dan tiada siapa yang mengalaminya. Semua orang baik-baik saja, semua orang suka semuanya.

Untuk mengukur, mula-mula gantung sekumpulan metrik, lihatnya, dan kemudian tentukan perkara yang anda tidak berpuas hati dan perkara yang perlu diperbaiki. Untuk mengukur ini, kami mempunyai alat hebat yang dipanggil Pinba.

Ia membolehkan anda mengumpul statistik yang sangat terperinci daripada NGINX untuk setiap permintaan dan kod tindak balas, dan pengedaran masa - apa sahaja yang anda mahukan. Ia mempunyai pengikatan kepada semua jenis sistem analitik yang berbeza, dan kemudian anda boleh melihat semuanya dengan cantik.

Mula-mula kami mengukurnya, kemudian kami memperbaikinya.

Selanjutnya. Kami mengoptimumkan membaca dengan cache, menulis dengan sharding, tetapi ini adalah perkara yang jelas.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Selanjutnya. Jika anda baru mula membina sistem anda, maka adalah lebih baik untuk menjadikan foto sebagai fail tidak boleh diubah. Kerana anda serta-merta kehilangan seluruh kelas masalah dengan ketidaksahihan cache, dengan cara logik harus mencari versi foto yang betul, dan sebagainya.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Katakan anda memuat naik seratus, kemudian memutarkannya, menjadikannya fail yang berbeza secara fizikal. Itu. tidak perlu berfikir: sekarang saya akan menjimatkan sedikit ruang, tulis ke fail yang sama, tukar versi. Ini sentiasa tidak berfungsi dengan baik dan menyebabkan banyak sakit kepala kemudian.

Perkara seterusnya. Mengenai ubah saiz dengan cepat.

Sebelum ini, apabila pengguna memuat naik foto, kami serta-merta memotong sejumlah besar saiz untuk semua keadaan, untuk pelanggan yang berbeza, dan mereka semua berada pada cakera. Sekarang kita telah meninggalkan ini.

Kami meninggalkan hanya tiga saiz utama: kecil, sederhana dan besar. Kami hanya mengecilkan segala-galanya daripada saiz yang berada di belakang saiz yang diminta kepada kami di Uport, kami hanya melakukan skala bawah dan memberikannya kepada pengguna.

CPU lapisan caching di sini ternyata jauh lebih murah berbanding jika kita sentiasa menjana semula saiz ini pada setiap storan. Katakan kita mahu menambah yang baharu, ini akan mengambil masa sebulan - jalankan skrip di mana-mana sahaja yang akan melakukan semua ini dengan kemas, tanpa memusnahkan kluster. Itu. Jika anda mempunyai peluang untuk memilih sekarang, lebih baik membuat saiz fizikal sesedikit mungkin, tetapi supaya sekurang-kurangnya beberapa pengedaran adalah, katakan, tiga. Dan segala-galanya boleh diubah saiznya dengan cepat menggunakan modul siap pakai. Semuanya sangat mudah dan boleh diakses sekarang.

Dan sandaran tak segerak tambahan adalah bagus.

Seperti yang ditunjukkan oleh amalan kami, skema ini berfungsi dengan baik dengan penyalinan tertunda bagi fail yang diubah.

Seni bina untuk menyimpan dan berkongsi foto dalam Badoo

Perkara terakhir juga jelas. Jika infrastruktur anda tidak mempunyai masalah seperti itu sekarang, tetapi ada sesuatu yang boleh pecah, ia pasti akan pecah apabila ia menjadi lebih sedikit. Oleh itu, adalah lebih baik untuk memikirkan perkara ini terlebih dahulu dan tidak mengalami masalah dengannya. Itu sahaja yang saya ingin katakan.

kenalan

Β» bo0rsh201
Β» Blog Badoo

Laporan ini adalah transkrip salah satu ucapan terbaik pada persidangan pembangun sistem beban tinggi HighLoad ++. Kurang daripada sebulan lagi untuk persidangan HighLoad++ 2017.

Kami sudah sediakan Program persidangan, jadual itu kini sedang giat dibentuk.

Tahun ini kami terus meneroka topik seni bina dan penskalaan:

Kami juga menggunakan beberapa bahan ini dalam kursus latihan dalam talian kami untuk membangunkan sistem beban tinggi HighLoad.Panduan ialah rangkaian surat, artikel, bahan, video yang dipilih khas. Buku teks kami sudah mengandungi lebih daripada 30 bahan unik. Sambung!

Sumber: www.habr.com

Tambah komen