Bagaimana cara mempercepat akses ke aplikasi API dengan cepat dan mudah?

Jawabannya sederhana: menggunakan alat yang sudah terbukti seperti cache dan penskalaan horizontal. Kita harus segera mengatakan bahwa ini bukan satu-satunya alat, tetapi lebih sering daripada tidak, pendekatan klasik yang terbukti ternyata paling efektif bahkan dalam kondisi modern. Mari kita lihat contoh praktisnya.



Tentang masalah aslinya



Platform video PREMIER, sebagaimana layaknya sumber daya modern, telah membuat layanan rekomendasi untuk kliennya berdasarkan pembelajaran mesin. Banyak pengguna beralih ke platform video - sekitar satu juta sehari, PREMIER sangat populer - dan panggilan datang melalui formulir web, dari aplikasi untuk perangkat seluler dan dari Smart TV.



Data awal yang menjadi dasar pembelajaran mesin dari layanan kami bekerja disimpan di DBMS ClickHouse berkolom. Sesuai jadwal, data diproses di latar belakang untuk membangun model (yang akan digunakan untuk mengeluarkan rekomendasi akhir). Hasil perhitungan disimpan di DBMS relasional PostgreSQL.



Solusi untuk masalah interaksi cepat antara server aplikasi dan klien dalam waktu singkat selalu relevan. Untuk memastikan kecepatan kerja yang diperlukan - dan waktu respons seharusnya tidak lebih dari 50 md - kami harus mengoptimalkan struktur database relasional, mengimplementasikan caching dan penskalaan horizontal. Kami akan membicarakan beberapa teknik sekarang.







Tentang caching



Caching adalah teknik pengoptimalan yang umum. Kami membuat penyimpanan perantara tambahan, lebih cepat dari yang utama, dan menempatkan data paling populer di sana. Akses ke data cache dipercepat secara dramatis, dan ini secara signifikan meningkatkan kecepatan aplikasi. Saat mengembangkan aplikasi beban tinggi, caching adalah opsi paling umum untuk mengoptimalkan kinerja aplikasi tanpa memperluas sumber daya perangkat keras. Caching dapat menyebabkan beberapa penghematan dalam hal penggunaan sumber daya untuk menghasilkan kembali keluaran yang sama untuk masukan yang sama.



Kapasitas cache terbatas - semakin cepat penyimpanannya, semakin mahal harganya - sehingga perlu digunakan secara efisien. Tentu saja, secara teoritis Anda dapat melakukannya tanpa caching sama sekali jika penyimpanan utama Anda cukup cepat. Tapi secara ekonomi tidak menguntungkan, sejak itu Anda harus melakukan peningkatan perangkat keras yang signifikan, yang sering kali bermuara pada peningkatan RAM dan / atau mengganti disk dari HDD ke SSD. Itu. secara signifikan meningkatkan persyaratan infrastruktur, yang akan mempengaruhi parameter ekonomi dari seluruh aplikasi yang dibuat. Tanpa caching yang telah teruji waktu, dalam banyak kasus, hampir tidak mungkin untuk membuat produk massal.



Namun, menambahkan lapisan caching tidak secara otomatis menyelesaikan semua masalah. Penting juga untuk memikirkan aturan pengisiannya, yang bergantung pada karakteristik tugas, yang dapat berubah tergantung pada situasi dan seiring berkembangnya layanan. Dan perlu diingat bahwa ini bukan obat mujarab, tetapi hanya obat yang akan meringankan gejala masalah kinerja di bagian tertentu aplikasi Anda. Jika aplikasi Anda memiliki masalah arsitektural yang dalam, lingkungan eksekusi yang biasa-biasa saja, caching kemungkinan besar akan menambah masalah Anda.



