Pengetahuan tentang NGINX yang Digunakan dalam OpenResty

API7.ai

September 17, 2022

OpenResty (NGINX + Lua)

Melalui postingan sebelumnya, Anda telah memiliki pengetahuan umum tentang OpenResty. Dalam beberapa artikel berikutnya, saya akan membawa Anda melalui dua pilar utama OpenResty: NGINX dan LuaJIT, dan Anda dapat mempelajari OpenResty dengan lebih baik dengan menguasai dasar-dasar ini.

Hari ini saya akan mulai dengan NGINX, dan di sini saya hanya akan memperkenalkan beberapa dasar NGINX yang mungkin digunakan dalam OpenResty, yang hanya merupakan bagian kecil dari NGINX.

Mengenai konfigurasi, dalam pengembangan OpenResty, kita perlu memperhatikan poin-poin berikut.

  • Mengonfigurasi nginx.conf sesedikit mungkin.
  • Menghindari penggunaan kombinasi beberapa direktif seperti if, set, rewrite, dll.
  • Tidak menggunakan konfigurasi, variabel, dan modul NGINX ketika masalah dapat diselesaikan dengan kode Lua.

Metode-metode ini akan memaksimalkan keterbacaan, kemudahan pemeliharaan, dan kemampuan perluasan. Konfigurasi NGINX berikut adalah contoh buruk yang khas dalam menggunakan konfigurasi sebagai kode.

