Apa Itu LuaJIT? Mengapa APISIX Memilih LuaJIT?

Tao Yang

April 14, 2023

Products

Apakah Anda siap untuk membawa permainan API gateway Anda ke level berikutnya? Maka Anda mungkin perlu memperhatikan Apache APISIX, sebuah API gateway berbasis cloud-native yang sedang menjadi sorotan di komunitas pengembang. Yang lebih menarik lagi adalah bahwa Apache APISIX terutama dibangun menggunakan LuaJIT, sebuah bahasa yang ringan dan efisien yang tidak sepopuler beberapa bahasa lainnya.

Dalam artikel ini, kita akan melihat lebih dekat mengapa Apache APISIX memilih LuaJIT dibandingkan bahasa dan teknologi yang lebih populer, serta bagaimana fitur dan keunggulan unik LuaJIT dapat membantu Anda membangun API gateway yang sangat cepat dan mampu menangani beban kerja yang paling menuntut. Jadi, jika Anda ingin meningkatkan performa API gateway Anda, teruslah membaca!

Apa itu LuaJIT

Definisi

Secara sederhana, LuaJIT adalah implementasi dari kompiler just-in-time (JIT) untuk bahasa pemrograman Lua. Untuk pemahaman yang lebih baik, pembaca yang belum familiar dengan LuaJIT dapat memecahnya menjadi dua bagian: Lua dan JIT.

Lua

Lua adalah bahasa pemrograman yang elegan dan mudah dipelajari yang menawarkan manajemen memori otomatis, cakupan leksikal yang lengkap, closure, iterator, coroutine, tail call yang tepat, dan penanganan data praktis menggunakan array asosiatif. Untuk membaca lebih lanjut tentang sintaks Lua, silakan baca Getting Started With Lua untuk informasi lebih lanjut.

Lua dirancang untuk mudah diintegrasikan dengan C atau bahasa pemrograman lain yang banyak digunakan, memungkinkan pengembang untuk memanfaatkan kekuatan bahasa-bahasa tersebut. Lua menyediakan fitur-fitur yang biasanya bukan merupakan keunggulan bahasa seperti C, seperti abstraksi tingkat tinggi relatif terhadap perangkat keras, struktur dinamis, dan pengujian yang sederhana. Kernel bahasa yang kecil dan ketergantungan pada standar ANSI C membuatnya sangat portabel di berbagai platform. Akibatnya, Lua tidak hanya merupakan bahasa skrip yang dapat dijalankan sebagai program mandiri tetapi juga bahasa yang dapat disematkan ke dalam aplikasi lain.

Apache APISIX adalah contoh bagus dari penggunaan Lua dan C di tingkat rendah

Namun, pada saat ini, Lua masih memiliki dua masalah umum yang ditemukan dalam bahasa skrip tradisional: efisiensi rendah dan kode yang terekspos. Teknologi JIT yang diperkenalkan oleh LuaJIT dapat secara efektif menyelesaikan kedua masalah ini.

JIT

JIT (Just-In-Time Compilation), adalah bentuk kompilasi dinamis. Kompilasi dinamis bukan satu-satunya bentuk kompilasi dalam ilmu komputer. Misalnya, bahasa C yang banyak digunakan menggunakan bentuk kompilasi yang berbeda, yang dikenal sebagai kompilasi statis.

Penting untuk dicatat bahwa meskipun kita sering menggunakan istilah Ahead-of-Time Compilation (AOT) untuk menggambarkan kebalikan dari kompilasi dinamis yang digunakan dalam C, keduanya tidak sepenuhnya setara. AOT hanya menggambarkan perilaku mengkompilasi bahasa "tingkat tinggi" menjadi bahasa "tingkat rendah" sebelum menjalankan program. Bahasa target kompilasinya tidak harus berupa kode mesin yang spesifik untuk mesin host program, tetapi dapat didefinisikan secara arbitrer. Misalnya, mengkompilasi Java ke C atau mengkompilasi JavaScript ke V8 juga akan dianggap sebagai AOT. Karena semua kompilasi statis secara teknis dieksekusi sebelum waktu, dalam konteks spesifik ini, AOT dapat dilihat sebagai kompilasi statis yang berlawanan dengan JIT.

Mengabaikan istilah-istilah kompleks ini, ketika mempertimbangkan output dari kompilasi statis, Anda mungkin menemukan bahwa masalah yang dihadapi oleh bahasa Lua juga dapat diselesaikan dengan kompilasi statis. Namun, ini akan mengakibatkan hilangnya keunggulan yang diberikan Lua sebagai bahasa skrip: fleksibilitas pembaruan panas dan kompatibilitas platform yang baik. Oleh karena itu, saat ini, sebagian besar bahasa skrip, kecuali yang memiliki persyaratan khusus, menggunakan JIT untuk mencoba meningkatkan performa bahasa, seperti JavaScript V8 pada platform Chromium dan Ruby menggunakan YJIT.

JIT mencoba menggabungkan kelebihan dan kekurangan interpretasi dinamis Lua dan kompilasi statis C. Selama eksekusi bahasa skrip, JIT terus menganalisis fragmen kode yang sedang dieksekusi, dan mengkompilasi atau mengkompilasi ulang untuk meningkatkan efisiensi eksekusi. Pada titik ini, asumsi di balik JIT adalah bahwa peningkatan performa yang diperoleh dari proses ini akan lebih besar daripada biaya mengkompilasi atau mengkompilasi ulang kode. Secara teori, karena dapat dikompilasi ulang secara dinamis, JIT dapat mengoptimalkan dan mempercepat untuk arsitektur platform spesifik di mana program berjalan, dan dalam beberapa kasus, menghasilkan kecepatan eksekusi yang lebih cepat daripada kompilasi statis.

JIT dibagi menjadi dua jenis: Method JIT tradisional dan Trace JIT yang saat ini digunakan oleh LuaJIT. Method JIT menerjemahkan setiap metode menjadi kode mesin, sementara Trace JIT, yang lebih canggih, mengasumsikan bahwa "Untuk kode yang hanya dieksekusi sekali atau dua kali, eksekusi interpretasi lebih cepat daripada eksekusi yang dikompilasi JIT". Berdasarkan ini, Trace JIT mengoptimalkan JIT tradisional dengan mengidentifikasi fragmen kode yang sering dieksekusi (yaitu kode pada jalur panas) sebagai kode yang perlu dilacak dan mengkompilasi bagian kode ini menjadi kode mesin untuk dieksekusi, seperti yang ditunjukkan pada diagram di bawah ini.

Prinsip LuaJIT

LuaJIT

LuaJIT (versi 2.x) meningkatkan performa JIT secara signifikan dengan mengintegrasikan interpreter berkecepatan tinggi yang ditulis dalam bahasa Assembly dan backend generator kode yang dioptimalkan berdasarkan Static Single Assignment (SSA). Akibatnya, LuaJIT telah menjadi salah satu implementasi bahasa dinamis tercepat.

Selain itu, dibandingkan dengan binding Lua dan C yang rumit di Lua asli untuk interaksi dengan C, LuaJIT juga mengimplementasikan FFI (Foreign Function Interface). Teknologi ini memungkinkan kita untuk memanggil fungsi C eksternal dan menggunakan struktur data C langsung dari kode Lua tanpa mengetahui jumlah dan jenis parameter. Dengan fitur ini, kita dapat langsung menggunakan FFI untuk mengimplementasikan struktur data yang diperlukan alih-alih menggunakan tipe Table asli Lua, lebih meningkatkan performa program dalam skenario yang sensitif terhadap performa. Teknik menggunakan FFI untuk meningkatkan performa berada di luar cakupan artikel ini, dan informasi lebih mendalam dapat ditemukan di artikel Why Does lua-resty-core Perform Better?.

Secara ringkas, LuaJIT telah mengimplementasikan salah satu Trace JIT tercepat dalam bahasa skrip hingga saat ini, menggunakan sintaks Lua. Selain itu, LuaJIT menyediakan fitur-fitur seperti FFI untuk mengatasi masalah efisiensi rendah dan kode yang terekspos pada Lua, sehingga membuat Lua menjadi bahasa skrip dan bahasa yang disematkan yang sangat fleksibel, berperforma tinggi, dan memiliki penggunaan memori yang sangat rendah.

Perbandingan dengan WASM dan Bahasa Lain

Dibandingkan dengan Lua dan LuaJIT, kita mungkin lebih familiar dengan beberapa bahasa lain, seperti JavaScript (Node.js), Python, Golang, Java, dll. Dengan membandingkan Lua/LuaJIT dengan bahasa-bahasa populer ini, kita dapat memahami lebih baik fitur dan keunggulan unik LuaJIT. Berikut adalah beberapa perbandingan singkat:

  • Sintaks Lua dirancang untuk non-insinyur perangkat lunak. Mirip dengan bahasa R, Lua juga memiliki indeks array yang dimulai dari 1, yang cocok untuk orang biasa.
  • Lua sangat cocok sebagai bahasa yang disematkan. Lua sendiri memiliki VM yang ringan, dan LuaJIT tetap ringan bahkan setelah menambahkan berbagai fitur dan optimasi. Akibatnya, ukuran LuaJIT tidak meningkat secara signifikan ketika diintegrasikan langsung ke dalam program C, tidak seperti lingkungan runtime besar seperti Node.js dan Python. Dengan demikian, Lua sebenarnya adalah pilihan yang paling banyak digunakan dan mainstream di antara semua bahasa yang disematkan.
  • Lua juga sangat cocok sebagai bahasa "perekat". Seperti JavaScript (Node.js) dan Python, Lua juga dapat menghubungkan berbagai pustaka dan kode dengan sangat baik. Namun, sedikit berbeda dari bahasa lain, Lua memiliki kopling yang lebih tinggi dengan ekosistem dasar, sehingga ekosistem Lua mungkin tidak universal di berbagai bidang.

WASM (Web Assembly) adalah teknologi lintas platform yang sedang berkembang. Teknologi ini, awalnya dirancang untuk melengkapi daripada menggantikan JavaScript, dapat mengkompilasi bahasa lain menjadi bytecode WASM dan menjalankan kode sebagai sandbox yang aman, membuat semakin banyak program mempertimbangkan menggunakan WASM sebagai platform yang disematkan atau "perekat". Meskipun demikian, Lua/LuaJIT masih memiliki banyak keunggulan dibandingkan WASM yang sedang berkembang:

  • Performa WASM terbatas dan tidak dapat mencapai tingkat assembly. Dalam skenario umum, WASM tentu lebih baik daripada Lua dalam hal performa, tetapi masih ada jarak antara WASM dan LuaJIT.
  • Efisiensi transmisi data antara WASM dan program host relatif rendah. Di sisi lain, LuaJIT dapat melakukan transmisi data yang efisien melalui FFI.

Mengapa Apache APISIX Memilih LuaJIT?

Meskipun banyak keunggulan LuaJIT telah dijelaskan di atas, Lua bukanlah bahasa yang populer atau pilihan populer bagi sebagian besar pengembang. Jadi, mengapa Apache APISIX, sebuah API gateway cloud-native di bawah Apache Foundation, memilih LuaJIT?

Sebagai API gateway cloud-native, Apache APISIX memiliki karakteristik yang dinamis, real-time, dan berperforma tinggi, menyediakan fungsi manajemen lalu lintas yang kaya seperti load balancing, upstream dinamis, canary release, degradasi layanan, autentikasi, observabilitas, dll. Kita dapat menggunakan Apache APISIX untuk menangani lalu lintas north-south tradisional, serta lalu lintas east-west antar layanan, dan juga dapat berfungsi sebagai Ingress controller untuk k8s.

Semua ini dibangun di atas stack teknologi NGINX dan LuaJIT yang dipilih oleh Apache APISIX.

Keunggulan Menggabungkan LuaJIT dengan NGINX

NGINX adalah web server berperforma tinggi yang terkenal yang berfungsi sebagai proxy HTTP, TCP/UDP dan reverse proxy.

Namun, dalam praktiknya, kita merasa kesal bahwa setiap kali kita memodifikasi file konfigurasi NGINX, kita perlu menggunakan perintah nginx -s reload untuk memuat ulang konfigurasi NGINX.

Selain itu, penggunaan perintah ini secara sering untuk memuat ulang konfigurasi dapat menyebabkan ketidakstabilan koneksi dan meningkatkan kemungkinan kehilangan bisnis. Dalam beberapa kasus, mekanisme pemuatan ulang konfigurasi NGINX juga dapat menyebabkan proses lama terlalu lama untuk direklamasi, memengaruhi operasi bisnis normal. Untuk analisis yang lebih komprehensif tentang topik ini, kami merekomendasikan membaca artikel Why NGINX's reload is not a hot reload?. Kami tidak akan membahas topik ini lebih lanjut di sini.

Salah satu tujuan Apache APISIX adalah untuk menyelesaikan masalah konfigurasi dinamis NGINX. Fleksibilitas tinggi, performa tinggi, dan penggunaan memori yang sangat rendah dari LuaJIT membuat ini mungkin. Mengambil rute yang paling umum sebagai contoh, Apache APISIX hanya mengkonfigurasi satu lokasi sebagai titik masuk utama dalam file konfigurasi NGINX, dan distribusi rute selanjutnya diselesaikan oleh modul distribusi rute APISIX, sehingga mencapai konfigurasi rute yang dinamis.

Untuk mencapai performa tinggi, Apache APISIX menggunakan algoritma pencocokan rute berbasis prefix tree yang ditulis dalam C, dan di atasnya, menyediakan antarmuka untuk Lua menggunakan FFI yang disediakan oleh LuaJIT. Fleksibilitas Lua juga memungkinkan modul distribusi rute Apache APISIX untuk dengan mudah mendukung pencocokan rute bawahan dari prefiks yang sama melalui ekspresi tertentu dan metode lainnya. Pada akhirnya, dengan menggantikan fungsi distribusi rute asli NGINX, Apache APISIX mencapai fungsionalitas konfigurasi dinamis dengan performa tinggi dan fleksibilitas. Untuk implementasi detail dari fitur ini, Anda dapat merujuk ke lua-resty-radixtree dan route.lua.

Selain rute, APISIX juga dapat memuat ulang fungsi seperti penyeimbangan, pemeriksaan kesehatan, konfigurasi node upstream, sertifikat server, dan plugin yang memperluas kemampuan APISIX tanpa perlu me-restart server.

Selain itu, selain mengembangkan plugin dan fitur lainnya menggunakan LuaJIT, Apache APISIX juga mendukung pengembangan plugin menggunakan berbagai bahasa seperti Java, Go, Node, Python, dan WASM. Ini sangat mengurangi ambang batas untuk pengembangan kustom Apache APISIX, menghasilkan ekosistem plugin yang kaya dan komunitas open-source yang aktif.

Prinsip dan Ekosistem Plugin Apache APISIX

Kesimpulan

LuaJIT adalah implementasi dari Lua, sebuah kompiler just-in-time.

Sebagai API gateway open-source yang dinamis, real-time, dan berperforma tinggi, Apache APISIX menyediakan fungsi manajemen lalu lintas yang kaya seperti load balancing, upstream dinamis, canary release, circuit breaker, autentikasi, dan observabilitas, berdasarkan performa tinggi dan fleksibilitas tinggi yang dibawa oleh NGINX dan LuaJIT.

Saat ini, Apache APISIX telah merilis versi baru, 3.x, yang mencakup lebih banyak integrasi dengan proyek open-source dan penyedia cloud, dukungan gRPC asli, opsi pengembangan plugin tambahan, dan dukungan service mesh. Bergabunglah dengan komunitas Apache APISIX untuk mempelajari lebih lanjut tentang penerapan LuaJIT dalam API gateway cloud-native.

Tags: