Halo, Habr! Saya mempersembahkan untuk Anda terjemahan artikel
“Utusan model threading” oleh Matt Klein.
Artikel ini sepertinya cukup menarik bagi saya, dan karena Utusan paling sering digunakan sebagai bagian dari "istio" atau hanya sebagai kubernet "pengendali masuk", oleh karena itu kebanyakan orang tidak memiliki interaksi langsung yang sama dengannya seperti misalnya dengan instalasi Nginx atau Haproxy yang khas. Namun, jika sesuatu pecah, akan lebih baik untuk memahami cara kerjanya dari dalam. Saya mencoba menerjemahkan teks sebanyak mungkin ke dalam bahasa Rusia, termasuk kata-kata khusus, bagi mereka yang sulit melihat ini, saya meninggalkan aslinya dalam tanda kurung. Selamat datang di kucing.
Dokumentasi teknis tingkat rendah pada basis kode Utusan saat ini cukup langka. Untuk memperbaikinya, saya berencana membuat serangkaian artikel blog tentang berbagai subsistem Utusan. Karena ini adalah artikel pertama, beri tahu saya apa yang Anda pikirkan dan apa yang mungkin Anda minati dalam artikel-artikel berikut.
Salah satu pertanyaan teknis paling umum yang saya dapatkan tentang Utusan adalah permintaan untuk deskripsi tingkat rendah dari model threading yang digunakan. Dalam posting ini, saya akan menjelaskan bagaimana Utusan memetakan koneksi ke utas, serta deskripsi sistem Penyimpanan Lokal Thread, yang digunakan secara internal untuk membuat kode lebih paralel dan berkinerja tinggi.
Ikhtisar pengaliran
Utusan menggunakan tiga jenis aliran:- Utama: Utas ini mengontrol awal dan akhir proses, semua pemrosesan XDS (xDiscovery Service) API, termasuk DNS, pemeriksaan kesehatan, klaster umum dan manajemen layanan (runtime), pengaturan ulang statistik, administrasi dan manajemen umum proses - sinyal Linux, restart panas, dll. Segala sesuatu yang terjadi di utas ini asinkron dan non-pemblokiran. Secara umum, utas utama mengoordinasikan semua proses fungsional yang penting, yang tidak memerlukan CPU dalam jumlah besar untuk diselesaikan. Ini memungkinkan sebagian besar kode kontrol ditulis seolah-olah satu-utas.
- Pekerja: Secara default, Utusan membuat utas pekerja untuk setiap utas perangkat keras dalam sistem, ini dapat dikontrol menggunakan opsi
--concurrency
. Setiap utas pekerja memulai perulangan acara "non-pemblokiran", yang bertanggung jawab untuk mendengarkan setiap pendengar, pada saat penulisan (29 Juli 2017) tidak ada pecahan dari pendengar, menerima yang baru koneksi, membuat instance dari tumpukan filter untuk terhubung, dan memproses semua operasi I / O selama masa koneksi. Sekali lagi, ini memungkinkan sebagian besar kode pemrosesan koneksi ditulis seolah-olah itu adalah single-threaded. - File flusher: Setiap file yang ditulis Utusan, terutama akses log, saat ini memiliki aliran pemblokiran independen. Ini disebabkan oleh fakta bahwa menulis ke file yang di-cache oleh sistem file, bahkan ketika menggunakan
O_NONBLOCK
, kadang-kadang dapat diblokir (desah). Ketika utas pekerja perlu menulis ke file, data tersebut sebenarnya dipindahkan ke buffer dalam memori, di mana akhirnya dialirkan melalui aliran flush file . Ini adalah salah satu area kode di mana secara teknis semua utas pekerja dapat memblokir kunci yang sama saat mencoba mengisi buffer memori.
Penanganan koneksi
Sebagaimana dibahas secara singkat di atas, semua utas pekerja mendengarkan semua pendengar tanpa segmentasi apa pun. Dengan demikian, kernel digunakan untuk mengirim soket yang diterima ke thread pekerja dengan benar. Core modern umumnya sangat pandai dalam hal ini, mereka menggunakan fitur-fitur seperti meningkatkan prioritas input-output (IO) untuk mencoba mengisi utas dengan kerja, sebelum mulai menggunakan utas lain yang juga mendengarkan pada soket yang sama, dan juga tidak menggunakan penguncian melingkar. (Spinlock) untuk menangani setiap permintaan.
Setelah koneksi diterima pada utas pekerja, itu tidak pernah meninggalkan utas ini. Semua pemrosesan lebih lanjut dari koneksi sepenuhnya diproses di utas pekerja, termasuk perilaku penerusan.
Ini memiliki beberapa konsekuensi penting:- Semua kumpulan koneksi di Utusan berada dalam alur kerja. Dengan demikian, meskipun kumpulan koneksi HTTP / 2 hanya membuat satu koneksi ke setiap host upstream pada suatu waktu, jika ada empat utas pekerja, akan ada empat koneksi HTTP / 2 ke host upstream dalam kondisi mapan.
- Alasan Utusan bekerja dengan cara ini adalah karena dengan menyimpan segala sesuatu dalam satu alur kerja, hampir semua kode dapat ditulis tanpa memblokir dan seolah-olah itu adalah single-threaded. Desain ini membuat penulisan banyak kode lebih mudah dan skala sangat baik untuk jumlah alur kerja yang hampir tak terbatas.
- Namun, salah satu kesimpulan utama adalah bahwa dari sudut pandang kumpulan memori dan efisiensi koneksi, sebenarnya sangat penting untuk mengkonfigurasi parameter
--concurrency
. Memiliki lebih banyak utas pekerja daripada yang diperlukan akan menyebabkan hilangnya memori, membuat lebih banyak koneksi tidak aktif dan memperlambat kecepatan masuk ke kolam koneksi. Di Lyft, wadah sespan utusan kami bekerja dengan konkurensi sangat rendah, sehingga kinerjanya kira-kira setara dengan layanan yang mereka duduki di sebelahnya. Kami menjalankan Utusan sebagai proxy tepi (edge) hanya dengan konkurensi maksimum.
Apa yang dimaksud dengan non-blocking?
Istilah "non-blocking" sejauh ini telah digunakan beberapa kali dalam membahas cara kerja utas utama dan pekerja. Semua kode ditulis asalkan tidak ada yang diblokir. Namun, ini tidak sepenuhnya benar (yang tidak sepenuhnya benar?).
Utusan menggunakan beberapa kunci proses yang panjang:- Seperti yang telah disebutkan, saat menulis log akses, semua utas pekerja mendapatkan kunci yang sama sebelum mengisi buffer log dalam memori. Waktu penahanan kunci harus sangat rendah, tetapi ada kemungkinan bahwa kunci ini akan ditantang dengan konkurensi tinggi dan throughput tinggi.
- Utusan menggunakan sistem yang sangat canggih untuk memproses statistik yang bersifat lokal ke aliran. Ini akan menjadi topik posting terpisah. Namun, saya akan secara singkat menyebutkan bahwa sebagai bagian dari pemrosesan statistik aliran lokal, kadang-kadang diperlukan untuk mendapatkan kunci untuk "toko statistik" pusat. Kunci ini seharusnya tidak diperlukan.
- Utas utama secara berkala membutuhkan koordinasi dengan semua alur kerja. Ini dilakukan dengan "menerbitkan" dari utas utama ke utas pekerja, dan terkadang dari utas pekerja kembali ke utas utama. Untuk mengirim, pemblokiran diperlukan agar pesan yang diterbitkan dapat antri untuk pengiriman berikutnya. Kunci ini seharusnya tidak pernah mengalami persaingan serius, tetapi mereka masih dapat diblokir secara teknis.
- Ketika Utusan menulis log ke aliran kesalahan sistem (kesalahan standar), ia menerima kunci pada seluruh proses. Secara keseluruhan, logging lokal Utusan dianggap mengerikan dalam hal kinerja, jadi tidak ada banyak perhatian yang diberikan untuk memperbaikinya.
- Ada beberapa kunci acak lainnya, tetapi tidak satupun dari mereka yang kinerjanya kritis dan tidak boleh diperdebatkan.
Utas penyimpanan lokal
Karena cara Utusan memisahkan tanggung jawab utas utama dari tanggung jawab alur kerja, ada persyaratan bahwa pemrosesan rumit dapat dilakukan pada utas utama dan kemudian diberikan kepada masing-masing alur kerja dengan konkurensi tingkat tinggi. Bagian ini menjelaskan sistem Utusan Penyimpanan Lokal Utas (TLS) di tingkat tinggi. Di bagian selanjutnya, saya akan menjelaskan bagaimana ini digunakan untuk mengelola cluster.

Seperti yang sudah dijelaskan, utas utama memproses hampir semua fungsi manajemen dan fungsionalitas bidang kontrol dalam proses Utusan. Pesawat kendali agak kelebihan beban di sini, tetapi jika Anda melihatnya dalam proses Utusan itu sendiri dan membandingkannya dengan penerusan yang dilakukan ulir pekerja, ini sepertinya tepat. Sebagai aturan umum, proses utas utama berfungsi, dan kemudian perlu memperbarui setiap utas pekerja sesuai dengan hasil pekerjaan ini,
sedangkan utas pekerja tidak perlu menetapkan kunci pada setiap akses .
Sistem Utusan TLS (Thread local storage) berfungsi sebagai berikut:- Kode yang berjalan di utas utama dapat mengalokasikan slot TLS untuk seluruh proses. Meskipun ini diabstraksikan, dalam praktiknya ini adalah indeks dalam vektor yang menyediakan akses O (1).
- Aliran utama dapat mengatur data acak di slotnya. Ketika ini dilakukan, data diterbitkan dalam setiap alur kerja sebagai peristiwa loop peristiwa reguler.
- Utas pekerja dapat membaca dari slot TLS mereka dan mengambil data utas lokal yang tersedia di sana.
Meskipun ini adalah paradigma yang sangat sederhana dan sangat kuat, ini sangat mirip dengan konsep pemblokiran RCU (Read-Copy-Update). Intinya, alur kerja tidak pernah melihat perubahan data apa pun di slot TLS saat dijalankan. Perubahan hanya terjadi selama periode istirahat antara acara kerja.
Utusan menggunakan ini dalam dua cara berbeda:- Dengan menyimpan berbagai data pada setiap alur kerja, akses ke data ini dilakukan tanpa pemblokiran.
- Dengan menyimpan pointer global ke data global dalam mode baca-saja di setiap utas pekerja. Dengan demikian, setiap utas pekerja memiliki penghitung referensi data, yang tidak dapat dikurangi selama pelaksanaan pekerjaan. Hanya ketika semua pekerja tenang dan mengunggah data baru yang dibagikan maka data lama akan dihancurkan. Ini identik dengan RCU.
Threading pembaruan cluster
Di bagian ini, saya akan menjelaskan bagaimana TLS (Thread local storage) digunakan untuk mengelola sebuah cluster. Manajemen klaster mencakup pemrosesan xDS dan / atau DNS API, serta pemeriksaan kesehatan.
Manajemen aliran Cluster meliputi komponen dan langkah-langkah berikut:- Cluster Manager adalah komponen dalam Utusan yang mengelola semua cluster hulu yang diketahui, API CDS (Cluster Discovery Service), SDS (Secret Discovery Service) dan EDS (Endpoint Discovery Service), DNS dan pemeriksaan eksternal aktif kesehatan (pemeriksaan kesehatan). Dia bertanggung jawab untuk menciptakan representasi “akhirnya konsisten” dari setiap klaster hulu yang mencakup inang yang ditemukan, serta status kesehatan.
- Pemeriksa kesehatan melakukan pemeriksaan kesehatan aktif dan melaporkan perubahan status kesehatan kepada manajer klaster.
- CDS (Cluster Discovery Service) / SDS (Secret Discovery Service) / EDS (Endpoint Discovery Service) / DNS dilakukan untuk menentukan keanggotaan cluster. Perubahan status dikembalikan ke manajer kluster.
- Setiap alur kerja terus-menerus menjalankan loop acara.
- Ketika manajer cluster menentukan bahwa status untuk cluster telah berubah, itu membuat snapshot cluster read-only baru dan mengirimkannya ke setiap utas pekerja.
- Selama periode tidak aktif berikutnya, alur kerja akan memperbarui foto di slot TLS khusus.
- Selama acara I / O yang tuan rumah harus menentukan untuk load balancing, load balancer akan meminta slot TLS (Thread local storage) untuk mendapatkan informasi host. Tidak diperlukan kunci untuk ini. Perhatikan juga bahwa TLS juga dapat memicu peristiwa selama pemutakhiran, sehingga penyeimbang muatan dan komponen lainnya dapat menghitung ulang cache, struktur data, dll. Ini di luar ruang lingkup tulisan ini, tetapi digunakan di berbagai tempat dalam kode.
Dengan menggunakan prosedur di atas, Utusan dapat memproses setiap permintaan tanpa kunci apa pun (selain yang dijelaskan sebelumnya). Selain kompleksitas kode TLS itu sendiri, sebagian besar kode tidak perlu memahami cara kerja multithreading, dan dapat ditulis dalam mode single-threaded. Ini membuatnya lebih mudah untuk menulis sebagian besar kode di samping kinerja yang unggul.
Subsistem lain yang menggunakan TLS
TLS (Thread local storage) dan RCU (Read Copy Update) banyak digunakan di Utusan.
Contoh penggunaan:- Mekanisme mengubah fungsionalitas selama eksekusi: Daftar fungsi yang diaktifkan saat ini dihitung di utas utama. Setiap alur kerja kemudian dilengkapi dengan snapshot read-only menggunakan semantik RCU.
- Mengganti tabel rute : untuk tabel rute yang disediakan oleh RDS (Route Discovery Service), tabel rute dibuat di utas utama. Cuplikan baca-saja nantinya akan diberikan ke setiap alur kerja menggunakan semantik RCU (Baca Salin Pembaruan). Ini membuat memodifikasi tabel rute secara efisien.
- Caching Header HTTP: Ternyata, menghitung header HTTP untuk setiap permintaan (saat melakukan ~ 25K + RPS per core) cukup mahal. Utusan menghitung header secara terpusat kira-kira setiap setengah detik dan memberikannya kepada setiap karyawan melalui TLS dan RCU.
Ada kasus lain, tetapi contoh sebelumnya harus memberikan pemahaman yang baik tentang apa yang digunakan untuk TLS.
Perangkap kinerja yang dikenal
Meskipun Utusan bekerja secara keseluruhan dengan cukup baik, ada beberapa area terkenal yang perlu perhatian ketika digunakan dengan konkurensi dan bandwidth yang sangat tinggi:
- Seperti yang sudah dijelaskan dalam artikel ini, saat ini semua utas pekerja terkunci ketika mereka menulis ke buffer memori log akses. Dengan konkurensi tinggi dan throughput tinggi, akan perlu untuk mengemas log akses untuk setiap alur kerja karena pengiriman yang tidak teratur saat menulis ke file akhir. Atau, Anda dapat membuat log akses terpisah untuk setiap alur kerja.
- Meskipun statistik sangat dioptimalkan, dengan konkurensi dan throughput yang sangat tinggi, kemungkinan ada persaingan atom pada statistik individu. Solusi untuk masalah ini adalah penghitung per satu alur kerja dengan reset berkala penghitung pusat. Ini akan dibahas dalam posting selanjutnya.
- Arsitektur yang ada tidak akan berfungsi dengan baik jika Utusan digunakan dalam skenario di mana ada sangat sedikit koneksi yang membutuhkan sumber daya pemrosesan yang signifikan. Tidak ada jaminan bahwa komunikasi akan didistribusikan secara merata di antara alur kerja. Ini dapat diselesaikan dengan menyeimbangkan koneksi kerja, di mana kemampuan untuk bertukar koneksi antara aliran kerja akan direalisasikan.
Kesimpulan
Model threading Utusan dirancang untuk memberikan kemudahan pemrograman dan konkurensi besar karena penggunaan memori dan koneksi yang berpotensi boros jika tidak dikonfigurasi dengan benar. Model ini memungkinkannya bekerja dengan sangat baik dengan jumlah utas dan throughput yang sangat tinggi.
Seperti yang saya sebutkan secara singkat di Twitter, desain juga dapat berjalan di atas tumpukan jaringan yang berfungsi penuh dalam mode pengguna, seperti DPDK (Data Plane Development Kit), yang dapat menyebabkan server reguler memproses jutaan permintaan per detik dengan pemrosesan L7 penuh. Akan sangat menarik untuk melihat apa yang akan dibangun dalam beberapa tahun mendatang.
Satu komentar cepat terakhir: Saya sering ditanya mengapa kami memilih C ++ untuk Utusan. Alasannya, seperti sebelumnya, adalah bahwa itu masih satu-satunya bahasa tingkat industri yang digunakan secara luas untuk membangun arsitektur yang dijelaskan dalam posting ini. C ++ jelas tidak cocok untuk semua atau bahkan untuk banyak proyek, tetapi untuk kasus penggunaan tertentu itu masih satu-satunya alat untuk menyelesaikan pekerjaan (untuk menyelesaikan pekerjaan).
Tautan ke kode
Tautan ke file dengan antarmuka dan implementasi header yang dibahas dalam posting ini: