0Pricing
PHP Academy · درس

Apache Kafka مع PHP

بثّ الأحداث عالية الإنتاجية باستخدام Kafka

Apache Kafka مع PHP درس مجاني في PHP Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في PHP Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة PHP Academy 4 دروس في المجموع.

Kafka مع PHP

Kafka ليس قائمة مهام — بل هو سجل التزام موزع مخصص للإضافة فقط. يضيف المنتجون السجلات إلى الموضوعات، ويقرأ المستهلكون بدءًا من إزاحتهم الخاصة، ويمكنهم إعادة قراءة السجل السابق. وهذا يجعل Kafka مناسبًا لبث الأحداث عالي الإنتاجية، وevent sourcing، وتغذية مجموعات مستهلكين مستقلة متعددة من تدفق واحد.

في PHP، تتعاملون مع Kafka من خلال ext-rdkafka، وهي واجهة ربط فوق مكتبة C المجرّبة جيدًا librdkafka.

سجل لا قائمة انتظار

إليك التحول الذهني عند الانتقال من RabbitMQ إلى Kafka:

  • لا تُحذف الرسائل عند استهلاكها؛ بل تنتهي صلاحيتها وفق سياسة الاحتفاظ، حسب الوقت أو الحجم.
  • يتتبع كل مستهلك إزاحته الخاصة — أي موضعه في السجل.
  • يُقسَّم الموضوع إلى أقسام؛ ولا يُضمن الترتيب إلا داخل القسم الواحد.
  • تقرأ مجموعات المستهلكين المتعددة الموضوع نفسه بصورة مستقلة.

إذا كنتم بحاجة إلى إعادة القراءة، أو التوزيع على قراء متعددين، أو معدل نقل هائل، فـ Kafka مناسب. أما إذا كنتم بحاجة إلى توجيه كل رسالة ومدد انتهاء صلاحية، فـ RabbitMQ مناسب.

تثبيت ext-rdkafka

ثبّتوا المكتبة الأصلية أولًا، ثم إضافة PECL، ثم غلافًا أعلى مستوى عند الحاجة.

# Debian/Ubuntu
apt-get install -y librdkafka-dev
pecl install rdkafka
echo "extension=rdkafka.so" >> php.ini

# Optional ergonomic wrapper
composer require longlang/phpkafka  # or arnaud-lb/php-rdkafka-stubs for IDE

إنتاج السجلات

أنشئ RdKafka\Producer، واحصل على مؤشر إلى موضوع، ثم استدعِ produce(). يتحكم المفتاح في القسم الذي يصل إليه السجل — المفتاح نفسه يعني القسم نفسه مع الحفاظ على الترتيب. استدعِ flush() دائمًا قبل الخروج، وإلا فستفقد السجلات المخزنة مؤقتًا.

<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$producer = new RdKafka\Producer($conf);

$topic = $producer->newTopic('orders');
// RD_KAFKA_PARTITION_UA = let the partitioner choose by key
$key = 'order-42';
$topic->produce(RD_KAFKA_PARTITION_UA, 0, json_encode(['id' => 42]), $key);

$producer->poll(0);
$result = $producer->flush(10000); // wait up to 10s
if ($result !== RD_KAFKA_RESP_ERR_NO_ERROR) {
    throw new RuntimeException('Failed to flush');
}

التقسيم حسب المفتاح

يمثل التقسيم جوهر قابلية Kafka للتوسع والحفاظ على الترتيب. يحسب موزّع الأقسام الافتراضي تجزئة مفتاح السجل: partition = hash(key) % numPartitions. ويهم اختيار مفتاح جيد:

  • التقسيم حسب customerId → تبقى جميع أحداث العميل مرتبة وفي قسم واحد.
  • مفتاح null → توزيع بالتناوب على الأقسام، لتحقيق أقصى معدل نقل من دون ضمان الترتيب.

لا يمكنكم أبدًا تقليل عدد أقسام الموضوع، كما أن إضافة أقسام تعيد توزيع خريطة التجزئة — لذلك حدّدوا عدد الأقسام مسبقًا بما يناسب أقصى توازٍ مطلوب.

مجموعات المستهلكين والإزاحات

استخدم KafkaConsumer عالي المستوى مع group.id. يوزّع Kafka الأقسام على أعضاء المجموعة ويعيد موازنتها عند انضمام أعضاء أو مغادرتهم. يقرأ كل عضو أقسامه المعيّنة فقط، مما يمنحكم توسعًا أفقيًا تلقائيًا.

<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->set('group.id', 'order-emailers');
$conf->set('auto.offset.reset', 'earliest'); // start of log if no offset
$conf->set('enable.auto.commit', 'false');   // we commit manually

$consumer = new RdKafka\KafkaConsumer($conf);
$consumer->subscribe(['orders']);

while (true) {
    $msg = $consumer->consume(5000);
    if ($msg->err === RD_KAFKA_RESP_ERR_NO_ERROR) {
        handle($msg->payload);
        $consumer->commit($msg); // commit offset AFTER work
    }
}
function handle(string $p): void {}

متى تُجرى عملية الالتزام

تحدد عملية الالتزام بالإزاحة دلالات التسليم:

  • إجراء الالتزام بعد المعالجة → التسليم مرة واحدة على الأقل (at-least-once)، إذ تؤدي الأعطال قبل الالتزام إلى إعادة قراءة السجل.
  • إجراء الالتزام قبل المعالجة → التسليم مرة واحدة كحد أقصى (at-most-once)، إذ قد يؤدي العطل إلى فقدانه.

