Memulai dengan Lua

API7.ai

September 23, 2022

OpenResty (NGINX + Lua)

Setelah memahami dasar-dasar NGINX secara umum, kita akan mempelajari Lua lebih lanjut. Lua adalah bahasa pemrograman yang digunakan dalam OpenResty, dan penting untuk menguasai sintaks dasarnya.

Lua adalah bahasa skrip yang kecil dan halus, lahir di laboratorium universitas di Brasil, yang namanya berarti "bulan indah" dalam bahasa Portugis. NGINX lahir di Rusia, Lua di Brasil, dan OpenResty di Cina, negara asal penulis. Menariknya, ketiga teknologi open source yang sama-sama cerdas ini berasal dari negara-negara BRICS, bukan dari Eropa atau Amerika.

Lua dirancang untuk memposisikan dirinya sebagai bahasa perekat yang sederhana, ringan, dan dapat disematkan, yang tidak mengambil jalur besar dan berani. Meskipun Anda mungkin tidak menulis kode Lua secara langsung dalam pekerjaan sehari-hari, Lua digunakan secara luas. Banyak game online, seperti World of Warcraft, menggunakan Lua untuk menulis plugin; Redis, basis data key-value, memiliki Lua yang tertanam untuk mengontrol logika.

Di sisi lain, meskipun pustaka Lua relatif sederhana, ia dapat dengan mudah memanggil pustaka bahasa pemrograman C, dan banyak kode bahasa pemrograman C yang matang dapat digunakan untuknya. Misalnya, di OpenResty, Anda akan sering perlu memanggil fungsi bahasa pemrograman C dari NGINX dan OpenSSL, berkat kemampuan Lua dan LuaJIT untuk mencapai pustaka C dengan mudah.

Di sini, saya akan membawa Anda melalui pengenalan cepat dengan tipe data dan sintaks Lua sehingga Anda dapat mempelajari OpenResty dengan lebih lancar nantinya.

Lingkungan dan hello world

Kita tidak perlu menginstal lingkungan Lua 5.1 standar secara khusus karena OpenResty tidak lagi mendukung Lua standar, hanya LuaJIT. Perhatikan bahwa sintaks Lua yang saya sajikan di sini juga kompatibel dengan LuaJIT dan tidak berdasarkan Lua 5.3 terbaru.

Anda dapat menemukan direktori dan file executable LuaJIT di direktori instalasi OpenResty. Saya berada di lingkungan Mac dan menggunakan brew untuk menginstal OpenResty, jadi jalur lokal Anda kemungkinan akan berbeda dengan berikut ini.

$ ll /usr/local/Cellar/openresty/1.13.6.2/luajit/bin/luajit lrwxr-xr-x 1 ming admin 18B 4 2 14:54 /usr/local/Cellar/openresty/1.13.6.2/luajit/bin/luajit -> luajit-2.1.0-beta3

Anda juga dapat menemukannya di direktori file executable sistem.

$ which luajit /usr/local/bin/luajit

Periksa versi LuaJIT.

$ luajit -v LuaJIT 2.1.0-beta2 -- Copyright (C) 2005-2017 Mike Pall. http://luajit.org/

Setelah memeriksa informasi ini, Anda dapat membuat file 1.lua baru dan menggunakan LuaJIT untuk menjalankan kode hello world.

$ cat 1.lua print("hello world") $ luajit 1.lua hello world

Tentu saja, Anda juga dapat menggunakan resty untuk menjalankannya langsung, mengetahui bahwa pada akhirnya itu juga dieksekusi dengan LuaJIT.

$ resty -e 'print("hello world")' hello world

Kedua cara menjalankan hello world ini mungkin. Saya lebih suka pendekatan resty karena banyak kode OpenResty juga dijalankan oleh resty nantinya.

Tipe Data

Tidak banyak tipe data dalam Lua, dan Anda dapat mengembalikan jenis nilai dengan fungsi type, seperti berikut.

