Demonstrasi desain sistem

Hadirin

Artikel ini adalah seri berikutnya tentang bagaimana saya mendesain aplikasi populer. Direkomendasikan (walaupun tidak sepenuhnya diperlukan) untuk membaca postingan sebelumnya yang telah saya susun dengan berguna dalam daftar di sini.

Kami mengharapkan pemahaman dasar tentang prinsip-prinsip arsitektur dan AWS, namun mudah-mudahan, postingan ini dapat didekati oleh sebagian besar insinyur.

Argumen

Pertama, mari kita lihat pernyataan masalah kita.

Sistem untuk Didesain

Kami berharap dapat merancang platform video on demand seperti YouTube atau Netflix. Sepertinya Anda belum pernah menemukannya sebelumnya, namun jika Anda belum pernah menemukannya, premisnya adalah bahwa pengguna dapat mengunggah atau melihat video secara online. Persyaratan pastinya adalah:

  1. Kita harus bisa mengunggah video.
  2. Kita harus dapat melihat video.
  3. Kita harus bisa melakukan pencarian berdasarkan judul video.

Kami akan mengabaikan fakta bahwa Anda tidak dapat mengupload video ke Netflix kecuali Anda adalah studio produksi. Bayangkan Anda adalah Quentin Tarantino.

Pendekatan

Kami memiliki pendekatan standar terhadap desain sistem yang dijelaskan lebih menyeluruh dalam artikel di sini. Namun, langkah-langkahnya dirangkum di bawah ini:

  1. Klarifikasi persyaratan: Memastikan kami memiliki semua informasi sebelum memulai. Ini mungkin termasuk berapa banyak permintaan atau pengguna yang kami harapkan.
  2. Estimasi di balik sampul:Melakukan beberapa perhitungan cepat untuk mengukur kinerja sistem yang diperlukan. Misalnya berapa besar penyimpanan atau bandwidth yang kita perlukan?
  3. Desain antarmuka sistem:Seperti apa sistem kita dari luar, bagaimana orang berinteraksi dengannya? Umumnya ini adalah kontrak API.
  4. Desain model data:Seperti apa tampilan data kita saat kita menyimpannya. Pada titik ini kita mungkin memikirkan model relasional vs non-relasional.
  5. Desain logis:Memasangkannya dalam sistem yang kasar! Pada titik ini saya berpikir pada tingkat 'bagaimana saya menjelaskan ide saya kepada seseorang yang tidak tahu apa-apa tentang teknologi?'
  6. Desain fisik:Sekarang kami mulai mengkhawatirkan server, bahasa pemrograman, dan detail implementasi. Kita dapat menempatkannya di atas desain logis.
  7. Identifikasi dan atasi hambatan:Pada tahap ini kita akan memiliki sistem yang berfungsi! Kami sekarang menyempurnakan desainnya.

Karena itu, mari kita mulai!

Klarifikasi Persyaratan

Mari pikirkan beberapa pertanyaan awal. Awalnya, saya bertanya-tanya berapa ukuran maksimum dan rata-rata sebuah video. Saya kemudian memikirkan tentang jumlah pengguna dan rasio baca/tulis/pencarian mereka.

Selain itu, perangkat/kecepatan koneksi apa yang digunakan klien? Kami mungkin perlu mengoptimalkan format dan ukuran file berdasarkan ini.

Bagian Belakang Estimasi Amplop

Anggaplah kita memiliki 100 juta pengguna (secara realistis mungkin lebih banyak lagi!), yang membaca dan menulis dengan rasio harian 100:1. Ukuran file rata-rata adalah 1GB untuk HD, dan ukuran file maksimum adalah 50GB (tidak peduli seberapa akuratnya).

Ini berarti kami akan memiliki kapasitas kasar 100,000,000 * 1GB = 100PB/d hanya untuk mengunggah! Kami dapat menyelesaikannya dalam permintaan per detik berdasarkan durasi video rata-rata, namun dapat dikatakan bahwa kami berurusan dengan banyak data!

Ini juga merupakan sistem global, sehingga kita dapat berasumsi bahwa kita akan menggunakan banyak perangkat yang berbeda, mulai dari perangkat seluler hingga perangkat tertanam, pada berbagai jaringan, sehingga kita perlu merancang secara defensif untuk hal ini.

