ngx_http_lua_module - ฝังพลังของ Lua ลงในเซิร์ฟเวอร์ Nginx HTTP
โมดูลนี้เป็นองค์ประกอบหลักของ OpenResty หากคุณใช้โมดูลนี้ แสดงว่าคุณใช้งาน OpenResty เป็นหลัก :)
โมดูลนี้ไม่ได้แจกจ่ายกับแหล่ง Nginx ดูคำแนะนำในการติดตั้ง
พร้อมผลิต.
เอกสารนี้อธิบาย ngx_lua v0.10.25 ซึ่งเผยแพร่เมื่อวันที่ 19 มิถุนายน 2023
วิดีโอ YouTube "ตัวอย่าง Hello World HTTP พร้อม OpenResty/Lua"
วิดีโอ YouTube "เขียนโมดูล Lua ของคุณเองในแอปพลิเคชัน OpenResty/Nginx"
วิดีโอ YouTube "การสาธิตยูทิลิตี้บรรทัดคำสั่ง Resty ของ OpenResty"
วิดีโอ YouTube "วัดเวลาดำเนินการของรหัส Lua อย่างถูกต้องใน OpenResty"
วิดีโอ YouTube "คอมไพล์โมดูล Lua ล่วงหน้าลงใน LuaJIT Bytecode เพื่อเร่งความเร็วการเริ่มต้น OpenResty"
คุณสามารถสมัครรับข้อมูลช่อง YouTube อย่างเป็นทางการของเรา OpenResty
กลับไปที่สารบบ
# set search paths for pure Lua external libraries (';;' is the default path):
lua_package_path '/foo/bar/?.lua;/blah/?.lua;;' ;
# set search paths for Lua external libraries written in C (can also use ';;'):
lua_package_cpath '/bar/baz/?.so;/blah/blah/?.so;;' ;
server {
location /lua_content {
# MIME type determined by default_type:
default_type 'text/plain' ;
content_by_lua_block {
ngx.say('Hello,world!')
}
}
location /nginx_var {
# MIME type determined by default_type:
default_type 'text/plain' ;
# try access /nginx_var?a=hello,world
content_by_lua_block {
ngx.say(ngx.var.arg_a)
}
}
location = /request_body {
client_max_body_size 50k ;
client_body_buffer_size 50k ;
content_by_lua_block {
ngx.req.read_body() -- explicitly read the req body
local data = ngx.req.get_body_data()
if data then
ngx.say(" body data: ")
ngx.print(data)
return
end
-- body may get buffered in a temp file:
local file = ngx.req.get_body_file()
if file then
ngx.say(" body is in file ", file)
else
ngx.say(" no body found ")
end
}
}
# transparent non-blocking I/O in Lua via subrequests
# (well, a better way is to use cosockets)
location = /lua {
# MIME type determined by default_type:
default_type 'text/plain';
content_by_lua_block {
local res = ngx.location.capture(" /some_other_location ")
if res then
ngx.say(" status: ", res.status)
ngx.say(" body: ")
ngx.print(res.body)
end
}
}
location = /foo {
rewrite_by_lua_block {
res = ngx.location.capture(" /memc ",
{ args = { cmd = " incr ", key = ngx.var.uri } }
)
}
proxy_pass http://blah.blah.com;
}
location = /mixed {
rewrite_by_lua_file /path/to/rewrite.lua;
access_by_lua_file /path/to/access.lua;
content_by_lua_file /path/to/content.lua;
}
# use nginx var in code path
# CAUTION: contents in nginx var must be carefully filtered,
# otherwise there'll be great security risk!
location ~ ^/app/([-_a-zA-Z0-9/]+) {
set $path $1 ;
content_by_lua_file /path/to/lua/app/root/ $path .lua;
}
location / {
client_max_body_size 100k;
client_body_buffer_size 100k;
access_by_lua_block {
-- check the client IP address is in our black list
if ngx.var.remote_addr == " 132.5.72.3 " then
ngx.exit(ngx.HTTP_FORBIDDEN)
end
-- check if the URI contains bad words
if ngx.var.uri and
string.match(ngx.var.request_body, " evil ")
then
return ngx.redirect(" /terms_of_use.html ")
end
-- tests passed
}
# proxy_pass/fastcgi_pass/etc settings
}
}
กลับไปที่สารบบ
โมดูลนี้ฝัง LuaJIT 2.0/2.1 ลงใน Nginx มันเป็นองค์ประกอบหลักของ OpenResty หากคุณใช้โมดูลนี้ แสดงว่าคุณใช้งาน OpenResty เป็นหลัก
ตั้งแต่เวอร์ชัน v0.10.16
ของโมดูลนี้ ระบบไม่รองรับล่าม Lua มาตรฐาน (หรือที่เรียกว่า "PUC-Rio Lua") อีกต่อไป เอกสารนี้ใช้คำว่า "Lua" และ "LuaJIT" แทนกันเพื่ออ้างถึงล่าม LuaJIT
ด้วยการใช้ประโยชน์จากคำขอย่อยของ Nginx โมดูลนี้จึงอนุญาตให้รวมเธรด Lua อันทรงพลัง (เรียกว่า Lua "coroutines") เข้ากับโมเดลเหตุการณ์ Nginx
ซึ่งแตกต่างจาก mod_lua ของ Apache และ mod_magnet ของ Lighttpd โค้ด Lua ที่ดำเนินการโดยใช้โมดูลนี้สามารถ ไม่มีการบล็อกการรับส่งข้อมูลเครือข่ายได้ 100% ตราบใดที่ Nginx API สำหรับ Lua ที่ได้รับจากโมดูลนี้ใช้เพื่อจัดการคำขอไปยังบริการอัปสตรีม เช่น MySQL, PostgreSQL, Memcached , Redis หรือบริการเว็บ HTTP อัปสตรีม
อย่างน้อยสามารถใช้ไลบรารี Lua และโมดูล Nginx ต่อไปนี้กับโมดูลนี้ได้:
โมดูล Nginx เกือบทุกตัวสามารถใช้กับโมดูล ngx_lua นี้โดยใช้ ngx.location.capture หรือ ngx.location.capture_multi แต่ขอแนะนำให้ใช้ไลบรารี lua-resty-*
เหล่านั้นแทนการสร้างคำขอย่อยเพื่อเข้าถึงโมดูลอัปสตรีม Nginx เนื่องจาก อดีตมักจะมีความยืดหยุ่นและประสิทธิภาพของหน่วยความจำมากกว่ามาก
ตัวแปล Lua (หรือที่เรียกว่า "Lua State" หรือ "อินสแตนซ์ LuaJIT VM") จะถูกแชร์กับคำขอทั้งหมดในกระบวนการของผู้ปฏิบัติงาน Nginx เดียวเพื่อลดการใช้หน่วยความจำให้เหลือน้อยที่สุด บริบทคำขอจะถูกแยกออกโดยใช้ Lua coroutines แบบไลท์เวท
โมดูล Lua ที่โหลดยังคงอยู่ในระดับกระบวนการของผู้ปฏิบัติงาน Nginx ส่งผลให้หน่วยความจำมีขนาดเล็กใน Lua แม้ว่าจะอยู่ภายใต้ภาระงานหนักก็ตาม
โมดูลนี้เสียบเข้ากับระบบย่อย "http" ของ Nginx เพื่อให้สามารถพูดได้เฉพาะโปรโตคอลการสื่อสารดาวน์สตรีมในตระกูล HTTP (HTTP 0.9/1.0/1.1/2.0, WebSockets ฯลฯ...) หากคุณต้องการทำการสื่อสาร TCP ทั่วไปกับไคลเอนต์ดาวน์สตรีม คุณควรใช้โมดูล ngx_stream_lua แทน ซึ่งมี Lua API ที่เข้ากันได้
กลับไปที่สารบบ
เพียงเพื่อชื่อไม่กี่:
ความเป็นไปได้นั้นไม่จำกัด เนื่องจากโมดูลช่วยให้สามารถรวบรวมองค์ประกอบต่างๆ ภายใน Nginx รวมถึงการเปิดเผยพลังของภาษา Lua ให้กับผู้ใช้ โมดูลนี้ให้ความยืดหยุ่นเต็มที่ในการเขียนสคริปต์ ในขณะเดียวกันก็ให้ระดับประสิทธิภาพที่เทียบเคียงได้กับโปรแกรมภาษา C ดั้งเดิมทั้งในแง่ของเวลา CPU และขนาดหน่วยความจำด้วย LuaJIT 2.x
โดยทั่วไปการใช้งานภาษาสคริปต์อื่นๆ มักจะประสบปัญหาเพื่อให้ตรงกับระดับประสิทธิภาพนี้
กลับไปที่สารบบ
เวอร์ชันล่าสุดของโมดูลนี้เข้ากันได้กับ Nginx เวอร์ชันต่อไปนี้:
ไม่ รองรับแกน Nginx ที่เก่ากว่า 1.6.0 (พิเศษ)
กลับไปที่สารบบ
ขอ แนะนำให้ใช้ OpenResty รุ่นต่างๆ ซึ่งรวม Nginx, ngx_lua (โมดูลนี้), LuaJIT รวมถึงโมดูล Nginx ที่ทรงพลังอื่นๆ และไลบรารี Lua ไว้ด้วย
เราไม่แนะนำให้สร้างโมดูลนี้ด้วย Nginx ด้วยตัวเอง เนื่องจากเป็นเรื่องยากที่จะตั้งค่าให้ถูกต้องทุกประการ
โปรดทราบว่าการเปิดตัวอย่างเป็นทางการของ Nginx, LuaJIT และ OpenSSL มีข้อจำกัดหลายประการและข้อบกพร่องที่มีมายาวนาน ซึ่งอาจทำให้คุณลักษณะบางอย่างของโมดูลนี้ถูกปิดใช้งาน ทำงานไม่ถูกต้อง หรือทำงานช้าลง แนะนำให้ใช้ OpenResty อย่างเป็นทางการ เนื่องจากรวม LuaJIT 2.1 fork และแพตช์ Nginx/OpenSSL ที่ได้รับการปรับปรุงแล้วของ OpenResty
หรืออีกทางหนึ่ง ngx_lua สามารถคอมไพล์ลงใน Nginx ได้ด้วยตนเอง:
สร้างซอร์สด้วยโมดูลนี้:
wget ' https://openresty.org/download/nginx-1.19.3.tar.gz '
tar -xzvf nginx-1.19.3.tar.gz
cd nginx-1.19.3/
# tell nginx's build system where to find LuaJIT 2.0:
export LUAJIT_LIB=/path/to/luajit/lib
export LUAJIT_INC=/path/to/luajit/include/luajit-2.0
# tell nginx's build system where to find LuaJIT 2.1:
export LUAJIT_LIB=/path/to/luajit/lib
export LUAJIT_INC=/path/to/luajit/include/luajit-2.1
# Here we assume Nginx is to be installed under /opt/nginx/.
./configure --prefix=/opt/nginx
--with-ld-opt= " -Wl,-rpath,/path/to/luajit/lib "
--add-module=/path/to/ngx_devel_kit
--add-module=/path/to/lua-nginx-module
# Note that you may also want to add `./configure` options which are used in your
# current nginx build.
# You can get usually those options using command nginx -V
# you can change the parallelism number 2 below to fit the number of spare CPU cores in your
# machine.
make -j2
make install
# Note that this version of lug-nginx-module not allow to set `lua_load_resty_core off;` any more.
# So, you have to install `lua-resty-core` and `lua-resty-lrucache` manually as below.
cd lua-resty-core
make install PREFIX=/opt/nginx
cd lua-resty-lrucache
make install PREFIX=/opt/nginx
# add necessary `lua_package_path` directive to `nginx.conf`, in the http context
lua_package_path " /opt/nginx/lib/lua/?.lua;; " ;
กลับไปที่สารบบ
เริ่มต้นจาก NGINX 1.9.11 คุณยังสามารถคอมไพล์โมดูลนี้เป็นโมดูลไดนามิกได้โดยใช้ตัวเลือก --add-dynamic-module=PATH
แทน --add-module=PATH
บนบรรทัดคำสั่ง ./configure
ด้านบน จากนั้นคุณสามารถโหลดโมดูลใน nginx.conf
ของคุณได้อย่างชัดเจนผ่านคำสั่ง load_module เช่น
load_module /path/to/modules/ndk_http_module.so; # assuming NDK is built as a dynamic module too
load_module /path/to/modules/ngx_http_lua_module.so;
กลับไปที่สารบบ
ในขณะที่สร้างโมดูลนี้ผ่าน OpenResty หรือด้วยคอร์ Nginx คุณสามารถกำหนดมาโคร C ต่อไปนี้ผ่านตัวเลือกคอมไพเลอร์ C:
NGX_LUA_USE_ASSERT
เมื่อกำหนด จะเปิดใช้งานการยืนยันในฐานรหัส ngx_lua C แนะนำสำหรับการดีบักหรือการทดสอบบิลด์ ซึ่งสามารถแนะนำโอเวอร์เฮดรันไทม์บางส่วน (เล็กน้อย) เมื่อเปิดใช้งาน มาโครนี้เปิดตัวครั้งแรกในรุ่น v0.9.10
NGX_LUA_ABORT_AT_PANIC
เมื่อ LuaJIT VM ตื่นตระหนก ngx_lua จะสั่งให้กระบวนการของผู้ปฏิบัติงาน nginx ปัจจุบันปิดการทำงานอย่างสง่างามตามค่าเริ่มต้น ด้วยการระบุแมโคร C นี้ ngx_lua จะยกเลิกกระบวนการของผู้ปฏิบัติงาน nginx ปัจจุบัน (ซึ่งโดยปกติจะส่งผลให้เกิดไฟล์ดัมพ์หลัก) ทันที ตัวเลือกนี้มีประโยชน์สำหรับการดีบัก VM panics ตัวเลือกนี้ถูกนำมาใช้ครั้งแรกในรุ่น v0.9.8
หากต้องการเปิดใช้งานมาโครเหล่านี้ตั้งแต่หนึ่งรายการขึ้นไป เพียงส่งตัวเลือกคอมไพเลอร์ C เพิ่มเติมไปที่สคริปต์ ./configure
configure ของ Nginx หรือ OpenResty ตัวอย่างเช่น
./configure --with-cc-opt="-DNGX_LUA_USE_ASSERT -DNGX_LUA_ABORT_AT_PANIC"
กลับไปที่สารบบ
กลับไปที่สารบบ
รายชื่ออีเมล openresty-en สำหรับผู้พูดภาษาอังกฤษ
กลับไปที่สารบบ
รายชื่อผู้รับจดหมายแบบ openresty มีไว้สำหรับผู้พูดภาษาจีน
กลับไปที่สารบบ
ที่เก็บโค้ดของโปรเจ็กต์นี้โฮสต์บน GitHub ที่ openresty/lua-nginx-module
กลับไปที่สารบบ
กรุณาส่งรายงานข้อผิดพลาด สิ่งที่อยากได้ หรือแพตช์โดย
กลับไปที่สารบบ
ดูวิดีโอ YouTube "วัดเวลาดำเนินการของรหัส Lua อย่างถูกต้องใน OpenResty"
จากการเปิดตัว v0.5.0rc32
*_by_lua_file
กำหนดค่าคำสั่งทั้งหมด (เช่น content_by_lua_file) รองรับการโหลดไฟล์รหัสไบต์ดิบ LuaJIT 2.0/2.1 โดยตรง:
/path/to/luajit/bin/luajit -b /path/to/input_file.lua /path/to/output_file.ljbc
ตัวเลือก -bg
สามารถใช้เพื่อรวมข้อมูลการดีบักในไฟล์ LuaJIT bytecode:
/path/to/luajit/bin/luajit -bg /path/to/input_file.lua /path/to/output_file.ljbc
โปรดดูเอกสารอย่างเป็นทางการของ LuaJIT ในตัวเลือก -b
สำหรับรายละเอียดเพิ่มเติม:
https://luajit.org/running.html#opt_b
โปรดทราบว่าไฟล์ bytecode ที่สร้างโดย LuaJIT 2.1 เข้ากัน ไม่ ได้กับ LuaJIT 2.0 และในทางกลับกัน การรองรับรหัสไบต์ LuaJIT 2.1 ถูกเพิ่มครั้งแรกใน ngx_lua v0.9.3
ความพยายามที่จะโหลดไฟล์รหัสไบต์ Lua 5.1 มาตรฐานลงในอินสแตนซ์ ngx_lua ที่เชื่อมโยงกับ LuaJIT 2.0/2.1 (หรือกลับกัน) จะส่งผลให้เกิดข้อความแสดงข้อผิดพลาด Nginx เช่นข้อความด้านล่าง:
[error] 13909#0: *1 failed to load Lua inlined code: bad byte-code header in /path/to/test_file.luac
การโหลดไฟล์ bytecode ผ่าน Lua primitives เช่น require
และ dofile
ควรทำงานได้ตามที่คาดไว้เสมอ
กลับไปที่สารบบ
หากคุณต้องการเข้าถึงตัวแปรสภาพแวดล้อมของระบบ เช่น foo
ใน Lua ผ่าน Lua API os.getenv มาตรฐาน คุณควรแสดงรายการชื่อตัวแปรสภาพแวดล้อมนี้ในไฟล์ nginx.conf
ของคุณผ่านคำสั่ง env ตัวอย่างเช่น,
env foo;
กลับไปที่สารบบ
โปรโตคอล HTTP 1.0 ไม่รองรับเอาต์พุตแบบก้อน และต้องการส่วนหัว Content-Length
ที่ชัดเจน เมื่อเนื้อหาการตอบสนองไม่ว่างเปล่าเพื่อรองรับ HTTP 1.0 Keep-alive ดังนั้นเมื่อมีการร้องขอ HTTP 1.0 และเปิด on
คำสั่ง lua_http10_buffering ngx_lua จะบัฟเฟอร์เอาต์พุตของการเรียก ngx.say และ ngx.print และยังเลื่อนการส่งส่วนหัวการตอบกลับออกไปจนกว่าจะได้รับเอาต์พุตเนื้อหาการตอบกลับทั้งหมด ในเวลานั้น ngx_lua สามารถคำนวณความยาวรวมของเนื้อหาและสร้างส่วนหัว Content-Length
ที่เหมาะสมเพื่อกลับไปยังไคลเอนต์ HTTP 1.0 หากส่วนหัวการตอบกลับ Content-Length
ถูกตั้งค่าไว้ในโค้ด Lua ที่ทำงานอยู่ การบัฟเฟอร์นี้จะถูกปิดใช้งาน แม้ว่าคำสั่ง lua_http10_buffering จะเปิด on
ก็ตาม
สำหรับการตอบสนองเอาต์พุตการสตรีมขนาดใหญ่ สิ่งสำคัญคือต้องปิดใช้งานคำสั่ง lua_http10_buffering เพื่อลดการใช้หน่วยความจำให้เหลือน้อยที่สุด
โปรดทราบว่าเครื่องมือมาตรฐาน HTTP ทั่วไป เช่น คำขอ ab
และ http_load
ปัญหา HTTP 1.0 เป็นค่าเริ่มต้น หากต้องการบังคับให้ curl
ส่งคำขอ HTTP 1.0 ให้ใช้ตัวเลือก -0
กลับไปที่สารบบ
ด้วย LuaJIT 2.x คุณสามารถเชื่อมโยงโค้ดไบต์ของโมดูล Lua ล้วนๆ เข้ากับไฟล์ปฏิบัติการ Nginx แบบคงที่ได้
คุณสามารถใช้ไฟล์ปฏิบัติการ luajit
เพื่อคอมไพล์ไฟล์โมดูล .lua
lua Lua ไปยังไฟล์อ็อบเจ็กต์ .o
ที่มีข้อมูลโค้ดไบต์ที่ส่งออก จากนั้นลิงก์ไฟล์ .o
โดยตรงในบิลด์ Nginx ของคุณ
ด้านล่างนี้เป็นตัวอย่างเล็กๆ น้อยๆ เพื่อแสดงให้เห็นสิ่งนี้ พิจารณาว่าเรามีไฟล์ .lua
ต่อไปนี้ชื่อ foo.lua
:
-- foo.lua
local _M = {}
function _M . go ()
print ( " Hello from foo " )
end
return _M
จากนั้นเราจะคอมไพล์ไฟล์ .lua
นี้เป็นไฟล์ foo.o
:
/path/to/luajit/bin/luajit -bg foo.lua foo.o
สิ่งสำคัญที่นี่คือชื่อของไฟล์ .lua
lua ซึ่งจะกำหนดวิธีการใช้โมดูลนี้ในภายหลังบนดินแดน Lua ชื่อไฟล์ foo.o
ไม่สำคัญเลย ยกเว้นนามสกุลไฟล์ .o
(ซึ่งจะบอก luajit
ว่าใช้รูปแบบเอาต์พุตใด) หากคุณต้องการตัดข้อมูลการดีบัก Lua ออกจากโค้ดไบต์ผลลัพธ์ คุณสามารถระบุตัวเลือก -b
ด้านบนแทน -bg
ได้
จากนั้นเมื่อสร้าง Nginx หรือ OpenResty ให้ส่งตัวเลือก --with-ld-opt="foo.o"
ไปยังสคริปต์ ./configure
:
./configure --with-ld-opt= " /path/to/foo.o " ...
สุดท้ายนี้ คุณสามารถทำสิ่งต่อไปนี้ในโค้ด Lua ใดๆ ที่รันโดย ngx_lua:
local foo = require " foo "
foo . go ()
และโค้ดชิ้นนี้ไม่ต้องขึ้นอยู่กับไฟล์ foo.lua
ภายนอกอีกต่อไปแล้ว เนื่องจากมีการรวบรวมไว้ในไฟล์ปฏิบัติการ nginx
แล้ว
หากคุณต้องการใช้จุดในชื่อโมดูล Lua เมื่อโทร require
เช่นเดียวกับใน
local foo = require " resty.foo "
จากนั้นคุณจะต้องเปลี่ยนชื่อไฟล์ foo.lua
เป็น resty_foo.lua
ก่อนที่จะคอมไพล์เป็นไฟล์ .o
ด้วยยูทิลิตีบรรทัดคำสั่ง luajit
สิ่งสำคัญคือต้องใช้ LuaJIT เวอร์ชันเดียวกันทุกประการเมื่อคอมไพล์ไฟล์ .lua
ไปเป็นไฟล์ .o
เพื่อสร้าง nginx + ngx_lua เนื่องจากรูปแบบไบต์ LuaJIT อาจเข้ากันไม่ได้ระหว่าง LuaJIT เวอร์ชันต่างๆ เมื่อรูปแบบไบต์โค้ดเข้ากันไม่ได้ คุณจะเห็นข้อผิดพลาดรันไทม์ Lua แจ้งว่าไม่พบโมดูล Lua
เมื่อคุณมีไฟล์ .lua
หลายไฟล์ที่ต้องคอมไพล์และลิงก์ ให้ระบุไฟล์ .o
พร้อมกันในค่าของตัวเลือก --with-ld-opt
ตัวอย่างเช่น
./configure --with-ld-opt= " /path/to/foo.o /path/to/bar.o " ...
หากคุณมีไฟล์ .o
มากเกินไป คุณอาจไม่สามารถตั้งชื่อไฟล์ทั้งหมดด้วยคำสั่งเดียวได้ ในกรณีนี้ คุณสามารถสร้างไลบรารีแบบคงที่ (หรือไฟล์เก็บถาวร) สำหรับไฟล์ .o
ของคุณได้ เช่นเดียวกับใน
ar rcus libmyluafiles.a * .o
จากนั้นคุณสามารถเชื่อมโยงไฟล์เก็บถาวร myluafiles
โดยรวมกับไฟล์ปฏิบัติการ nginx ของคุณได้:
./configure
--with-ld-opt= " -L/path/to/lib -Wl,--whole-archive -lmyluafiles -Wl,--no-whole-archive "
โดยที่ /path/to/lib
คือพาธของไดเร็กทอรีที่มีไฟล์ libmyluafiles.a
ควรสังเกตว่าต้องใช้ตัวเลือกตัวเชื่อมโยง --whole-archive
ถาวรที่นี่ เพราะไม่เช่นนั้นไฟล์เก็บถาวรของเราจะถูกข้ามเนื่องจากไม่มีการกล่าวถึงสัญลักษณ์ในไฟล์เก็บถาวรของเราในส่วนหลักของไฟล์ปฏิบัติการ nginx
กลับไปที่สารบบ
หากต้องการแชร์ข้อมูลทั่วโลกระหว่างคำขอทั้งหมดที่จัดการโดยกระบวนการของผู้ปฏิบัติงาน Nginx เดียวกัน ให้ห่อหุ้มข้อมูลที่แชร์ไว้ในโมดูล Lua ใช้ Lua require
บิวด์อินเพื่อนำเข้าโมดูล จากนั้นจัดการข้อมูลที่แชร์ใน Lua วิธีนี้ใช้ได้เนื่องจากโมดูล Lua ที่จำเป็นถูกโหลดเพียงครั้งเดียวและ Coroutines ทั้งหมดจะแชร์สำเนาของโมดูลเดียวกัน (ทั้งโค้ดและข้อมูล)
โปรดทราบว่า เราไม่สนับสนุนการใช้ตัวแปร Lua ส่วนกลางอย่างยิ่ง เนื่องจากอาจทำให้เกิดสภาวะการแข่งขันที่ไม่คาดคิดระหว่างคำขอที่เกิดขึ้นพร้อมกัน
นี่คือตัวอย่างเล็กๆ น้อยๆ เกี่ยวกับการแชร์ข้อมูลภายในพนักงาน Nginx ผ่านโมดูล Lua:
-- mydata.lua
local _M = {}
local data = {
dog = 3 ,
cat = 4 ,
pig = 5 ,
}
function _M . get_age ( name )
return data [ name ]
end
return _M
จากนั้นเข้าถึงได้จาก nginx.conf
:
location /lua {
content_by_lua_block {
local mydata = require "mydata"
ngx.say(mydata.get_age("dog"))
}
}
โมดูล mydata
ในตัวอย่างนี้จะถูกโหลดและรันในคำขอแรกไปยังตำแหน่ง /lua
เท่านั้น และคำขอที่ตามมาทั้งหมดไปยังกระบวนการของผู้ปฏิบัติงาน Nginx เดียวกันจะใช้อินสแตนซ์ที่โหลดซ้ำของโมดูลตลอดจนสำเนาเดียวกันของข้อมูลใน จนกว่าสัญญาณ HUP
จะถูกส่งไปยังกระบวนการหลักของ Nginx เพื่อบังคับให้โหลดซ้ำ เทคนิคการแบ่งปันข้อมูลนี้จำเป็นสำหรับแอปพลิเคชัน Lua ที่มีประสิทธิภาพสูงซึ่งใช้โมดูลนี้
โปรดทราบว่าการแบ่งปันข้อมูลนี้เป็นแบบ ต่อพนักงาน และไม่ใช่แบบ ต่อเซิร์ฟเวอร์ นั่นคือ เมื่อมีกระบวนการของผู้ปฏิบัติงาน Nginx หลายกระบวนการภายใต้ต้นแบบ Nginx การแบ่งปันข้อมูลจะไม่สามารถข้ามขอบเขตกระบวนการระหว่างผู้ปฏิบัติงานเหล่านี้ได้
โดยปกติจะแนะนำให้แชร์ข้อมูลแบบอ่านอย่างเดียวด้วยวิธีนี้ คุณยังสามารถแบ่งปันข้อมูลที่เปลี่ยนแปลงได้ระหว่างคำขอที่เกิดขึ้นพร้อมกันทั้งหมดของกระบวนการของผู้ปฏิบัติงาน Nginx แต่ละคน ตราบใดที่ ไม่มี การดำเนินการ I/O ที่ไม่ปิดกั้น (รวมถึง ngx.sleep) ในระหว่างการคำนวณของคุณ ตราบใดที่คุณไม่ให้การควบคุมกลับไปที่ลูปเหตุการณ์ Nginx และตัวกำหนดเวลาเธรดแบบเบาของ ngx_lua (แม้จะโดยปริยาย) ก็จะไม่มีเงื่อนไขการแข่งขันใด ๆ ในระหว่างนั้น ด้วยเหตุนี้ โปรดใช้ความระมัดระวังเสมอเมื่อคุณต้องการแบ่งปันข้อมูลที่เปลี่ยนแปลงได้ในระดับผู้ปฏิบัติงาน การปรับปรุงประสิทธิภาพของรถบักกี้อาจนำไปสู่สภาวะการแข่งขันที่แก้ไขจุดบกพร่องได้ยากภายใต้ภาระงานหนัก
หากจำเป็นต้องมีการแบ่งปันข้อมูลทั่วทั้งเซิร์ฟเวอร์ ให้ใช้วิธีใดวิธีหนึ่งต่อไปนี้:
memcached
, redis
, MySQL
หรือ PostgreSQL
การเปิดตัวอย่างเป็นทางการของ OpenResty มาพร้อมกับชุดโมดูล Nginx และไลบรารี Lua ที่ให้อินเทอร์เฟซกับกลไกการจัดเก็บข้อมูลเหล่านี้กลับไปที่สารบบ
กลับไปที่สารบบ
เมธอด tcpsock:connect อาจบ่งบอกถึง success
แม้ว่าการเชื่อมต่อจะล้มเหลว เช่น ข้อผิดพลาด Connection Refused
อย่างไรก็ตาม ความพยายามในภายหลังในการจัดการอ็อบเจ็กต์ cosocket จะล้มเหลว และส่งคืนข้อความสถานะข้อผิดพลาดจริงที่สร้างขึ้นโดยการดำเนินการเชื่อมต่อที่ล้มเหลว
ปัญหานี้เกิดจากข้อจำกัดในรูปแบบเหตุการณ์ Nginx และดูเหมือนว่าจะส่งผลต่อ Mac OS X เท่านั้น
กลับไปที่สารบบ
dofile
ของ Lua และ require
บิวด์อินนั้นถูกนำไปใช้เป็นฟังก์ชัน C ใน LuaJIT 2.0/2.1 หากไฟล์ Lua ถูกโหลดโดย dofile
หรือ require
เรียกใช้ ngx.location.capture*, ngx.exec, ngx.exit หรือฟังก์ชัน API อื่น ๆ ที่ต้องการผลผลิต ในขอบเขต ระดับบนสุด ของไฟล์ Lua ข้อผิดพลาด Lua "พยายามให้ข้ามขอบเขตการโทร C" จะถูกยกขึ้น เพื่อหลีกเลี่ยงปัญหานี้ ให้วางการเรียกเหล่านี้ที่ต้องการให้ฟังก์ชัน Lua ของคุณเองในไฟล์ Lua แทนที่จะเป็นขอบเขตระดับบนสุดของไฟล์กลับไปที่สารบบ
ต้องใช้ความระมัดระวังเมื่อนำเข้าโมดูล และควรใช้แบบฟอร์มนี้:
local xxx = require ( ' xxx ' )
แทนที่จะเป็นรูปแบบเก่าที่เลิกใช้แล้ว:
require ( ' xxx ' )
นี่คือเหตุผล: จากการออกแบบแล้ว สภาพแวดล้อมทั่วโลกจะมีอายุการใช้งานเท่ากันกับตัวจัดการคำขอ Nginx ที่เชื่อมโยงอยู่ทุกประการ ตัวจัดการคำขอแต่ละตัวมีชุดตัวแปรส่วนกลาง Lua ของตัวเอง และนั่นคือแนวคิดของการแยกคำขอ โมดูล Lua ถูกโหลดจริง ๆ โดยตัวจัดการคำขอ Nginx ตัวแรกและถูกแคชโดย require()
ในตัวในตาราง package.loaded
สำหรับการอ้างอิงในภายหลัง และ module()
บิวด์อินที่ใช้โดยโมดูล Lua บางตัวมีผลข้างเคียงของการตั้งค่า ตัวแปรโกลบอลไปยังตารางโมดูลที่โหลด แต่ตัวแปรส่วนกลางนี้จะถูกล้างในตอนท้ายของตัวจัดการคำขอ และตัวจัดการคำขอที่ตามมาทุกตัวจะมีสภาพแวดล้อมโกลบอล (สะอาด) ของตัวเอง ดังนั้นเราจะได้รับข้อยกเว้น Lua สำหรับการเข้าถึงค่า nil
การใช้ตัวแปรโกลบอล Lua นั้นเป็นสิ่งที่ไม่แนะนำโดยทั่วไปในบริบท ngx_lua ดังนี้:
ดังนั้นจึงขอแนะนำ อย่างยิ่ง ให้ประกาศดังกล่าวภายในขอบเขตท้องถิ่นที่เหมาะสมแทน
-- Avoid
foo = 123
-- Recommended
local foo = 123
-- Avoid
function foo () return 123 end
-- Recommended
local function foo () return 123 end
หากต้องการค้นหาอินสแตนซ์ทั้งหมดของตัวแปรส่วนกลาง Lua ในโค้ด Lua ของคุณ ให้เรียกใช้เครื่องมือ lua-releng ในไฟล์ต้นฉบับ .lua
ทั้งหมด:
$ lua-releng
Checking use of Lua global variables in file lib/foo/bar.lua ...
1 [1489] SETGLOBAL 7 -1 ; contains
55 [1506] GETGLOBAL 7 -3 ; setvar
3 [1545] GETGLOBAL 3 -4 ; varexpand
เอาต์พุตแจ้งว่าบรรทัด 1489 ของไฟล์ lib/foo/bar.lua
เขียนไปยังตัวแปรโกลบอลชื่อ contains
บรรทัด 1506 อ่านจากตัวแปรโกลบอล setvar
และบรรทัด 1545 อ่านโกลบอล varexpand
เครื่องมือนี้จะรับประกันว่าตัวแปรในเครื่องในฟังก์ชันโมดูล Lua ทั้งหมดจะถูกประกาศด้วยคำหลัก local
มิฉะนั้น ข้อยกเว้นรันไทม์จะถูกส่งออกไป จะป้องกันสภาพการแข่งขันที่ไม่พึงประสงค์ขณะเข้าถึงตัวแปรดังกล่าว ดูการแชร์ข้อมูลภายใน Nginx Worker ด้วยเหตุผลเบื้องหลังนี้
กลับไปที่สารบบ
คำสั่ง ngx.location.capture และ ngx.location.capture_multi ไม่สามารถจับภาพตำแหน่งที่มีคำสั่ง add_before_body, add_after_body, auth_request, echo_location, echo_location_async, echo_subrequest หรือ echo_subrequest_async
location /foo {
content_by_lua_block {
res = ngx.location.capture( "/bar" )
}
}
location /bar {
echo_location /blah;
}
location /blah {
echo "Success!" ;
}
$ curl -i http://example.com/foo
จะไม่ทำงานตามที่คาดไว้
กลับไปที่สารบบ
เนื่องจากข้อจำกัดภายในในแกน Nginx ทำให้ cosocket API ถูกปิดใช้งานในบริบทต่อไปนี้: set_by_lua*, log_by_lua*, header_filter_by_lua* และ body_filter_by_lua
ขณะนี้ cosockets ยังถูกปิดใช้งานในบริบทคำสั่ง init_by_lua* และ init_worker_by_lua* แต่เราอาจเพิ่มการรองรับสำหรับบริบทเหล่านี้ในอนาคต เนื่องจากไม่มีข้อจำกัดในแกน Nginx (หรือข้อจำกัดอาจแก้ไขได้)
อย่างไรก็ตาม มีวิธีแก้ไขปัญหาชั่วคราว เมื่อบริบทดั้งเดิม ไม่ จำเป็นต้องรอผลลัพธ์ cosocket นั่นคือ การสร้างตัวจับเวลาแบบหน่วงเวลาเป็นศูนย์ผ่าน ngx.timer.at API และทำผลลัพธ์ cosocket ในตัวจัดการตัวจับเวลา ซึ่งทำงานแบบอะซิงโครนัสตามบริบทดั้งเดิมที่สร้างตัวจับเวลา
กลับไปที่สารบบ
หมายเหตุ หลังจากเวอร์ชัน v0.9.17
คุณสามารถหลีกเลี่ยงข้อผิดพลาดนี้ได้โดยใช้คำสั่งการกำหนดค่า *_by_lua_block {}
ลำดับ PCRE เช่น d
, s
หรือ w
จำเป็นต้องได้รับการดูแลเป็นพิเศษเนื่องจากในตัวอักษรสตริง อักขระแบ็กสแลช จะถูกแยกออกโดยทั้งตัวแยกวิเคราะห์ภาษา Lua และโดยตัวแยกวิเคราะห์ไฟล์การกำหนดค่า Nginx ก่อนการประมวลผลหากไม่ได้อยู่ใน
*_by_lua_block {}
คำสั่ง ดังนั้นตัวอย่างต่อไปนี้จะไม่ทำงานตามที่คาดไว้:
# nginx.conf
? location /test {
? content_by_lua '
? local regex = "d+" -- THIS IS WRONG OUTSIDE OF A *_by_lua_block DIRECTIVE
? local m = ngx.re.match("hello, 1234", regex)
? if m then ngx.say(m[0]) else ngx.say("not matched!") end
? ' ;
? }
# evaluates to "not matched!"
เพื่อหลีกเลี่ยงปัญหานี้ ให้หลีกเลี่ยงเครื่องหมายแบ็กสแลช สองครั้ง :
# nginx.conf
location /test {
content_by_lua '
local regex = "\\d+"
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
' ;
}
# evaluates to "1234"
ที่นี่ \\d+
ถูกแยกลงไปที่ \d+
โดยตัวแยกวิเคราะห์ไฟล์การกำหนดค่า Nginx และจะถูกแยกออกไปเพิ่มเติมที่ d+
โดยตัวแยกวิเคราะห์ภาษา Lua ก่อนที่จะรัน
อีกทางหนึ่ง รูปแบบ regex สามารถนำเสนอเป็นตัวอักษรสตริง Lua ที่มีวงเล็บยาวโดยใส่ไว้ใน "วงเล็บยาว", [[...]]
ซึ่งในกรณีนี้จะต้องใช้เครื่องหมายแบ็กสแลชเพียงครั้งเดียวสำหรับตัวแยกวิเคราะห์ไฟล์ Nginx
# nginx.conf
location /test {
content_by_lua '
local regex = [[\d+]]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
' ;
}
# evaluates to "1234"
ที่นี่ [[\d+]]
ถูกแยกลงไปที่ [[d+]]
โดยตัวแยกวิเคราะห์ไฟล์กำหนดค่า Nginx และประมวลผลได้อย่างถูกต้อง
โปรดทราบว่าอาจต้องใช้วงเล็บยาวจากวงเล็บยาว [=[...]=]
หากรูปแบบ regex มีลำดับ [...]
อาจใช้แบบฟอร์ม [=[...]=]
เป็นรูปแบบเริ่มต้นได้หากต้องการ
# nginx.conf
location /test {
content_by_lua '
local regex = [=[[0-9]+]=]
local m = ngx.re.match("hello, 1234", regex)
if m then ngx.say(m[0]) else ngx.say("not matched!") end
' ;
}
# evaluates to "1234"
อีกวิธีหนึ่งในการหลีกเลี่ยงลำดับ PCRE คือต้องแน่ใจว่าโค้ด Lua อยู่ในไฟล์สคริปต์ภายนอก และดำเนินการโดยใช้คำสั่ง *_by_lua_file
ต่างๆ ด้วยวิธีการนี้ แบ็กสแลชจะถูกแยกออกโดยโปรแกรมแยกวิเคราะห์ภาษา Lua เท่านั้น ดังนั้นจึงต้อง Escape เพียงครั้งเดียวเท่านั้น
-- test.lua
local regex = " \ d+ "
local m = ngx . re . match ( " hello, 1234 " , regex )
if m then ngx . say ( m [ 0 ]) else ngx . say ( " not matched! " ) end
-- evaluates to "1234"
ภายในไฟล์สคริปต์ภายนอก ลำดับ PCRE ที่แสดงเป็นตัวอักษรสตริง Lua ที่มีวงเล็บยาวไม่จำเป็นต้องมีการแก้ไข
-- test.lua
local regex = [[ d+ ]]
local m = ngx . re . match ( " hello, 1234 " , regex )
if m then ngx . say ( m [ 0 ]) else ngx . say ( " not matched! " ) end
-- evaluates to "1234"
ตามที่ระบุไว้ก่อนหน้านี้ ลำดับ PCRE ที่นำเสนอภายใน *_by_lua_block {}
คำสั่ง (มีให้หลังจากรุ่น v0.9.17
) ไม่จำเป็นต้องแก้ไข
# nginx.conf
location /test {
content_by_lua_block {
local regex = [[d+]]
local m = ngx.re.match( "hello, 1234" , regex)
if m then ngx.say(m[0]) else ngx.say( "not matched!" ) end
}
}
# evaluates to "1234"
หมายเหตุ ขอแนะนำให้ใช้ by_lua_file
เมื่อรหัส Lua ยาวมาก
กลับไปที่สารบบ
ไม่รองรับการผสม SSI กับ ngx_lua ในคำขอ Nginx เดียวกันเลย เพียงใช้ ngx_lua โดยเฉพาะ ทุกสิ่งที่คุณสามารถทำได้กับ SSI นั้นสามารถทำได้บน ngx_lua อยู่แล้ว และจะมีประสิทธิภาพมากขึ้นเมื่อใช้ ngx_lua
กลับไปที่สารบบ
Lua API บางตัวที่จัดทำโดย ngx_lua ยังไม่ทำงานในโหมด SPDY ของ Nginx: ngx.location.capture, ngx.location.capture_multi และ ngx.req.socket
กลับไปที่สารบบ
Nginx อาจยุติคำขอก่อนเวลาด้วย (อย่างน้อย):
ซึ่งหมายความว่าเฟสที่รันตามปกติจะถูกข้ามไป เช่น เฟสการเขียนซ้ำหรือการเข้าถึง นอกจากนี้ยังหมายความว่าเฟสต่อๆ ไปที่ทำงานโดยไม่คำนึงถึง เช่น log_by_lua จะไม่สามารถเข้าถึงข้อมูลที่ตั้งค่าตามปกติในเฟสเหล่านั้นได้
กลับไปที่สารบบ
bsdrecv
ngx_hash_t
เพื่อเพิ่มประสิทธิภาพกระบวนการค้นหาส่วนหัวในตัวสำหรับ ngx.req.set_header และอื่นๆignore_resp_headers
, ignore_resp_body
และ ignore_resp
ให้กับเมธอด ngx.location.capture และ ngx.location.capture_multi เพื่อให้สามารถปรับประสิทธิภาพระดับไมโครได้ที่ฝั่งผู้ใช้stat
คล้ายกับ mod_luaกลับไปที่สารบบ
การเปลี่ยนแปลงที่ทำในโมดูลนี้ทุกรุ่นจะแสดงอยู่ในบันทึกการเปลี่ยนแปลงของชุด OpenResty:
https://openresty.org/#Changes
กลับไปที่สารบบ
จำเป็นต้องมีการขึ้นต่อกันต่อไปนี้เพื่อรันชุดทดสอบ:
เวอร์ชัน Nginx >= 1.4.2
โมดูล Perl:
โมดูล Nginx:
ลำดับที่โมดูลเหล่านี้ถูกเพิ่มระหว่างการกำหนดค่ามีความสำคัญ เนื่องจากตำแหน่งของโมดูลตัวกรองใดๆ ในสายการกรองจะกำหนดเอาต์พุตสุดท้าย เป็นต้น ลำดับการเพิ่มที่ถูกต้องแสดงไว้ด้านบน
ไลบรารี Lua บุคคลที่สาม:
การใช้งาน: