Tower في Rust: دليل شامل مع أمثلة تطبيقية

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

ما هي مكتبة Tower ولماذا تحتاجها؟

Tower هي مكتبة أساسية في نظام Rust البيئي، توفر تجريدات (abstractions) لبناء خدمات شبكية غير متزامنة (asynchronous) معيارية وقابلة للتجميع والوسائط البرمجية الوسيطة (middleware). ليست إطار عمل HTTP جاهزًا (مثل Actix-web أو Axum)، بل هي "مادة بناء" لهم. على سبيل المثال، تم بناء Axum فوق Tower.

الفكرة الأساسية لـ Tower هي الخاصية (trait) Service، والتي تمثل دالة غير متزامنة تحول الطلب (Request) إلى استجابة (Response). الوسيط (Middleware) أو "الطبقة" (Layer) في Tower هي طريقة لتغليف خدمة بخدمة أخرى، لإضافة سلوك إضافي (تسجيل، مصادقة، مهلات زمنية، موازنة تحميل، إلخ) دون تغيير الكود الأصلي للخدمة.

لماذا تحتاجها؟

  • المعيارية (Modularity): يمكنك تقسيم منطق معالجة الطلبات المعقد إلى مكونات صغيرة ومستقلة وقابلة لإعادة الاستخدام (خدمات ووسائط).
  • قابلية التجميع (Composability): يمكن دمج الوسائط بسهولة، "وضعها" فوق بعضها البعض مثل الطبقات (layers).
  • عدم التزامن (Asynchrony): صُممت Tower للعمل مع Rust غير المتزامن (tokio, async-std) وتدعم futures.
  • قابلية الاختبار (Testability): يمكن اختبار كل مكون بشكل منفصل.

تثبيت Tower

Tower هي مكتبة Rust، لذا يتم تثبيتها عبر مدير الحزم Cargo. أضف التبعية إلى ملف Cargo.toml الخاص بك:

[dependencies]
tower = { version = "0.4", features = ["full"] } # "full" يشمل جميع الميزات
tokio = { version = "1", features = ["full"] } # لبيئة التشغيل غير المتزامنة

بعد ذلك، نفذ الأمر التالي في الطرفية:

cargo build

سيتم تحميل المكتبة وترجمتها مع مشروعك.

بداية سريعة: أول خدمة ووسيط (Middleware) لك

لنقم بإنشاء خدمة بسيطة تستقبل نصًا وترجعه بحروف كبيرة (uppercase). ثم نغلفها بوسيط يقوم بتسجيل كل طلب.

use std::convert::Infallible;
use std::task::{Context, Poll};
use tower::{Service, Layer, ServiceBuilder};
use tower::layer::util::Identity;
use std::future::{Ready, ready};

// 1. تعريف الخدمة
#[derive(Clone)]
struct MyService;

impl<Request> Service<Request> for MyService {
    type Response = String;
    type Error = Infallible;
    type Future = Ready<Result<Self::Response, Self::Error>>;

    fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        Poll::Ready(Ok(()))
    }

    fn call(&mut self, req: Request) -> Self::Future {
        // ببساطة نعيد نصًا
        ready(Ok(format!("مرحبًا من الخدمة! الطلب: {:?}", std::any::type_name::<Request>())))
    }
}

// 2. تعريف وسيط للتسجيل
#[derive(Clone)]
struct LogMiddleware;

impl<S> Layer<S> for LogMiddleware {
    type Service = LogService<S>;

    fn layer(&self, inner: S) -> Self::Service {
        LogService { inner }
    }
}

#[derive(Clone)]
struct LogService<S> {
    inner: S,
}

impl<S, Request> Service<Request> for LogService<S>
where
    S: Service<Request>,
    S::Future: std::marker::Send,
    Request: std::fmt::Debug,
{
    type Response = S::Response;
    type Error = S::Error;
    type Future = S::Future;

    fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.inner.poll_ready(cx)
    }

    fn call(&mut self, req: Request) -> Self::Future {
        println!("[تسجيل] تم استلام الطلب: {:?}", req);
        self.inner.call(req)
    }
}

#[tokio::m                    

توصيات