$ resty -e 'print(type("hello world")) print(type(print)) print(type(true)) print(type(360.0)) print(type({})) print(type(nil)) '

Informasi berikut akan dicetak.

string function boolean number table nil

Ini adalah tipe data dasar dalam Lua. Mari kita perkenalkan secara singkat.

String

Dalam Lua, string adalah nilai yang tidak dapat diubah. Jika Anda ingin memodifikasi string, Anda harus membuat yang baru. Pendekatan ini memiliki kelebihan dan kekurangan: kelebihannya adalah bahwa bahkan jika string yang sama muncul berkali-kali, hanya ada satu salinan dalam memori, tetapi kekurangannya juga jelas: jika Anda ingin memodifikasi dan menyambung string, Anda membuat banyak string tambahan yang tidak perlu.

Mari kita ambil contoh untuk menggambarkan kekurangan ini. Dalam Lua, kita menggunakan dua tanda titik untuk menunjukkan penambahan string. Kode berikut menyambungkan angka 1 hingga 10 sebagai string.

$ resty -e 'local s = "" for i = 1, 10 do s = s .. tostring(i) end print(s)'

Di sini kita melakukan loop sepuluh kali, dan hanya hasil terakhir yang kita butuhkan; sembilan string baru di antaranya tidak berguna. Mereka tidak hanya memakan ruang ekstra tetapi juga mengonsumsi operasi CPU yang tidak perlu.

Tentu saja, kita akan memiliki solusi untuk ini nanti di bagian optimasi kinerja.

Juga, dalam Lua, Anda memiliki tiga cara untuk mengekspresikan string: tanda kutip tunggal, tanda kutip ganda, dan tanda kurung panjang ([[]]). Dua yang pertama relatif mudah dipahami dan umumnya digunakan dalam bahasa lain, jadi apa gunanya tanda kurung panjang?

Mari kita lihat contoh konkret.

$ resty -e 'print([[string has \n and \r]])' string has \n and \r

Anda dapat melihat bahwa string dalam tanda kurung panjang tidak di-escape dengan cara apa pun.

Anda mungkin bertanya: Bagaimana jika string di atas termasuk tanda kurung panjang? Jawabannya sederhana: tambahkan satu atau lebih simbol = di tengah tanda kurung panjang.

$ resty -e 'print([=[ string has a [[]]. ]=])' string has a [[]].

Boolean

Ini adalah yang sederhana, true dan false. Dalam Lua, hanya nil dan false yang salah; yang lainnya benar, termasuk 0 dan string kosong. Kita dapat memverifikasi ini dengan kode berikut.

$ resty -e 'local a = 0 if a then print("true") end a = "" if a then print("true") end'

Jenis penilaian ini tidak konsisten dengan banyak bahasa pengembangan umum, jadi untuk menghindari kesalahan dalam hal seperti ini, Anda dapat menulis objek perbandingan secara eksplisit, seperti berikut.

$ resty -e 'local a = 0 if a == false then print("true") end '

Number

Tipe number Lua diimplementasikan sebagai angka floating-point presisi ganda. Perlu disebutkan bahwa LuaJIT mendukung mode dual-number, yang berarti LuaJIT menyimpan bilangan bulat sebagai bilangan bulat dan angka floating-point sebagai angka floating-point presisi ganda, tergantung pada konteksnya.

Selain itu, LuaJIT mendukung long-long integers untuk bilangan bulat besar, seperti contoh berikut.

$ resty -e 'print(9223372036854775807LL - 1)' 9223372036854775806LL

Function

Fungsi adalah warga negara kelas satu dalam Lua, dan Anda dapat menyimpan fungsi dalam variabel atau menggunakannya sebagai referensi masuk dan keluar ke fungsi lain.

Misalnya, dua deklarasi fungsi berikut persis sama.

function foo() end

dan

foo = function () end

Table