Ada beberapa opsi tempat menyimpan sumber daya yang disimpan dalam cache: secara lokal - pada contoh klien di browser, di layanan CDN pihak ketiga, di sisi aplikasi. Kami akan berbicara tentang cache dalam aplikasi. Memori proses aplikasi mungkin merupakan opsi cache data paling umum yang akan Anda temui. Namun, solusi ini memiliki kekurangan, karena memori dikaitkan dengan proses melakukan tugas tertentu. Ini semua lebih penting jika Anda berencana untuk menskalakan aplikasi Anda, karena memori tidak dialokasikan di antara proses, mis. itu tidak akan tersedia di proses lain, seperti yang bertanggung jawab atas pemrosesan asinkron. Ya, penyimpanan ke cache berfungsi, tetapi kami benar-benar tidak mendapatkan manfaat penuh darinya.



Cache pemberi rekomendasi







Kembali ke proyek, kami memahami kebutuhan akan solusi cache terpusat. Untuk cache bersama, Anda dapat menggunakan, misalnya, Memcache: jika aplikasi terhubung ke instance yang sama, Anda dapat menggunakannya dalam banyak proses. Di satu sisi, Memcached adalah solusi yang sederhana dan nyaman, di sisi lain, ini agak terbatas dalam hal pengelolaan pembatalan yang tepat, pengetikan data, dan kueri yang lebih kompleks ke penyimpanan data yang di-cache. Sekarang, faktanya, penyimpanan Redis telah menjadi standar dalam tugas cache, tanpa kekurangan Memcache.



Redis adalah penyimpanan cepat nilai-nilai kunci. Ini meningkatkan efisiensi bekerja dengan data, karena menjadi mungkin untuk menentukan struktur. Memberikan kontrol terperinci atas kecacatan dan preemption, memungkinkan Anda memilih dari enam kebijakan yang berbeda. Redis mendukung preemption malas dan aktif, serta preemption waktu. Penggunaan struktur data Redis dapat memberikan pengoptimalan yang nyata, tergantung pada entitas bisnisnya. Misalnya, daripada menyimpan objek sebagai string serial, pengembang dapat menggunakan struktur data hash untuk menyimpan dan memanipulasi bidang dan nilai dengan kunci. Hash menghilangkan kebutuhan untuk mengambil seluruh string, deserialisasi, dan menggantinya di cache dengan nilai baru pada setiap pembaruan, yang berarti konsumsi sumber daya lebih sedikit dan kinerja yang lebih baik. Struktur data lainnya,"Lembar" yang disarankan Redis, "kumpulan", "kumpulan yang diurutkan", "hyperlog", "bitmap", dan "indeks geografis") dapat digunakan untuk mengimplementasikan skenario yang lebih kompleks. Set yang diurutkan untuk menganalisis data deret waktu menawarkan pengurangan kompleksitas dan volume dalam memproses dan mentransfer data. Struktur data HyperLogLog dapat digunakan untuk menghitung item unik dalam satu set dengan hanya menggunakan sejumlah kecil memori persisten, khususnya 12 KB untuk setiap HyperLogLog (ditambah beberapa byte untuk kunci itu sendiri). Bagian penting dari sekitar 200 perintah yang tersedia di Redis dikhususkan untuk operasi pemrosesan data dan menyematkan logika ke dalam database itu sendiri menggunakan skrip Lua.Kemampuan perintah dan skrip bawaan memberikan fleksibilitas untuk memproses data secara langsung di Redis tanpa harus mengirim data melalui jaringan ke aplikasi Anda, mengurangi beban penerapan logika caching tambahan. Memiliki data cache tersedia segera setelah memulai ulang dapat secara signifikan mengurangi waktu pemanasan cache dan meringankan beban penghitungan ulang konten cache dari penyimpanan data utama. Kami akan berbicara tentang fitur konfigurasi Redis dan prospek pengelompokan di artikel berikut.Kami akan berbicara tentang fitur konfigurasi Redis dan prospek pengelompokan di artikel berikut.Kami akan berbicara tentang fitur konfigurasi Redis dan prospek pengelompokan di artikel berikut.



Setelah memilih alat caching, masalah utamanya adalah sinkronisasi data yang disimpan di cache dan data yang disimpan dalam aplikasi. Ada berbagai cara untuk menyinkronkan data tergantung pada logika bisnis aplikasi Anda. Dalam kasus kami, kesulitannya terletak pada pembuatan algoritme untuk pembatalan data. Data yang ditempatkan di cache disimpan di sana untuk waktu yang terbatas, selama ada kebutuhan untuk menggunakannya dalam situasi saat ini, atau setidaknya kemungkinan seperti itu. Seiring perkembangan situasi, mereka harus menyediakan ruang untuk data lain yang, dalam kondisi yang berubah, lebih dibutuhkan. Tugas utama dalam hal ini adalah pemilihan kriteria yang akan digunakan untuk mengeluarkan data dari cache. Paling sering, ini adalah waktu relevansi data, tetapi perlu diingat tentang parameter lain: tentang volume, peringkat (disediakan sama, dengan toleransi,seumur hidup), kategori (data utama atau tambahan), dll.



Cara dasar dan umum untuk selalu memperbarui data adalah keusangan waktu. Metode ini, mengingat pembaruan terpusat berkala dari data aplikasi rekomendasi, juga digunakan oleh kami. Namun, semuanya tidak sesederhana yang terlihat pada pandangan pertama: dalam hal ini, sangat penting untuk memantau peringkat sehingga hanya data yang sangat populer yang masuk ke cache. Ini menjadi mungkin berkat pengumpulan statistik kueri dan penerapan "pemanasan awal data", yaitu pramuat data ke dalam cache pada awal aplikasi. Manajemen ukuran cache juga merupakan aspek penting dari caching. Dalam aplikasi kami, sekitar jutaan rekomendasi dihasilkan, oleh karena itu, tidak realistis untuk menyimpan semua data ini di cache. Manajemen ukuran cache dilakukan dengan menghapus data dari cache untuk memberi ruang bagi data baru.Ada beberapa metode standar: TTL, FIFO, LIFO, terakhir diakses. Untuk saat ini, kami menggunakan TTL, karena Instance Redis tidak melampaui memori yang dialokasikan dan sumber daya disk.



Ingatlah bahwa cache berbeda. Paling sering, ada dua kategori: write-through dan deferred. Dalam kasus pertama, penulisan dilakukan secara sinkron ke cache dan penyimpanan utama. Yang kedua, awalnya, penulisan hanya dilakukan di cache, dan penulisan ke penyimpanan utama akan ditunda hingga data yang diubah diganti dengan blok cache lain. Cache tulis kembali lebih sulit untuk diterapkan, karena memerlukan pemantauan data dalam cache untuk penulisan berikutnya ke penyimpanan utama saat dihapus dari cache. Kami menggunakan opsi pertama sehubungan dengan prosedur pemanasan cache. Perhatikan bahwa "pemanasan" itu sendiri adalah tugas yang penting dan sulit, solusi kami akan dibahas di artikel berikut.



Skala di aplikasi rekomendasi



Untuk memberikan akses PREMIER ke aplikasi rekomendasi, kami menggunakan protokol HTTP, yang sering kali merupakan opsi utama untuk interaksi aplikasi. Sangat cocok untuk mengatur interaksi antar aplikasi, terutama jika Kubernetes dan Ingress Controller digunakan sebagai lingkungan infrastruktur. Menggunakan Kubernetes membuat penskalaan lebih mudah. Alat ini mampu secara otomatis menyeimbangkan permintaan antar pod dalam cluster untuk operasi yang merata, sehingga memudahkan developer untuk meningkatkan skala. Modul Ingress Controller bertanggung jawab untuk ini, yang mendefinisikan aturan untuk koneksi eksternal ke aplikasi di Kubernetes. Secara default, aplikasi di Kubernetes tidak dapat diakses dari jaringan eksternal. Untuk menyediakan akses eksternal ke aplikasi, Anda harus mendeklarasikan sumber daya Ingress yang mendukung penyeimbangan otomatis.Kami menggunakan Nginx Ingress Controller yang mendukung SSL / TLS, aturan penulisan ulang URI, dan VirtualServer serta VirtualServerRoute untuk merutekan permintaan ke aplikasi yang berbeda bergantung pada URI dan header host.