تُجري عملية الالتزام التلقائي (enable.auto.commit=true) الالتزام وفق مؤقت، بغض النظر عما إذا كان عملكم قد اكتمل — وهذا مريح، لكنه قد يؤدي إلى إسقاط السجلات بصمت عند حدوث عطل. عطّلوها وأجروا الالتزام يدويًا عندما تكون صحة البيانات مهمة.

معالجة أخطاء الاستهلاك

ليست كل نتيجة من consume() رسالة. يجب التفرّع وفق رمز الخطأ — إذ إن __PARTITION_EOF و__TIMED_OUT إشارتا تحكم طبيعيتان وليستا حالتي فشل.

<?php
$msg = $consumer->consume(2000);
switch ($msg->err) {
    case RD_KAFKA_RESP_ERR_NO_ERROR:
        echo "Got: {$msg->payload} @ offset {$msg->offset}\n";
        break;
    case RD_KAFKA_RESP_ERR__PARTITION_EOF:
        echo "Reached end of partition\n"; // caught up, keep polling
        break;
    case RD_KAFKA_RESP_ERR__TIMED_OUT:
        echo "No message this poll\n";
        break;
    default:
        throw new \Exception($msg->errstr(), $msg->err);
}

ضبط معدل النقل

ينبع معدل نقل Kafka من التجميع على دفعات. ومن إعدادات المنتج المهمة:

  • linger.ms — انتظر قليلًا لتجميع عدد أكبر من السجلات في كل طلب، مثل 5–20ms.
  • batch.size / queue.buffering.max.messages — مخازن مؤقتة أكبر وعدد أقل من رحلات الذهاب والإياب.
  • compression.type — يقلل استخدام lz4 أو zstd لتكلفة الشبكة بشكل كبير.
  • acks — استخدم all للاستمرارية و1 لزمن استجابة أقل.
<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->set('compression.type', 'lz4');
$conf->set('linger.ms', '10');
$conf->set('batch.size', '65536');
$conf->set('acks', 'all');
$producer = new RdKafka\Producer($conf);

استدعاءات تقرير التسليم

بما أن produce() غير متزامنة، فلن يؤدي الإرسال الفاشل إلى إطلاق استثناء فورًا. سجّل دالة رد الاتصال الخاصة بتقرير التسليم في إعدادات المنتج لمعرفة مصير كل سجل — فهذه هي الطريقة الموثوقة الوحيدة لاكتشاف إخفاقات المنتج الصامتة في PHP.

<?php
$conf = new RdKafka\Conf();
$conf->set('bootstrap.servers', 'localhost:9092');
$conf->setDrMsgCb(function ($producer, $msg) {
    if ($msg->err) {
        fwrite(STDERR, 'Delivery FAILED: ' . rd_kafka_err2str($msg->err) . "\n");
    } else {
        echo "Delivered to partition {$msg->partition} @ offset {$msg->offset}\n";
    }
});
$producer = new RdKafka\Producer($conf);
// poll() services the callback queue; call it after producing
$producer->poll(0);

محاذير خاصة بـ PHP

يفترض Kafka عملاء طويلي التشغيل، بينما تعاكس دورة طلب PHP ذلك:

  • يخزّن المنتجون السجلات مؤقتًا بصورة غير متزامنة — نفّذ دائمًا flush() قبل انتهاء البرنامج، وإلا فستُفقد السجلات.
  • شغّل المستهلكين كعمال CLI دائمين تحت إشراف supervisor، ولا تشغّلهم مطلقًا داخل طلب ويب.
  • توقف إعادة الموازنة الاستهلاك مؤقتًا؛ لذلك اجعل العمل الخاص بكل رسالة قصيرًا، أو اضبط max.poll.interval.ms بسخاء حتى لا تتم إزالتك من المجموعة.
  • اضبط log_level وسجّل دالة رد اتصال المنتج الخاصة بتقرير التسليم لاكتشاف الإخفاقات الصامتة.

تحقق سريع

ضمانات الترتيب في Kafka.

مراجعة

Kafka بأسلوب PHP:

  • Kafka سجل قابل لإعادة التشغيل وليس قائمة انتظار؛ ويتتبع المستهلكون الإزاحات.
  • يحقق التقسيم حسب المفتاح ترتيبًا لكل مفتاح وتوسعًا أفقيًا.
  • تقسم مجموعات المستهلكين الأقسام وتعيد موازنتها تلقائيًا.
  • أجروا الالتزام بالإزاحات بعد العمل لتحقيق التسليم مرة واحدة على الأقل؛ وعطّلوا الالتزام التلقائي للتحكم الكامل.
  • اضبطوا linger.ms وbatch.size وcompression.type، واستدعوا flush() دائمًا.

التالي: ربط هذه الأسس في سير عمل موثوق قائم على الأحداث.

الأسئلة الشائعة

هل درس «Apache Kafka مع PHP» مجاني؟

نعم — نص درس «Apache Kafka مع PHP» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة PHP Academy، انتقل إلى CoddyKit PRO. تتضمن دورة PHP Academy 4 دروس في المجموع.

ماذا ستتعلم في «Apache Kafka مع PHP»؟

بثّ الأحداث عالية الإنتاجية باستخدام Kafka تتمرن على PHP Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ PHP Academy؟

لا تُشترط خبرة سابقة. PHP Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «Apache Kafka مع PHP»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس PHP Academy هذا؟

نعم. كل درس في PHP Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. لماذا نستخدم المراسلة غير المتزامنة
  2. العمل مع RabbitMQ في PHP
  3. Apache Kafka مع PHP
  4. بناء سير عمل موجّه بالأحداث
← العودة إلى PHP Academy