Table adalah satu-satunya struktur data dalam Lua dan secara alami sangat penting, jadi saya akan membahasnya secara khusus nanti. Kita bisa mulai dengan melihat contoh kode sederhana.

$ resty -e 'local color = {first = "red"} print(color["first"])' red

Nilai Null

Dalam Lua, nilai null adalah nil. Jika Anda mendefinisikan variabel tetapi tidak memberikan nilai, nilai defaultnya adalah nil.

$ resty -e 'local a print(type(a))' nil

Ketika Anda masuk ke sistem OpenResty, Anda akan menemukan banyak nilai null, seperti ngx.null, dan sebagainya. Kita akan membahasnya lebih lanjut nanti.

Tipe data Lua, saya akan memperkenalkan sebanyak ini, pertama-tama memberikan Anda dasar. Kita akan terus mempelajari apa yang perlu Anda kuasai nanti dalam artikel. Belajar melalui praktik dan penggunaan selalu cara paling nyaman untuk menyerap pengetahuan baru.

Pustaka Standar Umum

Seringkali, mempelajari bahasa sebenarnya adalah mempelajari pustaka standarnya.

Lua relatif kecil dan tidak memiliki banyak pustaka standar yang tertanam. Juga, dalam lingkungan OpenResty, pustaka standar Lua memiliki prioritas yang sangat rendah. Untuk fungsi yang sama, saya merekomendasikan menggunakan API OpenResty terlebih dahulu, fungsi pustaka LuaJIT, dan fungsi Lua biasa.

API OpenResty > fungsi pustaka LuaJIT > fungsi Lua standar adalah prioritas yang akan disebutkan berulang kali dalam hal kegunaan dan kinerja.

Namun, meskipun demikian, kita akan tetap menggunakan beberapa pustaka Lua dalam proyek aktual kita. Di sini, saya telah memilih beberapa pustaka standar yang lebih umum digunakan untuk memperkenalkannya, dan jika Anda ingin tahu lebih banyak, Anda dapat memeriksa dokumentasi resmi Lua.

Pustaka String

Manipulasi string adalah apa yang sering kita gunakan dan di mana jebakan terbesar berada.

Satu aturan sederhana adalah jika regex terlibat, pastikan untuk menggunakan ngx.re.* yang disediakan oleh OpenResty untuk menyelesaikannya, bukan string.* dari Lua. Ini karena regex Lua unik dan tidak sesuai dengan spesifikasi PCRE, dan saya yakin sebagian besar insinyur tidak akan bisa memainkannya.

Salah satu fungsi pustaka string yang paling umum digunakan adalah string.byte(s [, i [, j ]]), yang mengembalikan kode ASCII yang sesuai dengan karakter s[i], s[i + 1], s[i + 2], ------, s[j]. Nilai default i adalah 1, byte pertama, dan nilai default j adalah i.

Mari kita lihat contoh kode.

$ resty -e 'print(string.byte("abc", 1, 3)) print(string.byte("abc", 3)) -- Parameter ketiga hilang, parameter ketiga sama dengan yang kedua secara default, yaitu 3 print(string.byte("abc")) -- Parameter kedua dan ketiga hilang, keduanya default ke 1 '

Outputnya adalah:

979899 99 97

Pustaka Table

Dalam konteks OpenResty, saya tidak merekomendasikan menggunakan sebagian besar pustaka table yang disertakan dengan Lua, kecuali beberapa fungsi seperti table.concat dan table.sort. Mengenai detailnya, kita akan membahasnya di bab LuaJIT.

Di sini saya akan menyebutkan table.concat secara singkat. table.concat umumnya digunakan dalam skenario penggabungan string, seperti contoh di bawah ini. Ini dapat menghindari pembuatan banyak string yang tidak berguna.

$ resty -e 'local a = {"A", "b", "C"} print(table.concat(a))'

Pustaka Matematika