Konfigurasi dasar di Ingress Controller memungkinkan Anda untuk menggunakan hanya fungsi dasar Nginx - ini adalah perutean berdasarkan host dan jalur, dan fungsi tambahan seperti aturan penulisan ulang URI, header respons tambahan, waktu tunggu koneksi tidak tersedia. Anotasi yang diterapkan ke sumber daya Ingress memungkinkan Anda menggunakan fungsi Nginx itu sendiri (biasanya tersedia melalui konfigurasi aplikasi itu sendiri) dan mengubah perilaku Nginx untuk setiap sumber daya Ingress.



Kami berencana untuk menggunakan Nginx Ingress Controller tidak hanya dalam proyek yang kami pertimbangkan sekarang, tetapi juga di sejumlah aplikasi lain, yang akan kami bicarakan nanti. Kami akan membicarakan hal ini di artikel berikut.







Risiko dan konsekuensi menggunakan caching



Setiap tim yang bekerja untuk mengoptimalkan aplikasi intensif data akan memiliki banyak pertanyaan tentang cara mengoptimalkan caching. Pada saat yang sama, masalah dengan penyimpanan ke cache tidak dapat diselesaikan "sekali dan untuk selamanya"; seiring waktu, berbagai masalah muncul. Misalnya, seiring pertumbuhan basis pengguna Anda, Anda mungkin menghadapi statistik yang bertentangan tentang popularitas data menurut kategori, dan masalah ini perlu ditangani.



Meskipun caching adalah alat yang ampuh untuk mempercepat aplikasi, ini bukanlah satu-satunya solusi. Bergantung pada situasi Anda, Anda mungkin perlu mengoptimalkan logika aplikasi, mengubah stack dan lingkungan infrastruktur. Anda juga harus berhati-hati saat memvalidasi persyaratan non-fungsional. Mungkin saja setelah berdiskusi dengan pemilik produk, persyaratan aplikasi akan dilebih-lebihkan. Harus diingat bahwa setiap solusi memiliki karakteristiknya masing-masing.



Risiko menyediakan data lama, meningkatkan kompleksitas solusi secara keseluruhan, dan kemungkinan memasukkan bug laten harus diatasi sebelum metode caching apa pun diterapkan dalam sebuah proyek. Lagi pula, dalam kasus ini, caching hanya akan mempersulit pemecahan masalah, melainkan, itu hanya akan menyembunyikan masalah kinerja dan skalabilitas: apakah kueri database lambat? - Hasil cache dalam penyimpanan cepat! Apakah panggilan API lambat? - Hasil cache pada klien! Ini karena kompleksitas kode yang mengelola caching meningkat secara signifikan seiring dengan meningkatnya kompleksitas logika bisnis.



Pada versi pertama aplikasi, caching benar-benar memiliki efek yang nyata segera setelah implementasi. Lagipula, logika bisnisnya juga sederhana: Anda menyimpan nilainya dan mendapatkannya kembali. Pembatalan validasi itu mudah karena ketergantungan antar entitas bisnis sepele atau tidak ada sama sekali. Namun, seiring waktu, untuk meningkatkan kinerja, Anda perlu menyimpan sejumlah entitas bisnis ke dalam cache.



Caching bukanlah solusi terbaik untuk masalah kinerja. Dalam banyak kasus, mengoptimalkan kode dan penyimpanan inti Anda akan membantu Anda dalam jangka panjang. Selain itu, pengenalan cache seharusnya merupakan reaksi terhadap masalah tersebut, dan bukan pengoptimalan yang prematur.



Kesimpulannya, mengoptimalkan kinerja aplikasi dan membuatnya dapat diskalakan adalah proses berkelanjutan yang bertujuan untuk mencapai perilaku yang dapat diprediksi dalam persyaratan non-fungsional yang ditentukan. Caching diperlukan untuk mengurangi biaya perangkat keras dan waktu pengembangan yang dihabiskan untuk meningkatkan kinerja dan skalabilitas.



Tautan:






All Articles