1. Tổng quan
Nếu bạn đang làm việc với Lua Nginx Module hoặc OpenResty, chắc hẳn bạn đã từng nghe đến các chỉ thị như lua_package_path
, lua_package_cpath
, init_by_lua_block
và lua_shared_dict
. Đây là những công cụ mạnh mẽ giúp bạn tích hợp Lua vào Nginx để xây dựng các ứng dụng web hiệu suất cao. Trong bài viết này, chúng ta sẽ cùng tìm hiểu chi tiết về cách sử dụng và ý nghĩa của từng chỉ thị này.

Dưới đây là một sơ đồ minh họa logic hoạt động của Lua trong Nginx.
+-------------------+
| Nginx Server |
+-------------------+
|
v
+-------------------+
| Lua Nginx Module |
+-------------------+
|
v
+-----------------------------+
| init_by_lua_block |
| (Chạy khi Nginx khởi động) |
+-----------------------------+
|
v
+-----------------------------+
| lua_shared_dict |
| (Bộ nhớ chia sẻ giữa các |
| worker processes) |
+-----------------------------+
|
v
+-----------------------------+
| Request Handling |
| (Xử lý từng request) |
+-----------------------------+
|
v
+-----------------------------+
| Lua Code Execution |
| (Sử dụng require để tải |
| module từ lua_package_path|
| hoặc lua_package_cpath) |
+-----------------------------+
|
v
+-----------------------------+
| Response Generation |
| (Trả về kết quả cho client)|
+-----------------------------+
Giải thích sơ đồ:
- Nginx Server: Là điểm bắt đầu, nơi các request từ client được gửi đến.
- Lua Nginx Module: Module này tích hợp Lua vào Nginx, cho phép bạn chạy mã Lua trong các giai đoạn khác nhau của Nginx.
- init_by_lua_block: Được thực thi một lần khi Nginx khởi động hoặc reload, dùng để khởi tạo các cấu hình hoặc module Lua.
- lua_shared_dict: Bộ nhớ chia sẻ được sử dụng để lưu trữ dữ liệu toàn cục, có thể truy cập bởi tất cả các worker processes.
- Request Handling: Khi một request đến, Nginx sẽ chuyển nó đến Lua để xử lý.
- Lua Code Execution: Lua thực thi mã, sử dụng
require
để tải các module từlua_package_path
hoặclua_package_cpath
. - Response Generation: Sau khi xử lý xong, Lua trả về kết quả cho Nginx và Nginx gửi phản hồi đến client.
2. lua_package_path
và lua_package_cpath
Hai chỉ thị này được sử dụng để cấu hình đường dẫn tìm kiếm các module Lua trong Nginx. Chúng khác nhau ở loại module mà chúng tìm kiếm:
lua_package_path
- Mục đích: Xác định đường dẫn tìm kiếm các module Lua thuần túy (pure Lua modules), tức là các file Lua có phần mở rộng
.lua
. - Cách hoạt động: Khi bạn sử dụng
require
trong Lua để tải một module, Nginx sẽ tìm kiếm file.lua
trong các đường dẫn được chỉ định tronglua_package_path
. - Cú pháp đường dẫn:
?
: Được thay thế bằng tên module mà bạn yêu cầu trongrequire
.;;
: Kết thúc danh sách đường dẫn.
Ví dụ:
lua_package_path "/usr/local/share/lua/5.1/?.lua;/usr/local/share/lualib/?.lua;;";
- Nếu bạn gọi
require("example")
, Nginx sẽ tìm file: /usr/local/share/lua/5.1/example.lua
/usr/local/share/lualib/example.lua
lua_package_cpath
- Mục đích: Xác định đường dẫn tìm kiếm các module Lua được biên dịch (compiled Lua modules), tức là các file thư viện chia sẻ (shared libraries) có phần mở rộng
.so
. - Cách hoạt động: Khi bạn sử dụng
require
để tải một module biên dịch, Nginx sẽ tìm kiếm file.so
trong các đường dẫn được chỉ định tronglua_package_cpath
. - Cú pháp đường dẫn:
?
: Được thay thế bằng tên module mà bạn yêu cầu trongrequire
.;;
: Kết thúc danh sách đường dẫn.
Ví dụ:
lua_package_cpath "/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lualib/?.so;;";
- Nếu bạn gọi
require("example")
, Nginx sẽ tìm file: /usr/local/lib/lua/5.1/example.so
/usr/local/lib/lualib/example.so
Tóm tắt sự khác biệt
Chỉ thị | Tìm kiếm module | Phần mở rộng file | Ví dụ đường dẫn |
---|---|---|---|
lua_package_path | Module Lua thuần túy | .lua | /usr/local/share/lua/5.1/?.lua |
lua_package_cpath | Module Lua được biên dịch | .so | /usr/local/lib/lua/5.1/?.so |
Khi nào sử dụng?
lua_package_path
: Khi bạn sử dụng các module Lua thuần túy (viết bằng Lua).lua_package_cpath
: Khi bạn sử dụng các module Lua được biên dịch (thường là các thư viện C được bọc để sử dụng trong Lua).
Nếu chương trình của bạn chỉ sử dụng các module Lua thuần túy, bạn chỉ cần cấu hình lua_package_path
. Ngược lại, nếu sử dụng các module Lua được biên dịch, bạn cần cấu hình lua_package_cpath
. Trong trường hợp sử dụng cả hai loại module, bạn nên cấu hình cả hai để đảm bảo chương trình hoạt động chính xác.
3. init_by_lua_block
init_by_lua_block
là một chỉ thị được cung cấp bởi Lua Nginx Module (hoặc OpenResty). Đây là block Lua được sử dụng để chạy mã Lua một lần duy nhất khi Nginx khởi động hoặc reload.
Tác dụng của init_by_lua_block
- Mục đích: Thực hiện các tác vụ khởi tạo toàn cục, chẳng hạn như:
- Tải các module Lua cần thiết.
- Khởi tạo các cấu trúc dữ liệu hoặc biến toàn cục.
- Thiết lập các cấu hình hoặc môi trường cần thiết cho Lua.
- Thời điểm thực thi: Mã trong
init_by_lua_block
được thực thi trước khi Nginx bắt đầu xử lý bất kỳ request nào.
Ví dụ
init_by_lua_block {
require "resty.core" -- Tải module "resty.core" để tối ưu hóa hiệu suất Lua
local lrucache = require "resty.lrucache" -- Tải module LRU cache
collectgarbage("collect") -- Chạy garbage collector để dọn dẹp bộ nhớ
}
Phân tích:
require "resty.core"
: Tải moduleresty.core
, giúp tối ưu hóa hiệu suất Lua bằng cách sử dụng các API cấp thấp của Nginx.local lrucache = require "resty.lrucache"
: Tải moduleresty.lrucache
, một thư viện Lua để quản lý bộ nhớ cache theo thuật toán Least Recently Used (LRU).collectgarbage("collect")
: Chạy garbage collector của Lua để dọn dẹp bộ nhớ không còn được sử dụng.
Khi nào sử dụng init_by_lua_block
?
- Khi cần khởi tạo các module hoặc thư viện Lua.
- Khi cần thiết lập các cấu hình toàn cục hoặc chuẩn bị môi trường Lua.
- Không sử dụng cho logic liên quan đến request, vì block này chỉ chạy khi Nginx khởi động hoặc reload.
4. lua_shared_dict
lua_shared_dict
là một chỉ thị được sử dụng để khai báo một shared dictionary (bộ nhớ chia sẻ) trong Nginx. Shared dictionary này được lưu trữ trong RAM và có thể được truy cập bởi tất cả các worker processes của Nginx.
Đặc điểm của lua_shared_dict
- Lưu trữ dữ liệu trong bộ nhớ dùng chung:
- Dữ liệu được lưu trong RAM và có thể được truy cập bởi tất cả các worker processes.
- Hữu ích để lưu trữ cache, thống kê, hoặc các biến trạng thái toàn cục.
Kích thước cố định:
- Bạn cần chỉ định kích thước của shared dictionary (ví dụ:
100m
nghĩa là 100 MB). - Nếu dữ liệu vượt quá kích thước này, các mục cũ hơn sẽ bị loại bỏ theo cơ chế LRU (Least Recently Used).
Ví dụ
lua_shared_dict metrics_store 100m;
lua_shared_dict request_metrics 100m;
metrics_store
: Shared dictionary với kích thước 100 MB, dùng để lưu trữ các thông tin liên quan đến metric.request_metrics
: Shared dictionary với kích thước 100 MB, dùng để lưu trữ các thông tin thống kê request.
Sử dụng lua_shared_dict
trong Lua
-- Lưu trữ một giá trị trong shared dictionary
local metrics = ngx.shared.metrics_store
metrics:set("total_requests", 100)
-- Lấy giá trị từ shared dictionary
local total_requests = metrics:get("total_requests")
ngx.say("Total requests: ", total_requests)
-- Tăng giá trị trong shared dictionary
metrics:incr("total_requests", 1)
-- Xóa một giá trị trong shared dictionary
metrics:delete("total_requests")
Khi nào sử dụng lua_shared_dict
?
- Khi cần lưu trữ dữ liệu toàn cục, như cache hoặc thống kê.
- Khi cần chia sẻ dữ liệu giữa các worker processes.
- Khi cần tối ưu hóa hiệu suất bằng cách lưu trữ dữ liệu trong RAM.
Lưu ý
- Shared dictionary có kích thước cố định, không phù hợp để lưu trữ dữ liệu lớn.
- Không hỗ trợ các thao tác đồng thời phức tạp. Nếu cần, bạn có thể sử dụng Redis để thay thế.
5. Kết luận
Các chỉ thị lua_package_path
, lua_package_cpath
, init_by_lua_block
và lua_shared_dict
là những công cụ mạnh mẽ trong Lua Nginx Module. Chúng giúp bạn dễ dàng tích hợp Lua vào Nginx để xây dựng các ứng dụng web hiệu suất cao. Hy vọng bài viết này đã giúp bạn hiểu rõ hơn về cách sử dụng chúng!