مقدمة: لماذا تعتبر Rust لـ Serverless نقلة نوعية
\n\nأصبحت بنية Serverless معيارًا منذ فترة طويلة لبناء تطبيقات قابلة للتوسع واقتصادية. ومع ذلك، فإن اختيار اللغات التقليدية (Python, Node.js, Java) غالبًا ما يواجه مشكلة زمن البدء البارد واستهلاك عالي للذاكرة. تقدم Rust، مع تكلفتها الصفرية للتجريدات وعدم وجود جامع للقمامة، حلاً ثوريًا. في هذه المقالة، سنشرح كيفية إنشاء وتكوين ونشر وظائف serverless بلغة Rust لـ AWS Lambda - بدءًا من تثبيت الأدوات وصولاً إلى إصدارات الإنتاج.
\n\nبنهاية المقالة، ستكون قادرًا على:
\n- \n
- إعداد بيئة تطوير Rust + AWS Lambda \n
- كتابة وظيفة فعالة مع معالجة الأحداث \n
- تحسين الملف الثنائي لتقليل زمن البدء البارد \n
- نشر الوظيفة في AWS باستخدام Cargo Lambda \n
تجهيز البيئة: Cargo Lambda و AWS CLI
\n\nللعمل مع Rust على AWS Lambda، نحتاج إلى أداة خاصة - cargo-lambda. تقوم بأتمتة البناء والاختبار والنشر. سنقوم بتثبيتها عبر Cargo:
cargo install cargo-lambda\n\nتأكد أيضًا من تثبيت aws-cli (مُهيأ بمفاتيح IAM) و Docker (للاختبار المحلي). لننشئ مشروعًا جديدًا:
cargo lambda new my-rust-lambda\ncd my-rust-lambda\n\nسينشئ الأمر هيكلًا أساسيًا مع ملف Cargo.toml و src/main.rs. لاحظ التبعيات في Cargo.toml:
[package]\nname = "my-rust-lambda"\nversion = "0.1.0"\nedition = "2021"\n\n[dependencies]\nlambda_runtime = "0.11"\ntokio = { version = "1", features = ["macros", "rt-multi-thread"] }\nserde = { version = "1.0", features = ["derive"] }\nserde_json = "1.0"\ntracing = { version = "0.1", features = ["log"] }\ntracing-subscriber = { version = "0.3", features = ["fmt"] }\n\nالمكتبة الأساسية هي lambda_runtime. توفر trait LambdaHandler الذي سنقوم بتطبيقه.
كتابة أول وظيفة: معالجة طلب API Gateway
\n\nلننظر في سيناريو كلاسيكي: وظيفة تستقبل JSON باسم المستخدم وتعيد تحية. افتح src/main.rs واكتب الكود التالي:
use lambda_runtime::{run, service_fn, Error, LambdaEvent};\nuse serde::{Deserialize, Serialize};\n\n#[derive(Deserialize)]\nstruct Request {\n name: String,\n}\n\n#[derive(Serialize)]\nstruct Response {\n message: String,\n}\n\nasync fn function_handler(event: LambdaEvent<Request>) -> Result<Response, Error> {\n let name = event.payload.name;\n let message = format!("مرحبًا، {}! لقد قمت للتو بتشغيل Rust على Lambda!", name);\n Ok(Response { message })\n}\n\n#[tokio::main]\nasync fn main() -> Result<(), Error> {\n tracing_subscriber::fmt()\n .with_max_level(tracing::Level::INFO)\n .init();\n\n run(service_fn(function_handler)).await\n}\n\nدعنا نحلل النقاط الرئيسية:
\n- \n
LambdaEvent<Request>- هيكل يحتوي على نص الطلب وسياق التنفيذ. \n #[tokio::main]- وقت تشغيل غير متزامن ضروري لعمل Lambda runtime. \n serde- إلغاء تسلسل JSON الوارد وتسلسل الاستجابة. \n
اختبر محليًا:
\n\ncargo lambda watch\n\nثم في محطة طرفية أخرى:
\n\ncargo lambda invoke --data-ascii '{"name": "أليكسي"}'\n\nيجب أن ترى: {"message":"مرحبًا، أليكسي! لقد قمت للتو بتشغيل Rust على Lambda!"}
تحسين زمن البدء البارد وحجم الملف الثنائي
\n\nالميزة الرئيسية لـ Rust هي الحد الأدنى من زمن البدء البارد. ومع ذلك، لتحقيق ذلك، تحتاج إلى بناء الملف الثنائي بشكل صحيح