Pustaka matematika Lua terdiri dari serangkaian fungsi matematika standar. Pengenalan pustaka matematika memperkaya bahasa pemrograman Lua dan memudahkan penulisan program.

Dalam proyek OpenResty, kita jarang menggunakan Lua untuk melakukan operasi matematika. Namun, dua fungsi yang terkait dengan angka acak, math.random() dan math.randomseed(), sering digunakan, seperti kode berikut, yang dapat menghasilkan dua angka acak dalam rentang yang ditentukan.

$ resty -e 'math.randomseed (os.time()) print(math.random()) print(math.random(100))'

Variabel Dummy

Setelah memahami pustaka standar bersama ini, mari kita pelajari konsep baru - variabel dummy.

Bayangkan skenario di mana fungsi mengembalikan beberapa nilai, beberapa di antaranya tidak kita butuhkan, jadi bagaimana kita harus menerima nilai-nilai ini?

Saya tidak tahu bagaimana perasaan Anda tentang ini, tetapi bagi saya, setidaknya, itu menyiksa untuk mencoba memberikan nama yang bermakna untuk variabel yang tidak digunakan ini.

Untungnya, Lua memiliki solusi sempurna untuk ini, menyediakan konsep variabel dummy yang secara konvensional dinamai dengan garis bawah untuk membuang nilai yang tidak diperlukan dan berfungsi sebagai pengganti.

Mari kita ambil fungsi pustaka standar string.find sebagai contoh untuk melihat penggunaan variabel dummy. Fungsi pustaka normal ini mengembalikan dua nilai yang masing-masing mewakili subskrip awal dan akhir.

Jika kita hanya perlu mendapatkan subskrip awal, cukup sederhana untuk mendeklarasikan variabel untuk menerima nilai kembalian string.find sebagai berikut.

$ resty -e 'local start = string.find("hello", "he") print(start)' 1

Tetapi jika Anda hanya ingin mendapatkan subskrip akhir, maka Anda harus menggunakan variabel dummy

$ resty -e 'local _, end_pos = string.find("hello", "he") print(end_pos)' 2

Selain digunakan dalam nilai kembalian, variabel dummy sering digunakan dalam loop, seperti contoh berikut.

$ resty -e 'for _, v in ipairs({4,5,6}) do print(v) end' 4 5 6

Dan ketika ada beberapa nilai kembalian yang harus diabaikan, Anda dapat menggunakan kembali variabel dummy yang sama. Saya tidak akan memberikan contoh di sini. Bisakah Anda mencoba menulis kode contoh seperti ini sendiri? Anda dipersilakan untuk memposting kode di bagian komentar untuk berbagi dan bertukar dengan saya.

Ringkasan

Hari ini, kita telah melihat sekilas struktur data dan sintaks Lua standar, dan saya yakin Anda telah mendapatkan gambaran pertama tentang bahasa yang sederhana dan ringkas ini. Dalam pelajaran berikutnya, saya akan membawa Anda melalui hubungan antara Lua dan LuaJIT, dengan LuaJIT menjadi fokus utama OpenResty dan layak untuk digali lebih dalam.

Terakhir, saya ingin meninggalkan Anda dengan satu pertanyaan yang merenungkan.

Ingat kode yang Anda pelajari dalam posting ini ketika kita membahas pustaka matematika? Itu menghasilkan dua angka acak dalam rentang yang ditentukan.

$ resty -e 'math.randomseed (os.time()) print(math.random()) print(math.random(100))'

Namun, Anda mungkin telah memperhatikan bahwa kode tersebut di-seed dengan stempel waktu saat ini. Apakah ada masalah dengan pendekatan ini? Dan bagaimana seharusnya kita menghasilkan seed yang baik? Seringkali, angka acak yang kita kembangkan tidak acak dan memiliki risiko keamanan yang besar.

Silakan bagikan pendapat Anda dengan kami, dan juga silakan bagikan posting ini kepada rekan dan teman Anda. Mari berkomunikasi dan meningkatkan bersama.