azizdevfull/laravel-micro-rabbitmq

High-performance Microservice Event Bus (Pub/Sub) for Laravel using RabbitMQ.

Maintainers

Package info

github.com/azizdevfull/laravel-micro-rabbitmq

pkg:composer/azizdevfull/laravel-micro-rabbitmq

Statistics

Installs: 3

Dependents: 0

Suggesters: 0

Stars: 0

v1.1.0 2026-04-14 11:49 UTC

README

MicroRabbit โ€” bu Laravel mikroxizmatlari (Microservices) uchun yaratilgan, "Enterprise" darajasidagi o'zini-o'zi tiklovchi (Self-Healing) RabbitMQ event-bus kutubxonasi.

U shunchaki xabarlarni jo'natib qabul qilmaydi. U tarmoq uzilishlari, server qulashlari, dublikat xabarlar, zaharli ma'lumotlar (poison payloads) va qotib qolgan jarayonlar ta'sirini keskin kamaytirish uchun mo'ljallangan qudratli yadroga ega.

๐Ÿ“‘ Mundarija

๐Ÿ“ฆ 1. O'rnatish

Kutubxonani Composer orqali loyihangizga qo'shing:

composer require azizdevfull/laravel-micro-rabbitmq

So'ngra kutubxona sozlamalari (config) va Outbox tizimi uchun migratsiyalarni loyihangizga ko'chirib oling:

php artisan vendor:publish --tag="micro-rabbitmq-config"
php artisan vendor:publish --tag="micro-rabbit-migrations"

Baza jadvallarini yarating:

php artisan migrate

.env faylingizga RabbitMQ ulanish ma'lumotlarini kiriting:

MICRO_RABBITMQ_HOST=127.0.0.1
MICRO_RABBITMQ_PORT=5672
MICRO_RABBITMQ_USER=guest
MICRO_RABBITMQ_PASSWORD=guest

# Majburiy emas, qo'shimcha xavfsizlik sozlamalari:
MICRO_RABBITMQ_HEARTBEAT=30
MICRO_RABBITMQ_OUTBOX=false
MICRO_RABBITMQ_MONITORING=true
MICRO_RABBITMQ_PUBLISH_TIMEOUT=5
MICRO_RABBITMQ_OUTBOX_BATCH_SIZE=20

idempotency va distributed lock funksiyalari uchun production'da CACHE_DRIVER=redis tavsiya qilinadi.

โš™๏ธ 2. Konfiguratsiya (config/micro-rabbitmq.php)

Kutubxonani publish qilganingizdan so'ng, loyihangizda config/micro-rabbitmq.php fayli paydo bo'ladi. Uning ichidagi asosiy sozlamalar nima vazifa bajarishini bilish juda muhim:

  • connection.heartbeat: (Default: 30). Tarmoq xavfsizlik devorlari (Firewall) jim turgan ulanishni uzib yubormasligi uchun Worker har 30 soniyada RabbitMQ ga "Men tirikman" deb belgi (ping) berib turadi. Bu o'lik ulanishlarning oldini oladi.
  • features.idempotency: Dublikatlardan himoya. Yoqilgan bo'lsa, tizim message_id larni keshda saqlaydi va bitta xatni 2 marta o'qimaydi.
  • features.idempotency_ttl_seconds: Idempotency kaliti cache'da qancha vaqt yashashi (Default: 86400 soniya).
  • features.idempotency_processing_ttl_seconds: Worker ishlayotgan paytdagi lock qancha yashashi (Default: 300 soniya).
  • features.publish_timeout_seconds: Oddiy publish() broker tasdig'ini maksimal necha soniya kutishi (Default: 5).
  • consumer.prefetch_count / MICRO_RABBITMQ_PREFETCH: Har bir consumer bir vaqtda nechta unacked xabar ushlashini belgilaydi. Throughputni oshiradi, lekin juda katta qiymat fairness va xotira sarfiga salbiy ta'sir qilishi mumkin.
  • features.publish_confirm_batch_size / MICRO_RABBITMQ_PUBLISH_CONFIRM_BATCH: Publisher broker confirm'ni har nechta xabardan keyin kutishini belgilaydi. Katta batch odatda publish tezligini oshiradi.
  • features.publish_confirm_max_wait_ms / MICRO_RABBITMQ_PUBLISH_CONFIRM_MAX_WAIT_MS: Batch to'lmagan bo'lsa ham necha ms dan keyin confirm flush bo'lishi. Kichik qiymat latency'ni tushiradi, katta qiymat throughput'ni oshirishi mumkin.
  • features.tracing: Yoqilgan bo'lsa, kelgan trace_id (yoki yangi UUID) Context va Log::withContext() orqali barcha log yozuvlariga avtomatik qo'shiladi. Handler ichida alohida hech narsa qilmasdan ELK/Loki da so'rovni oxirigacha kuzatish mumkin bo'ladi.
  • features.outbox.enabled: Transactional Outbox pattern'ni yoqadi. Xatlar to'g'ridan-to'g'ri havo orqali emas, Baza orqali kafolatli jo'natiladi.
  • features.outbox.max_attempts: RabbitMQ o'chib qolsa, Outbox worker xatni jo'natishga necha marta urinishini belgilaydi (Default: 3).
  • features.outbox.publish_timeout_seconds: Outbox relay broker tasdig'ini maksimal necha soniya kutishi (Default: 5).
  • features.outbox.batch_size: Outbox worker bir aylanishda nechta xabar olishga urinishini belgilaydi (Default: 20).
  • paths: micro:cache qaysi papkalarni skan qilishini belgilaydi. Default: [base_path('app')].
  • retry.backoff_multiplier, retry.max_delay_ms, retry.jitter_ms: Retry kechikishining exponential backoff+jitter strategiyasi.
  • retry.non_retryable_exceptions: Qayta urinilmaydigan exception klasslari ro'yxati.
  • circuit_breaker.enabled / MICRO_RABBITMQ_CIRCUIT_BREAKER: RPC uchun Circuit Breaker'ni yoqadi/o'chiradi (Default: true).
  • circuit_breaker.threshold / MICRO_RABBITMQ_CB_THRESHOLD: Nechta ketma-ket xatoda circuit ochiladi (Default: 5).
  • circuit_breaker.timeout_seconds / MICRO_RABBITMQ_CB_TIMEOUT: Circuit ochiq turgan vaqt (soniyada). Bu vaqtdan keyin bitta "probe" request o'tkaziladi (Default: 30).
  • monitoring.*: Metrika cache'i va alert thresholdlari (outbox backlog, blocked, DLQ size, publish timeout) uchun sozlamalar.

Production benchmarklar uchun amaliy boshlang'ich tuning:

MICRO_RABBITMQ_PREFETCH=20
MICRO_RABBITMQ_PUBLISH_CONFIRM_BATCH=50
MICRO_RABBITMQ_PUBLISH_CONFIRM_MAX_WAIT_MS=10

Realistic Benchmark Natijalari

Muhim: Qisqa "burst" testlarda sun'iy yuqori raqamlar chiqadi. Quyidagi natijalar 5000โ€“25000 xabarlik sustained load bilan o'lchangan (1 consumer, SQLite, prefetch_count=1):

Workload 5 000 xabar 10 000 15 000 20 000 25 000
light (CPU only) 238 eps 211 219 211 215
db (INSERT) 214 eps 214 212 214 212
db_read (INSERT + SELECT) 244 eps 229 229 229 216

Asosiy xulosalar:

  • Haqiqiy bottleneck โ€” prefetch_count=1, handler emas. light va db workload orasida farq atigi ~3 eps. Sababi: prefetch=1 bilan consumer har bir xabarni ACK qilmasdan keyingisini ololmaydi โ€” bu RabbitMQ round-trip latency ceiling ni belgilaydi (~215 eps). Handler ichida DB query bo'lsa ham bo'lmasa ham natija bir xil.

  • Kichik testlardagi yuqori raqamlarga ishonmang. Burst rejimida (100โ€“400 xabar) consumer tayyor xabarlarni oldindan o'qib, 900โ€“1200 eps ko'rsatishi mumkin. Bu real sustained throughput emas.

  • Throughputni oshirish: prefetch_count va consumer worker sonini oshiring:

MICRO_RABBITMQ_PREFETCH=20
# 4 ta parallel consumer
php artisan micro:consume &
php artisan micro:consume &
php artisan micro:consume &
php artisan micro:consume &

prefetch=20 + 4 consumer bilan taxminiy throughput: 215 ร— 4 ร— ~3 = ~2500 eps. Aniq raqam uchun o'z muhitingizda o'lchang.

๐ŸŽง 3. Xabarlarni Qabul Qilish (Consumer)

MicroRabbit'da xabarlarni qabul qilish uchun config fayllarda marshrutlarni ro'yxatdan o'tkazish shart emas. Tizim PHP 8 Attributes orqali Auto-Discovery (o'zini-o'zi topish) xususiyatiga ega.

Default holatda micro:cache faqat base_path('app') ichini skan qiladi. Agar handlerlar boshqa joyda bo'lsa, config/micro-rabbitmq.php dagi paths array'iga qo'shib qo'ying.

Shunchaki istalgan papkada bitta Action klass yarating va unga atributlarni qo'shing:

namespace App\Actions;

use Azizdev\MicroRabbit\Attributes\ConsumeEvent;
use Azizdev\MicroRabbit\Attributes\MicroConfig;

#[ConsumeEvent(routingKey: 'order.created', queue: 'order_processing_queue', exchange: 'micro_events')]
#[MicroConfig(tries: 5, delayMs: 15000, retry: true)]
class OrderCreatedAction
{
    public function handle(array $payload)
    {
        // 1. Kelgan datani o'qiymiz
        $orderId = $payload['id'];

        // 2. Biznes logikani bajaramiz
        // ...

        // 3. Agar RPC qilingan bo'lsa, Return yozish kifoya, MicroRabbit uni javob qilib qaytaradi!
        return ['status' => 'success', 'processed_at' => now()];
    }
}