location ~ ^/mobile/(web/app.htm) { set $type $1; set $orig_args $args; if ( $http_user_Agent ~ "(iPhone|iPad|Android)" ) { rewrite ^/mobile/(.*) http://touch.foo.com/mobile/$1 last; } proxy_pass http://foo.com/$type?$orig_args; }

Ini adalah hal yang perlu kita hindari saat mengembangkan dengan OpenResty.

Konfigurasi NGINX

NGINX mengontrol perilakunya melalui file konfigurasi, yang dapat dianggap sebagai DSL sederhana. NGINX membaca konfigurasi saat proses dimulai dan memuatnya ke memori. Jika Anda memodifikasi file konfigurasi, Anda perlu me-restart atau me-reload NGINX dan menunggu hingga NGINX membaca file konfigurasi lagi agar konfigurasi baru berlaku. Hanya versi komersial NGINX yang menyediakan beberapa kemampuan dinamis ini saat runtime, dalam bentuk API.

Mari kita mulai dengan konfigurasi berikut, yang sangat sederhana.

worker_processes auto; pid logs/nginx.pid; error_log logs/error.log notice; worker_rlimit_nofile 65535; events { worker_connections 16384; } http { server { listen 80; listen 443 ssl; location / { proxy_pass https://foo.com; } } } stream { server { listen 53 udp; } }

Namun, bahkan konfigurasi sederhana ini melibatkan beberapa konsep dasar.

Pertama, setiap direktif memiliki konteks-nya sendiri, yang merupakan ruang lingkupnya dalam file konfigurasi NGINX.

Tingkat teratas adalah main, yang berisi beberapa instruksi yang tidak terkait dengan bisnis tertentu, seperti worker_processes, pid, dan error_log, yang semuanya merupakan bagian dari konteks main. Selain itu, ada hubungan hierarkis antara konteks. Misalnya, konteks location adalah server, konteks server adalah http, dan konteks http adalah main.

Direktif tidak dapat dijalankan dalam konteks yang salah. NGINX akan memeriksa apakah nginx.conf legal saat dimulai. Misalnya, jika kita mengubah listen 80; dari konteks server ke konteks main dan memulai layanan NGINX, kita akan melihat kesalahan seperti ini:

"listen" directive is not allowed here ......

Kedua, NGINX tidak hanya dapat menangani permintaan HTTP dan lalu lintas HTTPS, tetapi juga lalu lintas UDP dan TCP. L7 ada di HTTP dan L4 ada di Stream. Di OpenResty, lua-nginx-module dan stream-lua-nginx-module masing-masing sesuai dengan kedua hal ini.

Satu hal yang perlu diperhatikan di sini adalah bahwa OpenResty tidak mendukung semua fitur di NGINX, dan Anda perlu melihat versi OpenResty. Versi OpenResty konsisten dengan NGINX, sehingga mudah untuk mengidentifikasi.

Direktif konfigurasi yang terlibat dalam nginx.conf di atas ada dalam modul inti NGINX ngx_core_module, ngx_http_core_module, dan ngx_stream_core_module, yang dapat Anda klik untuk melihat dokumentasi spesifik.

Mode MASTER-WORKER

Setelah memahami file konfigurasi, mari kita lihat mode multi-proses NGINX (seperti yang ditunjukkan pada gambar di bawah). Seperti yang Anda lihat, saat NGINX dimulai, akan ada satu proses Master dan beberapa proses Worker (atau hanya satu proses Worker, tergantung pada bagaimana Anda mengonfigurasinya).

Mode Worker NGINX

Pertama-tama, proses Master, seperti namanya, memainkan peran "manajer" dan tidak bertanggung jawab untuk menangani permintaan dari klien. Ini mengelola proses Worker, termasuk menerima sinyal dari administrator dan memantau status Worker. Ketika proses Worker keluar secara tidak normal, proses Master akan memulai ulang proses Worker baru.

Proses Worker adalah "karyawan yang benar-benar bekerja" yang menangani permintaan dari klien. Mereka di-fork dari proses Master dan independen satu sama lain. Model multi-proses ini jauh lebih maju daripada model multi-thread Apache, tanpa penguncian lintas thread dan mudah untuk di-debug. Bahkan jika satu proses crash dan keluar, biasanya tidak memengaruhi pekerjaan proses Worker lainnya.

OpenResty menambahkan agen istimewa yang unik ke model Master-Worker NGINX. Proses ini tidak mendengarkan port apa pun dan memiliki hak istimewa yang sama dengan proses Master NGINX, sehingga dapat melakukan beberapa tugas yang memerlukan hak istimewa tinggi, seperti beberapa operasi penulisan ke file disk lokal.

Jika proses istimewa bekerja dengan mekanisme peningkatan biner panas NGINX, OpenResty dapat mengimplementasikan seluruh peningkatan biner sendiri secara langsung tanpa bergantung pada program eksternal.

Mengurangi ketergantungan pada program eksternal dan mencoba menyelesaikan masalah dalam proses OpenResty memfasilitasi penyebaran, mengurangi biaya operasi dan pemeliharaan, dan mengurangi kemungkinan kesalahan program. Proses istimewa dan ngx.pipe di OpenResty semuanya bertujuan untuk ini.

Fase Eksekusi

Fase eksekusi juga merupakan fitur penting NGINX dan terkait erat dengan implementasi spesifik OpenResty. NGINX memiliki 11 fase eksekusi, yang dapat kita lihat dalam kode sumber ngx_http_core_module.h:

typedef enum { NGX_HTTP_POST_READ_PHASE = 0, NGX_HTTP_SERVER_REWRITE_PHASE, NGX_HTTP_FIND_CONFIG_PHASE, NGX_HTTP_REWRITE_PHASE, NGX_HTTP_POST_REWRITE_PHASE, NGX_HTTP_PREACCESS_PHASE, NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, NGX_HTTP_PRECONTENT_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE } ngx_http_phases;

Jika Anda ingin mempelajari lebih lanjut tentang peran dari 11 fase ini, Anda dapat membaca dokumentasi NGINX sehingga saya tidak akan membahasnya di sini.

Kebetulan, OpenResty juga memiliki 11 direktif *_by_lua yang terkait dengan fase NGINX, seperti yang ditunjukkan pada gambar di bawah (dari dokumentasi lua-nginx-module).

Urutan Direktif Modul Lua NGINX

init_by_lua hanya dieksekusi saat proses Master dibuat, dan init_worker_by_lua hanya dieksekusi saat setiap proses Worker dibuat. Perintah *_by_lua lainnya dipicu oleh permintaan klien dan dieksekusi berulang kali.

Jadi selama fase init_by_lua, kita dapat memuat modul Lua dan data baca-saja publik untuk memanfaatkan fitur COW (copy on write) OS untuk menghemat memori.

Sebagian besar operasi dapat dilakukan di dalam content_by_lua, tetapi saya akan merekomendasikan untuk memisahkannya sesuai dengan fungsi yang berbeda, seperti berikut.

  • set_by_lua: mengatur variabel.
  • rewrite_by_lua: penerusan, pengalihan, dll.
  • access_by_lua: akses, izin, dll.
  • content_by_lua: menghasilkan konten kembali.
  • header_filter_by_lua: pemrosesan filter header respons.
  • body_filter_by_lua: pemrosesan filter tubuh respons.
  • log_by_lua: pencatatan log.

Mari saya beri contoh untuk menunjukkan manfaat dari pemisahan ini. Mari kita asumsikan bahwa banyak API teks biasa disediakan secara eksternal, dan sekarang kita perlu menambahkan logika enkripsi dan dekripsi kustom. Jadi, apakah kita perlu mengubah kode semua API?

location /mixed { content_by_lua '...'; }

Tentu saja tidak. Menggunakan fitur fase, kita dapat mendekripsi di fase access dan mengenkripsi di fase body filter tanpa membuat perubahan apa pun pada kode di fase content asli.

location /mixed { access_by_lua '...'; content_by_lua '...'; body_filter_by_lua '...'; }

Meningkatkan Biner NGINX Secara Langsung

Akhirnya, izinkan saya menjelaskan secara singkat tentang meningkatkan biner NGINX secara langsung. Kita tahu bahwa setelah Anda memodifikasi file konfigurasi NGINX, Anda perlu me-reload-nya agar berfungsi. Tetapi ketika NGINX meningkatkan dirinya sendiri, itu dapat dilakukan secara langsung. Ini mungkin terlihat seperti meletakkan kereta sebelum kuda, tetapi dapat dimengerti mengingat NGINX dimulai dengan load balancing statis tradisional, proxy terbalik, dan caching file.

Peningkatan panas dilakukan dengan mengirim sinyal USR2 dan WINCH ke proses Master lama. Untuk kedua langkah ini, yang pertama memulai proses Master baru; yang kedua mematikan proses Worker secara bertahap.

Setelah kedua langkah ini, proses Master baru dan Worker baru dimulai. Pada titik ini, proses Master lama tidak keluar. Alasan untuk tidak keluar sederhana: jika Anda perlu kembali, Anda masih dapat mengirim sinyal HUP ke Master lama. Tentu saja, jika Anda telah menentukan bahwa Anda tidak perlu kembali, Anda dapat mengirim sinyal KILL ke Master lama untuk keluar.

Itu saja, dan peningkatan biner NGINX secara langsung selesai.

Jika Anda ingin mengetahui informasi lebih rinci tentang ini, Anda dapat memeriksa dokumentasi resmi untuk terus belajar.

Ringkasan

Secara umum, apa yang Anda gunakan dalam OpenResty adalah dasar-dasar NGINX, terutama terkait dengan konfigurasi, proses master-slave, fase eksekusi, dll. Hal-hal lain yang dapat diselesaikan dengan kode Lua diselesaikan dengan kode sebanyak mungkin, daripada menggunakan modul dan konfigurasi NGINX, yang merupakan perubahan dalam pemikiran saat mempelajari OpenResty.

Terakhir, saya telah meninggalkan Anda dengan pertanyaan terbuka: Nginx secara resmi mendukung NJS, yang berarti Anda dapat menulis JS untuk mengontrol beberapa logika NGINX, mirip dengan OpenResty. Apa pendapat Anda tentang ini? Silakan bagikan artikel ini.