ميكروسيرفيسات بلغة Rust: الهندسة والممارسة من الألف إلى الياء
أصبحت بنية الميكروسيرفيسات معيارًا لبناء التطبيقات الحديثة القابلة للتوسع. تقدم لغة Rust مع تركيزها على الأداء وأمان الذاكرة والتوازي مزايا فريدة لتطوير الميكروسيرفيسات. في هذه المقالة، سنستعرض الدورة الكاملة لإنشاء ميكروسيرفيس بلغة Rust: من اختيار الأدوات إلى النشر.
لماذا Rust للميكروسيرفيسات؟
تجمع Rust بين التحكم منخفض المستوى للغات C/C++ وأمان اللغة الحديثة. بالنسبة للميكروسيرفيسات، هذا يعني:
- أمان الذاكرة بدون جامع القمامة — استهلاك موارد قابل للتنبؤ، وغياب "التباطؤ" الناتج عن GC.
- أداء عالٍ — على مستوى C++، وهو أمر حاسم للخدمات عالية التحميل.
- نموذج الملكية (ownership) — يمنع سباقات البيانات في مرحلة التجميع.
- حجم صغير للملفات الثنائية — بدء تشغيل سريع واستهلاك منخفض للذاكرة.
بفضل هذا، تعتبر Rust مثالية لخدمات الحافة (Edge-services) وبوابات API والمكونات الحيوية.
الأدوات الرئيسية للميكروسيرفيسات بلغة Rust
في نظام Rust البيئي، توجد العديد من المكتبات والأطر الناضجة:
- Actix-web — إطار ويب غير متزامن قوي بنموذج الممثلين (actor model). مناسب تمامًا لواجهات REST API.
- Tokio — وقت تشغيل غير متزامن، أساس معظم تطبيقات الشبكات.
- Axum — إطار حديث من فريق Tokio، يستخدم tower و hyper.
- Warp — إطار خفيف الوزن يعتمد على المرشحات (filters).
- Tonic — تنفيذ gRPC بلغة Rust.
للتطبيق العملي، سنستخدم Actix-web و Tokio.
هندسة الميكروسيرفيس بلغة Rust
لننظر إلى هندسة خدمة بسيطة لإدارة المستخدمين. يتضمن الميكروسيرفيس النموذجي:
- طبقة HTTP — معالجة الطلبات، التحقق من الصحة.
- منطق الأعمال — طبقة الخدمة.
- طبقة البيانات — التعامل مع قاعدة البيانات (عادةً بشكل غير متزامن).
- طبقة الاتصال — عملاء gRPC أو HTTP للخدمات الأخرى.
مثال: REST API بسيط باستخدام Actix-web
لننشئ ميكروسيرفيس أساسيًا مع نقاط نهاية لعمليات CRUD على المستخدمين.
// Cargo.toml[dependencies]actix-web = "4"actix-rt = "2"serde = { version = "1", features = ["derive"] }tokio = { version = "1", features = ["full"] }sqlx = { version = "0.7", features = ["runtime-tokio-rustls", "postgres"] }الملف الرئيسي main.rs:
use actix_web::{web, App, HttpServer, Responder, HttpResponse};use serde::{Deserialize, Serialize};use sqlx::postgres::PgPoolOptions;
#[derive(Serialize, Deserialize, Clone)]struct User { id: i32, name: String, email: String,}
// تخزين في الذاكرة (للتوضيح، في الواقع — قاعدة بيانات)use std::sync::Mutex;struct AppState { users: Mutex<Vec<User>>,}
async fn get_users(data: web::Data<AppState>) -> impl Responder { let users = data.users.lock().unwrap(); HttpResponse::Ok().json(users.clone())}
async fn create_user( data: web::Data<AppState>, new_user: web::Json<User>,) -> impl Responder { let mut users = data.users.lock().unwrap(); let user = User { id: users.len() as i32 + 1, ..new_user.into_inner() }; users.push(user.clone()); HttpResponse::Created().json(user)}
#[actix_web::main]async fn main() -> std::io::Result<()> { let app_state = web::Data::new(AppState { users: Mutex::new(vec![]), });
HttpServer::new(move || { App::new()\