Atributlar nima qiladi?

  1. #[ConsumeEvent] (Majburiy):
    • routingKey: Qaysi nomdagi xatni eshitishi kerakligi (masalan: user.registered).
    • queue: RabbitMQ da qaysi navbat yaratilishi kerakligi.
    • exchange: Qaysi pochtadan kutayotgani (Default: micro_events).
  2. #[MicroConfig] (Ixtiyoriy): Worker charchab qolmasligi uchun xatoliklar qoidasi.
    • tries: Agar kod xatolik (Exception) bersa, necha marta qayta urinish kerak? (Default: 3).
    • delayMs: Qayta urinishlar orasida necha millisoniya kutish (uxlash) kerak? (Default: 10000 = 10 sek).
    • retry: Qayta urinish tizimini umuman o'chirib qo'yish (false bo'lsa, 1-xatodayoq qabristonga ketadi).

DIQQAT: Klassni yozib bo'lgach, tizim uni tanib olishi uchun doim keshni yangilang:

php artisan micro:cache

๐Ÿ“จ 4. Xabar Jo'natish (Publish)

Xabar jo'natish juda oddiy. Fasaddan foydalanamiz:

use Azizdev\MicroRabbit\Facades\MicroRabbit;

// Eng sodda usul:
MicroRabbit::publish('user.registered', [
    'id' => 1,
    'name' => 'Azizbek'
]);

Tizim bu xatga avtomatik ravishda trace_id, message_id va hozirgi timestamp ni qo'shib, xavfsiz tarzda o'rab jo'natadi.

โฑ 5. RPC - Sinxron Javob Kutish

Mikroxizmatlar orasida kimdandir tezkor ma'lumot olish kerak bo'lganda (Sinxron), biz request dan foydalanamiz.

Muammo: Agar narigi xizmat o'chib yotgan bo'lsa, oddiy tizimlar cheksiz kutib, serverni qotirib qo'yadi. MicroRabbit Yechimi: Qat'iy Taymer (Strict Timeout).

use Azizdev\MicroRabbit\Facades\MicroRabbit;

try {
    // 3-parametr: 5 soniya kutish taymeri
    $response = MicroRabbit::request('get.user.balance', ['user_id' => 7], 5);
    
    echo "Foydalanuvchi balansi: " . $response['balance'];

} catch (\Exception $e) {
    // Agar 5 soniyada javob kelmasa yoki narigi servis o'chgan bo'lsa
    // Tizim qulab tushmaydi, balki chiroyli xatolik otadi!
    return response()->json(['error' => 'To\'lov tizimi vaqtinchalik ishlamayapti'], 504);
}

โšก 6. Circuit Breaker - Domino Qulashini To'xtatish

Muammo: user-service o'chib qoldi. Unga har bir RPC so'rov 5 soniya timeout bilan kutadi. Agar 100 ta concurrent so'rov bo'lsa โ€” 100 ร— 5 soniya = server thread pool to'ladi va boshqa barcha xizmatlar ham ishlashdan to'xtaydi. Bu Cascading Failure (Domino qulashi).

MicroRabbit Yechimi: Circuit Breaker. Belgilangan miqdorda xato yig'ilsa, tizim o'sha routing key uchun RPC so'rovlarni RabbitMQ ga umuman yubormasdan, darhol xato qaytaradi. Thread pool saqlanadi.

[CLOSED] โ†’ 5 xato โ†’ [OPEN] โ†’ 30 soniya โ†’ [HALF-OPEN] โ†’ muvaffaqiyat โ†’ [CLOSED]
  โœ… Normal       โŒ Blokli          ๐Ÿ” Sinov             โœ… Normal
use Azizdev\MicroRabbit\Facades\MicroRabbit;

try {
    $balance = MicroRabbit::request('get.user.balance', ['user_id' => 7], 5);

} catch (\RuntimeException $e) {
    // Circuit ochiq bo'lsa ham, timeout bo'lsa ham โ€” bu catch ga tushadi
    // $e->getMessage() da "Circuit Breaker" yoki "RPC Timeout" yozuvi bo'ladi
    return response()->json(['error' => 'Balans xizmati vaqtincha mavjud emas'], 503);
}

Circuit Breaker avtomatik ishlaydi โ€” dasturchi hech narsa qo'shimcha qilmaydi. .env orqali sozlanadi:

MICRO_RABBITMQ_CIRCUIT_BREAKER=true
MICRO_RABBITMQ_CB_THRESHOLD=5   # 5 ta xatoda circuit ochiladi
MICRO_RABBITMQ_CB_TIMEOUT=30    # 30 soniyadan keyin recovery probe o'tadi

Eslatma: Circuit Breaker faqat request() (RPC) uchun ishlaydi. Oddiy publish() asinxron bo'lgani uchun unga circuit breaker kerak emas.

๐Ÿ›ก 7. Transactional Outbox Pattern

Dahshatli ssenariy: Mijozdan pulni yechdingiz, bazaga yozdingiz. Endi RabbitMQ ga xat otayotganingizda server interneti uzildi. Mijozning puli ketdi, lekin xizmat ko'rsatilmadi!

Buning yagona yechimi โ€” Outbox Pattern. .env da MICRO_RABBITMQ_OUTBOX=true qiling. Endi MicroRabbit::publish() havo orqali xat otmaydi, balki uni o'sha vaqtdagi Tranzaksiya bilan birga Bazaga yozib qo'yadi.

use Illuminate\Support\Facades\DB;
use Azizdev\MicroRabbit\Facades\MicroRabbit;

DB::transaction(function () use ($user) {
    // 1. Pul yechiladi
    $user->decrement('balance', 50000);

    // 2. Xat bazadagi "micro_outbox" jadvaliga tushadi (Tarmoq uzilsa ham 0% xavf)
    MicroRabbit::publish('payment.success', ['user_id' => $user->id]);
});

Bazada yig'ilgan xatlarni tinimsiz ravishda RabbitMQ ga otib turuvchi pochta xizmatini serveringizda (Supervisor orqali) yoqib qo'ying:

php artisan micro:outbox:work

Bu Worker RabbitMQ o'chib qolsa ham qulab tushmaydi. U ulanish tiklanishini kutadi va o'zini-o'zi tiklaydi (Auto-Recovery).

๐Ÿงฑ 8. Chidamlilik va Himoya (Fault Tolerance)

Kutubxona "Arvoh" xatolardan quyidagicha himoyalangan:

  1. Idempotency: RabbitMQ adashib bitta xatni 2 marta jo'natib yuborsa ham, Worker qorovuli xatning ID sini eslab qoladi va 2-martasida uni ignor qiladi.
  2. Kutish Zali (Delayed Retry): Kodingiz API ga ulana olmay qulasa, worker xatni asrash uchun uni maxsus _delayed navbatiga otadi. Xat u yerda masalan 10 soniya uxlaydi va yana asosiy navbatga qaytadi.
  3. Qabriston (DLX - Dead Letter Exchange): Agar xat 3 marta urinib ham xatolik beraversa, boshqa sog'lom xatlarga xalaqit bermasligi uchun u _failed navbatiga (Qabristonga) otib yuboriladi.
  4. Circuit Breaker: RPC orqali murojaat qilingan xizmat o'chib qolsa, belgilangan xato chegarasidan keyin so'rovlar darhol rad etiladi. Thread pool to'lib qolmaydi, bitta xizmatning o'lishi qolganlarni olib ketmaydi (qarang: 6-bo'lim).
  5. Distributed Tracing (Auto): MICRO_RABBITMQ_TRACING=true bo'lsa, kelgan xabardagi trace_id avtomatik ravishda Log::withContext() orqali joriy handlerning barcha log yozuvlariga qo'shiladi. ELK/Loki da bitta so'rovni servisdan-servisga kuzatish uchun handler ichida hech narsa yozish shart emas.

Dasturchi xatoni to'g'rilagach, qabristondagi xatlarni bitta komanda bilan yana hayotga qaytarishi mumkin:

php artisan micro:retry {navbat_nomi}

๐Ÿงช 9. Test Yozish (Faking)

MicroRabbit bilan Pest yoki PHPUnit orqali test yozish "Oltin Standart" darajasida. Test paytida haqiqiy RabbitMQ ga ulanib vaqt o'tkazmaysiz.

use Azizdev\MicroRabbit\Facades\MicroRabbit;

test('buyurtma tasdiqlanganda xat otilishi kerak', function () {
    // 1. Qopqonni yoqamiz (Xatlar RabbitMQ o'rniga xotiraga tushadi)
    MicroRabbit::fake();

    // 2. Asosiy loyihaning mantiqiy qismi ishlaydi
    $this->postJson('/api/orders/confirm', ['id' => 123]);

    // 3. Xat aniq otilganiga ishonch hosil qilamiz
    MicroRabbit::assertPublished('order.confirmed');

    // 4. (Ixtiyoriy) Payload ichidagi datalarni chuqur tekshiramiz
    MicroRabbit::assertPublished('order.confirmed', function ($payload) {
        return $payload['id'] === 123;
    });

    // 5. Ortiqcha boshqa xat ketib qolmaganini tekshiramiz
    MicroRabbit::assertNotPublished('order.failed');
});

๐Ÿ’ป 10. Konsol Komandalari

  • php artisan micro:cache โ€” paths bo'yicha attributlarni topib, yashin tezligida ishlashi uchun keshga yozadi. Doim kod o'zgarganda urilishi shart.
  • php artisan micro:consume --queue={queue} โ€” Event worker'ni ishga tushiradi. --queue berilmasa barcha topilgan navbatlarni tinglaydi.
  • php artisan micro:outbox:work โ€” Outbox pattern yoqilgan bo'lsa, bazadan xatlarni olib uzluksiz RabbitMQ ga otib turuvchi asosiy dvigatel.
  • php artisan micro:retry {queue} โ€” _failed (Qabriston) ga tushib qolgan xatlarni qaytadan ishlash uchun asosiy navbatga qaytaradi.
  • php artisan micro:health โ€” Outbox backlog, DLQ hajmi va publish timeout metrikalarini tekshiradi.