Desain Antarmuka Sistem

Setelah mempelajari lebih banyak tentang sistem kami, kami dapat memutuskan bagaimana kami ingin berinteraksi dengannya. Ada tiga poin utama interaksi.

  1. Mengunggah video
  2. Unduh video
  3. Mencari

Mengunggah video akan dilakukan melalui HTTP. Satu hal yang perlu kita waspadai adalah ukuran file. Dalam skenario yang berbeda kita mungkin menggunakan FTP, namun mengingat kita memiliki pengguna acak di internet, hal ini tidak masuk akal di sini.

Apa yang saya sarankan lakukan adalah membuat formulir awal untuk mengirimkan metadata yang berkaitan dengan file (judul, tag, apa pun yang bukan video itu sendiri) yang akan mengembalikan GGUID yang terkait dengan file tersebut.

Dari sana kita dapat menggunakan API “File” dan “Blob” JavaScript untuk “memotong file video dan mengirim sebagian permintaan” dengan header “Rentang Konten” yang mewakili rentang byte yang diwakili oleh permintaan ini. Tipe konten kami adalah application/octet-stream dan server bertanggung jawab untuk merakit kembali bagian-bagiannya.

Ini berarti kita memerlukan titik akhir untuk membuat file, POST di titik akhir /file yang membuat objek file baru dengan metadata, lalu titik akhir lainnya di POST file/{id}/chunk yang memungkinkan pengguna memposting potongan.

Sesuatu yang juga dapat kami pikirkan adalah melakukan hashing pada file di klien, lalu membandingkan hash tersebut setelah kami memasang kembali file di server, untuk memastikan kecocokannya.

Pengunduhan dapat menggunakan Permintaan Rentang HTTP. Hal ini memungkinkan kami meminta bagian-bagian video sekaligus (mengembalikan 206), artinya kami dapat meminta segmen video secara dinamis sesuai kebutuhan. Menggunakan GET file/{id}/chunk dengan header Content-Range bisa menjadi salah satu solusi.

Untungnya pencariannya sedikit lebih sederhana, kita dapat GET hingga /search?title=<search title> dan menerima kode respons biasa.

Desain Model Data

Sekarang kita punya ide tentang bagaimana kita berinteraksi dengan sistem kita, mari pikirkan jenis data yang ingin kita simpan. Beberapa di antaranya akan didasarkan pada byte video, dan beberapa di antaranya akan didasarkan pada metadata.

Awalnya, mari kita pikirkan sebuah file. Sebuah file dapat menyimpan banyak informasi di sekitarnya: judul, tanggal pembuatan, pengguna yang mengunggahnya, jumlah hit, dll.

Berkas

id               BIGINT    PRIMARY KEY 
title            VARCHAR

Kita sekarang perlu memikirkan tentang potongan file kita. Ingat kita perlu melayani banyak jaringan dan perangkat! Ini berarti kita ingin menyimpan potongan dalam banyak kualitas dan format file berbeda. Dengan munculnya penyimpanan objek seperti AWS S3 kita dapat mereferensikan di mana di S3 file kita berada.

Bagian

id           BIGINT    PRIMARY KEY 
file_id      BIGINT    FOREIGN KEY REFERENCES file(id)
format       VARCHAR
quality      BIGINT
order        BIGINT    
location     VARCHAR

Dengan menggunakan cara di atas, kita dapat mengidentifikasi file mana yang merupakan milik suatu potongan, format potongan tersebut, ukuran potongan tersebut, dan bit file mana yang diwakilinya.

Ini merupakan inti dari model data kami. Mari beralih ke desain logis!

Desain Logis

Kita dapat menelusuri desain logis untuk menyatukan berbagai komponen. Saat pengguna ingin mengunggah file, mereka mengirimkan permintaan ke layanan file untuk membuat metadata file baru. Browser mereka kemudian memecah file menjadi beberapa bagian dan mengunggahnya ke layanan penulisan potongan file.

Layanan pemotongan file ini bertanggung jawab untuk merakit potongan dari klien menjadi satu file, memverifikasinya, dan membaginya menjadi beberapa bagian yang lebih dapat digunakan oleh pemutar media.

Dari sini kami tetap berada dalam antrean untuk memberi kami lapisan ketahanan jika layanan pengkodean file tidak berfungsi. Pesan penerimaan dapat dikembalikan ke pengguna sekarang, kita memiliki filenya, kita hanya perlu memprosesnya.

Layanan pengkodean file mengambil potongan file dan mengubahnya menjadi format dan kualitas berbeda untuk layanan berbeda. Hal yang menarik untuk diperhatikan adalah ada perbedaan antara pengkodean dan transcoding.

  • Transcoding: Membuat file dalam berbagai ukuran.
  • Pengkodean: Mengkode ulang file dalam format berbeda.

Pengkodean mencakup transkode, itulah sebabnya kami menggunakan istilah ini di sini.

Layanan ini bertanggung jawab untuk menulis potongan kami ke penyimpanan, serta memperbarui metadata file kami dengan lokasinya.

Permintaan baca juga melalui API Gateway, namun dialihkan ke layanan baca potongan file kami jika mereka meminta video untuk dialirkan, atau layanan file kami jika mereka mencari metadata.

Klien akan bertanggung jawab untuk menentukan potongan file mana yang mereka perlukan dan mengirimkan permintaan yang diperlukan untuk memuat bagian selanjutnya.

Fungsi pencarian seharusnya cukup mudah. Layanan file akan menambahkan metadata file ke mesin pencari, sehingga layanan pencarian bertanggung jawab untuk mengkonversi dari permintaan URL ke sesuatu yang dapat kita gunakan dengan mesin pencari, kemudian menyusun responsnya.

Desain Fisik

Sekarang kita sudah membahas gambaran kasar tentang bagaimana kita membangun platform kita, mari kita kembangkan menjadi sesuatu yang nyata di dunia nyata.

Bahkan ketika saya sedang menulisnya, saya dapat melihat sejumlah masalah dengan desain ini. Namun, mari kita jelajahi dan kita dapat menyempurnakannya di bagian selanjutnya.

Awalnya, semua layanan statis kami menggunakan AWS ECS. Ada argumen bahwa kita dapat beralih sepenuhnya tanpa server, tetapi menurut saya kita tidak ingin mengkhawatirkan waktu mulai yang dingin. Gateway kami adalah AWS API Gateway dan antrean kami menggunakan SQS.

Kami menyimpan potongan kami di S3, dan menggunakan AWS Elemental MediaConvert untuk pengkodean kami. Mesin pencarinya sendiri adalah Amazon OpenSearch.

Mengidentifikasi dan Mengatasi Kemacetan

Kami menyebutkan ada beberapa peningkatan kinerja yang dapat kami lakukan. Awalnya, saya berkonsentrasi pada jumlah data yang kami terima. Kita perlu memiliki sistem yang tangguh dan mampu menangani banyak informasi secara real-time. Ini sepertinya pekerjaan untuk streaming.

Dari diagram Anda dapat melihat bahwa API Gateway kami sekarang menulis ke Kinesis stream, yang pada gilirannya mendorong ke AWS EMR (mungkin dengan Spark Streaming aktif) untuk memproses dan menyusun ulang file kami.

Karena metadata file kami jarang berubah, kami juga dapat menambahkan lapisan cache ke layanan baca kami.

Kami juga dapat menerapkan streaming bitrate adaptif, yang dibahas dengan sangat baik di artikel tertaut. Di pembuat enkode, kami membuat potongan file yang sama dengan kualitas berbeda-beda. Dengan menggunakan teknik ini, kami dapat secara reaktif mengirimkan potongan dengan kualitas berbeda tergantung pada kondisi koneksi streamer saat ini.

Bagian terakhir dari optimasi adalah menambahkan CDN. Karena kami memiliki pengguna di seluruh dunia, kami tidak ingin mereka semua mencoba mengakses video mereka dari server di Inggris. Masuk akal untuk mencoba dan mendistribusikan konten kami lebih dekat ke tempat mereka berada. Catatan, Netflix sebenarnya memiliki solusinya sendiri untuk masalah ini yang disebut Open Connect, yang patut untuk dilihat!

Kesimpulan

Sebagai kesimpulan, kami telah membahas bagaimana kami dapat mendesain platform bergaya YouTube atau Netflix. Perlu diketahui bahwa ada “sejumlah alat yang dikembangkan Netflix secara khusus” untuk masalah ini.

Beberapa contohnya meliputi:

Periksa mereka!