ثلاث مكتبات Lua Resty شائعة الاستخدام في OpenResty
API7.ai
January 13, 2023
تعلم لغات البرمجة والمنصات غالبًا ما يكون مسألة فهم المكتبات القياسية والجهات الخارجية بدلاً من التركيز على بناء الجملة نفسه. بعد تعلم واجهة برمجة التطبيقات (API) الخاصة بها وتقنيات تحسين الأداء، نحتاج إلى تعلم استخدام مكتبات lua-resty
المختلفة لتوسيع قدرات OpenResty لدينا لتشمل المزيد من السيناريوهات.
أين يمكن العثور على مكتبة lua-resty
؟
مقارنةً بـ PHP و Python و JavaScript، فإن المكتبات القياسية والجهات الخارجية الحالية لـ OpenResty لا تزال محدودة نسبيًا، والعثور على مكتبات lua-resty
المناسبة ليس بالأمر السهل. ومع ذلك، هناك مصدران موصى بهما لمساعدتك في العثور عليها بشكل أسرع.
أول توصية هي مستودع awesome-resty
الذي يحتفظ به Aapo. يقوم هذا المستودع بتنظيم المكتبات المتعلقة بـ OpenResty حسب الفئة وهو شامل، بما في ذلك وحدات NGINX C، مكتبات lua-resty
، أطر عمل الويب، مكتبات التوجيه، القوالب، أطر عمل الاختبار، إلخ. إنه خيارك الأول لموارد OpenResty.
إذا لم تجد المكتبة المناسبة في مستودع Aapo، يمكنك أيضًا البحث في luarocks
أو topm
أو GitHub. قد تكون هناك بعض المكتبات التي لم يتم إصدارها مفتوحة المصدر لفترة طويلة ولم تحظ بالكثير من الاهتمام.
في المقالات السابقة، تعرفنا على العديد من المكتبات المفيدة مثل lua-resty-mlcache
، lua-resty-traffic
، lua-resty-shell
، إلخ. اليوم، في المقال الأخير من قسم تحسين أداء OpenResty، سنتعرف على 3 مكتبات إضافية فريدة، جميعها مساهمات من المطورين في المجتمع.
تحسين أداء ngx.var
أولاً، لنلقي نظرة على وحدة C: lua-var-nginx-module
. كما ذكرت سابقًا، فإن ngx.var
هي عملية تستهلك الأداء نسبيًا. وبالتالي، في الممارسة العملية، نحتاج إلى استخدام ngx.ctx
كطبقة تخزين مؤقت.
إذن، هل هناك طريقة لحل مشكلة أداء ngx.var
تمامًا؟
تقوم وحدة C هذه ببعض التجارب في هذا المجال، والنتائج ملحوظة، حيث تحقق تحسينًا في الأداء بمقدار 5 أضعاف مقارنة بـ ngx.var
. تستخدم هذه الوحدة طريقة FFI
، لذا تحتاج أولاً إلى تجميع OpenResty باستخدام خيار التجميع التالي.
./configure --prefix=/opt/openresty \
--add-module=/path/to/lua-var-nginx-module
ثم استخدم luarocks
لتثبيت مكتبة lua بالطريقة التالية:
luarocks install lua-resty-ngxvar
الطريقة المستخدمة هنا بسيطة جدًا، حيث تتطلب سطرًا واحدًا فقط من دالة fetch
. تعمل بشكل مماثل لـ ngx.var.remote_addr
الأصلية للحصول على عنوان IP العميل.
content_by_lua_block {
local var = require("resty.ngxvar")
ngx.say(var.fetch("remote_addr"))
}
بعد فهم هذه العمليات الأساسية، قد تكون أكثر فضولًا حول كيفية تحقيق هذه الوحدة لتحسين كبير في الأداء. كما نقول دائمًا، "لا توجد أسرار أمام الكود المصدري". لذا دعنا نكتشف كيفية جلب متغير remote_addr
.
ngx_int_t
ngx_http_lua_var_ffi_remote_addr(ngx_http_request_t *r, ngx_str_t *remote_addr)
{
remote_addr->len = r->connection->addr_text.len;
remote_addr->data = r->connection->addr_text.data;
return NGX_OK;
}
بعد قراءة هذا الكود، ستلاحظ أن طريقة Lua FFI
هذه هي نفسها طريقة lua-resty-core
. تتمتع بميزة واضحة تتمثل في استخدام FFI
للحصول على المتغيرات مباشرة، متجاوزة منطق البحث الأصلي لـ ngx.var
. عيبها واضح: إضافة دوال C واستدعاءات FFI
لكل متغير تريد الحصول عليه، وهو أمر يستغرق وقتًا وجهدًا.
قد يتساءل البعض، "لماذا أقول إن هذا يستغرق وقتًا وجهدًا؟ ألا يبدو الكود C أعلاه جيدًا؟" دعنا نلقي نظرة على مصدر هذه الأسطر من الكود، والتي تأتي من src/http/ngx_http_variables.c
في كود NGINX.
static ngx_int_t
ngx_http_variable_remote_addr(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data)
{
v->len = r->connection->addr_text.len;
v->valid = 1;
v->no_cacheable = 0;
v->not_found = 0;
v->data = r->connection->addr_text.data;
return NGX_OK;
}
بعد رؤية الكود المصدري، تم الكشف عن اللغز! lua-var-nginx-module
هي ناقل لكود متغيرات NGINX، مع تغليف FFI في الطبقة الخارجية، وبهذه الطريقة، تحقق تحسينًا في الأداء. هذه فكرة جيدة واتجاه جيد للتحسين.
عند تعلم مكتبة أو أداة، يجب ألا نتوقف فقط عند مستوى التشغيل، بل يجب أن نسأل أيضًا عن سبب القيام بذلك وننظر إلى الكود المصدري. بالطبع، أشجعك أيضًا على المساهمة بالكود لدعم المزيد من متغيرات NGINX.
JSON Schema
هنا أقدم مكتبة lua-resty
: lua-rapidjson
. إنها غلاف حول rapidjson
، مكتبة JSON مفتوحة المصدر من Tencent، والمعروفة بأدائها. هنا، نركز على الفرق بينها وبين cjson
: دعم JSON Schema
.
JSON Schema
هو معيار شائع يسمح لنا بوصف دقيق لتنسيق المعلمات في واجهة وكيفية التحقق منها. هنا مثال بسيط:
"stringArray": {
"type": "array",
"items": { "type": "string" },
"minItems": 1,
"uniqueItems": true
}
يصف هذا JSON بدقة أن المعلمة stringArray
هي من نوع مصفوفة النصوص وأن المصفوفة لا يمكن أن تكون فارغة، ولا يمكن أن تتكرر عناصر المصفوفة.
lua-rapidjson
تسمح لنا باستخدام JSON Schema في OpenResty، مما يمكن أن يجلب راحة كبيرة للتحقق من الواجهات. على سبيل المثال، لواجهة حد العد التي وصفناها سابقًا، يمكننا استخدام المخطط التالي لوصفها:
local schema = {
type = "object",
properties = {
count = {type = "integer", minimum = 0},
time_window = {type = "integer", minimum = 0},
key = {type = "string", enum = {"remote_addr", "server_addr"}},
rejected_code = {type = "integer", minimum = 200, maximum = 600},
},
additionalProperties = false,
required = {"count", "time_window", "key", "rejected_code"},
}
ستجد أن هذا يمكن أن يؤدي إلى فائدتين واضحتين:
- بالنسبة للواجهة الأمامية، يمكن للواجهة الأمامية إعادة استخدام وصف المخطط هذا مباشرة لتطوير الصفحات الأمامية والتحقق من المعلمات دون الحاجة إلى الاهتمام بالواجهة الخلفية.
- بالنسبة للواجهة الخلفية، تستخدم الواجهة الخلفية مباشرة وظيفة التحقق من المخطط
SchemaValidator
فيlua-rapidjson
لتحديد شرعية الواجهة، ولا حاجة لكتابة كود إضافي.
اتصال Worker
أخيرًا، أود التحدث عن مكتبة lua-resty
التي تمكن الاتصال بين العمال في OpenResty، حيث لا توجد آلية للاتصال المباشر بين العمال، مما يسبب الكثير من المشاكل. دعنا نتخيل سيناريو:
خدمة OpenResty لديها 24 عملية عامل، وعندما يقوم المسؤول بتحديث تكوين النظام من خلال واجهات برمجة التطبيقات REST HTTP، يتلقى عامل واحد فقط التحديث من المسؤول ويكتب النتيجة في قاعدة البيانات، ويقوم بتحديث
shared dict
وlru cache
داخل العامل الخاص به. إذن، كيف يمكن إخطار العمال الـ 23 الآخرين لتحديث هذا التكوين؟
نحتاج إلى آلية إخطار بين عدة عمال لإنجاز المهمة أعلاه. في حالة عدم دعم OpenResty لذلك، علينا إنقاذ الموقف باستخدام بيانات shared dict
عبر العمال.
lua-resty-worker-events
هي تنفيذ ملموس لهذه الفكرة. تحتفظ برقم إصدار في shared dict
، وعند نشر رسالة جديدة، تضيف واحدًا إلى رقم الإصدار وتضع محتوى الرسالة في القاموس مع رقم الإصدار كمفتاح.
event_id, err = _dict:incr(KEY_LAST_ID, 1)
success, err = _dict:add(KEY_DATA .. tostring(event_id), json)
أيضًا، يتم إنشاء حلقة polling
بفاصل زمني افتراضي قدره ثانية واحدة في الخلفية باستخدام ngx.timer
للتحقق باستمرار من تغييرات رقم الإصدار:
local event_id, err = get_event_id()
if event_id == _last_event then
return "done"
end
بهذه الطريقة، بمجرد العثور على إشعار حدث جديد للتعامل معه، يتم استرداد محتوى الرسالة من shared dict
بناءً على رقم الإصدار:
while _last_event < event_id do
count = count + 1
_last_event = _last_event + 1
data, err = _dict:get(KEY_DATA..tostring(_last_event))
end
بشكل عام، على الرغم من أن lua-resty-worker-events
لديها تأخير لمدة ثانية واحدة، إلا أنها لا تزال تنفذ آلية إخطار بين العمال.
ومع ذلك، في بعض السيناريوهات في الوقت الفعلي، مثل دفع الرسائل، قد يسبب لك عدم وجود اتصال مباشر بين عمليات Worker
في OpenResty بعض المشاكل. لا يوجد حل أفضل لهذا، ولكن إذا كانت لديك أفكار جيدة، فلا تتردد في مناقشتها على Github. العديد من ميزات OpenResty مدفوعة من المجتمع لبناء دورة بيئية حميدة.
الخلاصة
المكتبات الثلاث التي قدمناها اليوم فريدة وتجلب المزيد من الإمكانيات لتطبيقات OpenResty. أخيرًا، موضوع تفاعلي، هل وجدت أي مكتبات مثيرة للاهتمام حول OpenResty؟ أو ما الذي تجده أو تتساءل عنه في المكتبات المذكورة اليوم؟ أنت مرحب بإرسال هذه المقالة إلى مستخدمي OpenResty حولك لتبادل الأفكار والتقدم معًا.