Teruja untuk memperkenalkan Pingora, proksi HTTP baharu kami yang dibina di atas Rust. Memproses lebih daripada 1 trilion permintaan setiap hari, meningkatkan prestasi dan membawa keupayaan baharu kepada pelanggan Cloudflare sambil memerlukan hanya satu pertiga daripada CPU dan sumber memori infrastruktur proksi asal.
Memandangkan Cloudflare terus berkembang, kami mendapati bahawa kuasa pemprosesan NGINX tidak lagi dapat memenuhi keperluan kami. Walaupun ia menunjukkan prestasi yang baik selama bertahun-tahun, dari masa ke masa kami menyedari ia mempunyai had dalam menghadapi cabaran pada skala kami. Oleh itu, kami merasakan adalah perlu untuk membina beberapa penyelesaian baharu untuk memenuhi keperluan prestasi dan fungsi kami.
Pelanggan dan pengguna Cloudflare menggunakan rangkaian global Cloudflare sebagai proksi antara klien dan pelayan HTTP. Kami telah mengadakan banyak perbincangan, membangunkan banyak teknologi dan melaksanakan protokol baharu seperti pengoptimuman QUIC dan HTTP/2 untuk meningkatkan kecekapan pelayar dan ejen pengguna lain yang menyambung ke rangkaian kami.
Hari ini, kami akan menumpukan pada satu lagi aspek berkaitan persamaan ini: perkhidmatan proksi, yang bertanggungjawab mengurus trafik antara rangkaian dan pelayan Internet kami. Perkhidmatan proksi ini menyediakan sokongan dan kuasa untuk CDN, Workers fetch, Tunnel, Stream, R2 kami dan banyak lagi ciri dan produk lain.
Mari kita mendalami sebab kami memutuskan untuk meningkatkan perkhidmatan warisan kami dan meneroka proses pembangunan sistem Pingora. Sistem ini direka khusus untuk kes dan skala penggunaan pelanggan Cloudflare.
Dalam beberapa tahun kebelakangan ini, kami telah menghadapi beberapa batasan apabila menggunakan NGINX. Untuk beberapa batasan, kami telah mengoptimumkan atau menggunakan kaedah untuk memintasnya. Walau bagaimanapun, terdapat beberapa batasan yang lebih mencabar.
Seni bina (proses) pekerja NGINX [4] mempunyai kelemahan operasi untuk kes penggunaan kami, yang menjejaskan prestasi dan kecekapan kami.
Pertama sekali, dalam NGINX, setiap permintaan hanya boleh diproses oleh seorang pekerja sahaja. Ini mengakibatkan ketidakseimbangan beban antara semua teras CPU [5], mengakibatkan kelembapan [6].
Disebabkan kesan penguncian proses permintaan ini, permintaan yang melaksanakan tugas IO intensif CPU atau menyekat mungkin melambatkan permintaan lain. Seperti yang dinyatakan oleh catatan blog ini, kami telah melaburkan banyak masa dalam menyelesaikan masalah ini.
Isu paling kritikal untuk kes penggunaan kami ialah penggunaan semula sambungan, apabila mesin kami mewujudkan sambungan TCP dengan pelayan asal yang memproksi permintaan HTTP. Dengan menggunakan semula sambungan daripada kumpulan sambungan, anda boleh melangkau TCP dan TLS jabat tangan yang diperlukan untuk sambungan baharu, sekali gus mempercepatkan TTFB (masa kepada bait pertama) permintaan.
Walau bagaimanapun, kumpulan sambungan NGINX [9] sepadan dengan seorang pekerja. Apabila permintaan sampai kepada pekerja, ia hanya boleh menggunakan semula sambungan dalam pekerja itu. Apabila kami menambah lebih ramai pekerja NGINX mengikut skala, penggunaan semula sambungan kami menjadi lebih teruk kerana sambungan tersebar di lebih banyak kumpulan terpencil merentas semua proses. Ini menyebabkan TTFB yang lebih perlahan dan lebih banyak sambungan untuk dikekalkan, yang menggunakan sumber (dan wang) kami dan pelanggan kami.
Seperti yang dinyatakan dalam catatan blog lepas, kami mempunyai penyelesaian untuk beberapa isu ini. Tetapi jika kita boleh menyelesaikan masalah asas: model pekerja/proses, kita akan menyelesaikan semua masalah ini secara semula jadi.
Sesetengah jenis fungsi sukar untuk ditambah
NGINX ialah pelayan web yang hebat, pengimbang beban atau gerbang mudah. Tetapi Cloudflare melakukan lebih daripada itu. Kami pernah membina semua fungsi yang kami perlukan di sekitar NGINX, tetapi cuba mengelakkan terlalu banyak perbezaan daripada pangkalan kod huluan NGINX tidaklah mudah.
Sebagai contoh, apabila mencuba semula permintaan/kegagalan permintaan[10], kadangkala kami ingin menghantar permintaan ke pelayan asal yang berbeza dengan set pengepala permintaan yang berbeza. Tetapi NGINX tidak membenarkan ini. Dalam kes ini, kita perlu meluangkan masa dan usaha untuk mengatasi batasan NGINX.
Pada masa yang sama, bahasa pengaturcaraan yang terpaksa kami gunakan tidak membantu meringankan kesukaran ini. NGINX ditulis semata-mata dalam C, yang tidak selamat memori oleh reka bentuk. Menggunakan asas kod pihak ke-3 seperti ini sangat terdedah kepada ralat. Malah bagi jurutera yang berpengalaman, adalah mudah untuk jatuh ke dalam masalah keselamatan ingatan [11], dan kami ingin mengelakkan masalah ini sebanyak mungkin.
Bahasa lain yang kami gunakan untuk melengkapkan C ialah Lua. Ia kurang berisiko, tetapi juga kurang berprestasi. Tambahan pula, apabila berurusan dengan kod Lua yang kompleks dan logik perniagaan, kita sering mendapati diri kita kehilangan penaipan statik [12].
Dan komuniti NGINX tidak begitu aktif, dan pembangunan sering dilakukan "di sebalik pintu tertutup" [13].
Pilih untuk membina sendiri
Sejak beberapa tahun kebelakangan ini, memandangkan pangkalan pelanggan dan set ciri kami terus berkembang, kami terus menilai tiga pilihan:
Sejak beberapa tahun kebelakangan ini, kami telah menilai pilihan ini setiap suku tahun. Tiada formula yang jelas untuk menentukan pilihan yang terbaik. Dalam tempoh beberapa tahun, kami terus mengambil jalan yang paling sedikit tentangan dan terus meningkatkan NGINX. Walau bagaimanapun, dalam beberapa kes, ROI untuk membina agensi anda sendiri mungkin kelihatan lebih berbaloi. Kami menyeru untuk membina ejen dari awal dan mula mereka bentuk aplikasi ejen impian kami.
Untuk membina proksi yang pantas, cekap dan selamat yang boleh menyampaikan berjuta-juta permintaan sesaat, kami perlu membuat beberapa keputusan reka bentuk yang penting terlebih dahulu.
Kami memilih Rust[16] sebagai bahasa untuk projek kerana ia boleh melakukan perkara yang boleh C lakukan dengan cara yang selamat ingatan tanpa menjejaskan prestasi.
Walaupun terdapat beberapa perpustakaan HTTP pihak ketiga yang hebat, seperti hyper[17], kami memilih untuk membina perpustakaan kami sendiri kerana kami ingin memaksimumkan fleksibiliti kami dalam mengendalikan trafik HTTP dan memastikan kami boleh melakukannya mengikut kadar kami sendiri Inovasi.
Di Cloudflare, kami mengendalikan trafik untuk keseluruhan internet. Kami perlu menyokong banyak kes trafik HTTP yang pelik dan tidak mematuhi RFC. Ini adalah dilema biasa dalam komuniti HTTP dan Web, di mana pilihan sukar perlu dibuat antara mengikut ketat spesifikasi HTTP dan menyesuaikan diri dengan nuansa ekosistem yang lebih luas bagi pelanggan atau pelayan warisan berpotensi.
Kod status HTTP ditakrifkan dalam RFC 9110 sebagai integer tiga digit [18] dan secara amnya dijangka berada dalam julat 100 hingga 599. Hyper ialah satu pelaksanaan sedemikian. Walau bagaimanapun, banyak pelayan menyokong penggunaan kod status antara 599 dan 999. Kami mencipta soalan [19] untuk ciri ini yang meneroka pelbagai sisi perbahasan. Walaupun pasukan hiper akhirnya menerima perubahan ini, mereka mempunyai alasan kukuh untuk menolak permintaan sedemikian, dan ini hanyalah salah satu daripada banyak kes ketidakpatuhan yang perlu kami sokong.
Untuk memenuhi kedudukan Cloudflare dalam ekosistem HTTP, kami memerlukan perpustakaan HTTP yang teguh, bertolak ansur, boleh disesuaikan yang boleh bertahan dalam pelbagai persekitaran risiko Internet dan menyokong pelbagai kes penggunaan tidak patuh. Cara terbaik untuk menjamin ini adalah dengan melaksanakan seni bina kami sendiri.
Keputusan reka bentuk seterusnya melibatkan sistem penjadualan beban kerja kami. Kami memilih multithreading berbanding multiprocessing [20] untuk berkongsi sumber dengan mudah, terutamanya pengumpulan sambungan. Kami percaya bahawa mencuri kerja [21] juga perlu dilaksanakan untuk mengelakkan kategori isu prestasi tertentu yang dinyatakan di atas. Masa jalan tak segerak Tokio menghasilkan kesesuaian yang sempurna [22] untuk keperluan kita.
Akhir sekali, kami mahu projek kami menjadi intuitif dan mesra pembangun. Apa yang kami bina bukanlah produk akhir tetapi harus diperluaskan sebagai platform kerana lebih banyak ciri dibina di atasnya. Kami memutuskan untuk melaksanakan antara muka boleh atur cara berdasarkan peristiwa "kitaran hayat permintaan" yang serupa dengan NGINX/OpenResty[23]. Contohnya, peringkat Penapis Permintaan membenarkan pembangun menjalankan kod untuk mengubah suai atau menolak permintaan apabila pengepala permintaan diterima. Dengan reka bentuk ini, kami boleh memisahkan logik perniagaan dan logik proksi biasa kami dengan jelas. Pembangun yang sebelum ini mengusahakan NGINX boleh bertukar kepada Pingora dengan mudah dan cepat menjadi lebih produktif.
Mari cepat ke hadapan sekarang. Pingora mengendalikan hampir semua permintaan HTTP yang memerlukan interaksi dengan pelayan asal (seperti cache terlepas), dan kami mengumpul banyak data prestasi dalam proses itu.
Pertama, mari lihat cara Pingora mempercepatkan trafik pelanggan kami. Keseluruhan trafik pada Pingora menunjukkan pengurangan TTFB median sebanyak 5ms dan pengurangan persentil ke-95 sebanyak 80ms. Ia bukan kerana kami menjalankan kod dengan lebih pantas. Malah perkhidmatan lama kami boleh mengendalikan permintaan dalam julat sub-milisaat.
Penjimatan masa diperoleh daripada seni bina baharu kami, yang berkongsi sambungan merentas semua rangkaian. Ini bermakna penggunaan semula sambungan yang lebih baik dan kurang masa yang dihabiskan untuk berjabat tangan TCP dan TLS.
Di seluruh pelanggan, Pingora hanya mempunyai satu pertiga sambungan baharu sesaat berbanding perkhidmatan lama. Bagi satu pelanggan utama, ia meningkatkan penggunaan semula sambungan daripada 87.1% kepada 99.92%, yang mengurangkan sambungan baharu sebanyak 160x. Untuk meletakkannya dalam perspektif, dengan bertukar kepada Pingora, kami menyelamatkan pelanggan dan pengguna kami 434 tahun berjabat tangan setiap hari.
Mempunyai antara muka mesra pembangun yang biasa digunakan oleh jurutera sambil mengalih keluar pengehadan sebelumnya, membolehkan kami membangunkan lebih banyak ciri dengan lebih pantas. Ciri teras seperti protokol baharu berfungsi sebagai bahan binaan untuk cara kami menyampaikan lebih banyak kepada pelanggan kami.
Sebagai contoh, kami dapat menambahkan sokongan huluan HTTP/2 pada Pingora tanpa sekatan jalan utama. Ini membolehkan kami menyediakan gRPC kepada pelanggan kami tidak lama lagi[24]. Menambah fungsi yang sama kepada NGINX akan memerlukan lebih banyak usaha kejuruteraan dan mungkin tidak dapat dilakukan [25].
Baru-baru ini, kami mengumumkan pelancaran Cache Reserve[26], di mana Pingora menggunakan storan R2 sebagai lapisan caching. Sambil kami menambah lebih banyak ciri pada Pingora, kami dapat menawarkan produk baharu yang sebelum ini tidak boleh dilaksanakan.
Dalam pengeluaran, Pingora menggunakan kira-kira 70% kurang CPU dan 67% kurang memori daripada perkhidmatan lama kami di bawah beban trafik yang sama. Penjimatan itu datang dari beberapa faktor.
Kod Rust kami berjalan dengan lebih cekap [28] berbanding kod Lua lama [27] . Selain itu, terdapat juga perbezaan kecekapan dalam seni bina mereka. Contohnya, dalam NGINX/OpenResty, apabila kod Lua ingin mengakses pengepala HTTP, ia mesti membacanya daripada struktur NGINX C, memperuntukkan rentetan Lua, dan kemudian menyalinnya ke dalam rentetan Lua. Selepas itu, Lua juga sampah mengumpul rentetan barunya. Di Pingora, ia hanyalah akses rentetan lurus.
Model berbilang benang juga menjadikan perkongsian data merentas permintaan lebih cekap. NGINX juga mempunyai memori yang dikongsi, tetapi disebabkan oleh had pelaksanaan, setiap akses memori yang dikongsi mesti menggunakan mutex, dan hanya rentetan dan nombor boleh dimasukkan ke dalam memori yang dikongsi. Dalam Pingora, kebanyakan item yang dikongsi boleh diakses secara terus melalui rujukan yang dikongsi di belakang kaunter rujukan atom [29].
Seperti yang dinyatakan di atas, satu lagi bahagian penting dalam penjimatan CPU ialah pengurangan sambungan baharu. Jabat tangan TLS jelas lebih mahal daripada hanya menghantar dan menerima data melalui sambungan yang telah ditetapkan.
Pada skala kami, mengeluarkan ciri dengan cepat dan selamat adalah sukar. Sukar untuk meramalkan setiap kes tepi yang mungkin berlaku dalam persekitaran teragih yang mengendalikan berjuta-juta permintaan sesaat. Ujian fuzz dan analisis statik hanya boleh mengurangkan banyak perkara. Semantik selamat ingatan Rust melindungi kami daripada tingkah laku yang tidak ditentukan dan memberi kami keyakinan bahawa perkhidmatan kami akan berjalan dengan betul.
Dengan jaminan ini, kami boleh memberi lebih tumpuan kepada cara perubahan pada perkhidmatan kami akan berinteraksi dengan perkhidmatan atau sumber pelanggan lain. Kami dapat membangunkan ciri pada irama yang lebih tinggi tanpa dibebani dengan keselamatan ingatan dan ranap yang sukar didiagnosis.
Apabila kemalangan berlaku, jurutera perlu meluangkan masa untuk mendiagnosis bagaimana ia berlaku dan apa yang menyebabkannya. Sejak Pingora diasaskan, kami telah menyampaikan ratusan bilion permintaan dan masih belum ranap disebabkan kod perkhidmatan kami.
Malah, kemalangan Pingora sangat jarang berlaku sehingga apabila kita menghadapinya, kita biasanya mendapati masalah yang tidak berkaitan. Baru-baru ini, kami menemui pepijat kernel [30] sejurus selepas perkhidmatan kami mula ranap. Kami juga telah menemui isu perkakasan pada beberapa mesin yang pada masa lalu menolak ralat memori yang jarang berlaku disebabkan oleh perisian kami, walaupun selepas penyahpepijatan yang ketara hampir mustahil.
Ringkasnya, kami telah membina ejen dalaman yang lebih pantas, cekap dan serba boleh yang berfungsi sebagai platform untuk produk semasa dan masa hadapan kami.
Kami kemudiannya akan membincangkan lebih banyak butiran teknikal tentang isu dan pengoptimuman aplikasi yang kami hadapi, serta pengajaran yang kami pelajari daripada membina Pingora dan melancarkannya untuk menyokong bahagian penting internet. Kami juga akan memperkenalkan inisiatif sumber terbuka kami.
Artikel ini diterbitkan semula daripada blog CloudFlare, ditulis oleh Yuchen Wu & Andrew Hauck
Pautan: https://blog.cloudflare.com/zh-cn/how-we-built-pingora-the-proxy-that-connects-cloudflare-to-the-internet-zh-cn/
Atas ialah kandungan terperinci Betapa hebatnya Pingora! Pelayan web super popular mengatasi Nginx. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!