PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

muqaddima

Xobbi sifatida ishlayotgan veb-saytim qiziqarli uy sahifalari va shaxsiy saytlarni joylashtirish uchun mo'ljallangan. Bu mavzu meni dasturlash sayohatimning boshida qiziqtira boshladi; o'sha paytda o'zlari, sevimli mashg'ulotlari va loyihalari haqida yozadigan ajoyib mutaxassislarni topish meni hayratda qoldirdi. Ularni o'zim uchun kashf qilish odati bugungi kungacha saqlanib qolgan: deyarli har bir tijorat va unchalik tijorat bo'lmagan saytlarda men mualliflarga havolalarni izlash uchun pastki qismga qarashni davom ettiraman.

G'oyani amalga oshirish

Birinchi versiya mening shaxsiy veb-saytimdagi html sahifasi edi, u erda men imzolar bilan havolalarni ul ro'yxatiga joylashtirdim. Muayyan vaqt ichida 20 sahifani yozganimdan so'ng, men bu unchalik samarali emas deb o'ylay boshladim va jarayonni avtomatlashtirishga harakat qilishga qaror qildim. Stackoverflow-da men ko'p odamlar o'z profillarida saytlarni ko'rsatayotganini payqadim, shuning uchun men php-da tahlilchini yozdim, u birinchisidan boshlab profillar bo'ylab o'tdi (hozirgi kunga qadar SO manzillari quyidagicha: `/users/1` ), kerakli tegdan havolalarni ajratib oling va uni SQLite-ga qo'shing.

Buni ikkinchi versiya deb atash mumkin: SQLite jadvalidagi o'n minglab URL-manzillar to'plami, html-dagi statik ro'yxat o'rnini egalladi. Men ushbu ro'yxatda oddiy qidiruv qildim. Chunki faqat URL manzillari bor edi, keyin qidiruv shunchaki ularga asoslangan edi.

Ushbu bosqichda men loyihadan voz kechdim va uzoq vaqtdan keyin unga qaytdim. Ushbu bosqichda mening ish tajribam uch yildan oshdi va men jiddiyroq ish qilishim mumkinligini his qildim. Bundan tashqari, nisbatan yangi texnologiyalarni o'zlashtirishga katta ishtiyoq bor edi.

Zamonaviy versiya

Loyiha Docker-da joylashtirilgan ma'lumotlar bazasi mongoDb-ga o'tkazildi va yaqinda turp qo'shildi, bu dastlab faqat keshlash uchun edi. PHP mikroramkalaridan biri asos sifatida ishlatiladi.

muammo

Yangi saytlar konsol buyrug'i bilan qo'shiladi, u sinxron ravishda quyidagilarni bajaradi:

  • Kontentni URL orqali yuklab oladi
  • HTTPS mavjudligini ko'rsatadigan bayroqni o'rnatadi
  • Veb-saytning mohiyatini saqlaydi
  • Manba HTML va sarlavhalar "indekslash" tarixida saqlanadi
  • Tarkibni tahlil qiladi, Sarlavha va Tavsifni chiqaradi
  • Ma'lumotlarni alohida to'plamga saqlaydi

Bu shunchaki saytlarni saqlash va ularni ro'yxatda ko'rsatish uchun etarli edi:

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Ammo hamma narsani avtomatik ravishda indekslash, toifalash va tartiblash, hamma narsani yangilab turish g'oyasi ushbu paradigmaga yaxshi mos kelmadi. Hatto sahifalarni qo'shish uchun oddiygina veb-usulini qo'shish uchun kodni takrorlash va potentsial DDoS oldini olish uchun blokirovka qilish kerak edi.

Umuman olganda, albatta, hamma narsa sinxron tarzda amalga oshirilishi mumkin va veb-usulda siz shunchaki URL-manzilni saqlashingiz mumkin, shunda dahshatli daemon ro'yxatdagi URL-manzillar uchun barcha vazifalarni bajaradi. Ammo shunga qaramay, bu erda ham "navbat" so'zi o'zini ko'rsatadi. Va agar navbat amalga oshirilsa, unda barcha vazifalar bo'linishi va hech bo'lmaganda asinxron tarzda bajarilishi mumkin.

qaror

Navbatlarni amalga oshiring va barcha vazifalarni qayta ishlash uchun voqealarga asoslangan tizim yarating. Men uzoq vaqtdan beri Redis Streams-ni sinab ko'rmoqchi edim.

PHP da Redis oqimlaridan foydalanish

Chunki Mening ramkam Symfony, Laravel, Yii uchta gigantdan biri emasligi sababli, men mustaqil kutubxona topmoqchiman. Ammo, ma'lum bo'lishicha, (birinchi tekshirishda) alohida jiddiy kutubxonalarni topish mumkin emas. Navbatlar bilan bog'liq bo'lgan hamma narsa besh yil oldin 3 ta topshiriqdan olingan loyiha yoki ramkaga bog'langan.

Shaxsiy foydali komponentlar yetkazib beruvchi sifatida Symfony haqida ko'p eshitganman va ulardan ba'zilarini allaqachon ishlataman. Shuningdek, Laravel-dan ba'zi narsalar, masalan, ularning ORM-si, ramkaning o'zi bo'lmasdan ham ishlatilishi mumkin.

symfony/messenger

Birinchi nomzod darhol ideal bo'lib tuyuldi va hech qanday shubhasiz uni o'rnatdim. Ammo Symfony-dan tashqari foydalanish misollarini Google orqali topish qiyinroq bo'lib chiqdi. Umumjahon, ma'nosiz nomlar, xabarlarni uzatish uchun avtobus va hatto Redis-da bir nechta sinflarni qanday yig'ish mumkin?

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Rasmiy saytdagi hujjatlar juda batafsil edi, lekin ishga tushirish faqat Symfony uchun sevimli YML va simfonist bo'lmaganlar uchun boshqa sehrli usullardan foydalangan holda tasvirlangan. O'rnatish jarayonining o'zi, ayniqsa, Yangi yil bayramlarida menda qiziqish yo'q edi. Lekin men buni kutilmaganda uzoq vaqt qilishim kerak edi.

Symfony manbalaridan foydalangan holda tizimni qanday yaratishni aniqlashga urinish ham qisqa muddat uchun eng ahamiyatsiz vazifa emas:

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Bularning barchasini chuqur o'rganib, qo'lim bilan nimadir qilishga urinib ko'rganimdan so'ng, men qandaydir tayoqchalar bilan shug'ullanyapman degan xulosaga keldim va boshqa narsani sinab ko'rishga qaror qildim.

yoritilgan/navbat

Ma'lum bo'lishicha, bu kutubxona Laravel infratuzilmasi va boshqa bir qancha bog'liqliklar bilan chambarchas bog'langan, shuning uchun men bunga ko'p vaqt sarflamadim: men uni o'rnatdim, qaradim, bog'liqliklarni ko'rdim va o'chirib tashladim.

yiisoft/yii2-navbat

Xo'sh, bu erda u darhol nomidan, yana Yii2 bilan qattiq bog'liqlikdan kelib chiqqan. Men bu kutubxonadan foydalanishim kerak edi va bu yomon emas edi, lekin bu butunlay Yii2 ga bog'liq ekanligi haqida o'ylamagan edim.

Qolganlari

GitHub-da topilgan barcha narsalar yulduzlar, vilkalar va ko'p miqdordagi majburiyatlarsiz ishonchsiz, eskirgan va tashlab qo'yilgan loyihalar edi.

Symfony/messenger, texnik tafsilotlarga qaytish

Men bu kutubxonani aniqlashim kerak edi va yana bir oz vaqt sarflaganimdan so'ng, men bunga muvaffaq bo'ldim. Ma'lum bo'lishicha, hamma narsa juda qisqa va sodda edi. Avtobusni yaratish uchun men kichik zavod qurdim, chunki ... Menda bir nechta shinalar va turli xil ishlov beruvchilar bo'lishi kerak edi.

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Bir necha qadam:

  • Biz shunchaki qo'ng'iroq qilish mumkin bo'lgan xabarlarni qayta ishlovchilarni yaratamiz
  • Biz ularni HandlerDescriptor-ga o'rab olamiz (kutubxonadan sinf)
  • Biz ushbu "Deskriptorlarni" HandlersLocator misolida o'rab olamiz
  • MessageBus misoliga HandlersLocator qo'shish
  • Biz SendersLocator-ga "SenderInterface" to'plamini o'tkazamiz, mening holimda "RedisTransport" sinflarining misollari aniq tarzda sozlangan.
  • MessageBus misoliga SendersLocator qo'shish

MessageBus `->dispatch()` usuliga ega bo'lib, u HandlersLocator-da tegishli ishlov beruvchilarni qidiradi va avtobus (Redis oqimlari) orqali jo'natish uchun tegishli `SenderInterface` yordamida xabarni ularga uzatadi.

Konteyner konfiguratsiyasida (bu holda php-di) bu butun to'plamni quyidagicha sozlash mumkin:

        CONTAINER_REDIS_TRANSPORT_SECRET => function (ContainerInterface $c) {
            return new RedisTransport(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_SECRET),
                $c->get(CONTAINER_SERIALIZER))
            ;
        },
        CONTAINER_REDIS_TRANSPORT_LOG => function (ContainerInterface $c) {
            return new RedisTransport(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_LOG),
                $c->get(CONTAINER_SERIALIZER))
            ;
        },
        CONTAINER_REDIS_STREAM_RECEIVER_SECRET => function (ContainerInterface $c) {
            return new RedisReceiver(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_SECRET),
                $c->get(CONTAINER_SERIALIZER)
            );
        },
        CONTAINER_REDIS_STREAM_RECEIVER_LOG => function (ContainerInterface $c) {
            return new RedisReceiver(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_LOG),
                $c->get(CONTAINER_SERIALIZER)
            );
        },
        CONTAINER_REDIS_STREAM_BUS => function (ContainerInterface $c) {
            $sendersLocator = new SendersLocator([
                AppMessagesSecretJsonMessages::class => [CONTAINER_REDIS_TRANSPORT_SECRET],
                AppMessagesDaemonLogMessage::class => [CONTAINER_REDIS_TRANSPORT_LOG],
            ], $c);
            $middleware[] = new SendMessageMiddleware($sendersLocator);

            return new MessageBus($middleware);
        },
        CONTAINER_REDIS_STREAM_CONNECTION_SECRET => function (ContainerInterface $c) {
            $host = 'bu-02-redis';
            $port = 6379;
            $dsn = "redis://$host:$port";
            $options = [
                'stream' => 'secret',
                'group' => 'default',
                'consumer' => 'default',
            ];

            return Connection::fromDsn($dsn, $options);
        },
        CONTAINER_REDIS_STREAM_CONNECTION_LOG => function (ContainerInterface $c) {
            $host = 'bu-02-redis';
            $port = 6379;
            $dsn = "redis://$host:$port";
            $options = [
                'stream' => 'log',
                'group' => 'default',
                'consumer' => 'default',
            ];

            return Connection::fromDsn($dsn, $options);
        },

Bu erda siz SendersLocator-da biz ikkita turli xabarlar uchun turli xil "transportlarni" tayinlaganimizni ko'rishingiz mumkin, ularning har biri tegishli oqimlarga o'z ulanishiga ega.

Men quyidagi avtobus yordamida bir-biri bilan aloqa qiladigan uchta demon ilovasini namoyish qiluvchi alohida demo loyihasini yaratdim: https://github.com/backend-university/products/tree/master/products/02-redis-streams-bus.

Ammo men sizga iste'molchi qanday tuzilishi mumkinligini ko'rsataman:

use AppMessagesDaemonLogMessage;
use SymfonyComponentMessengerHandlerHandlerDescriptor;
use SymfonyComponentMessengerHandlerHandlersLocator;
use SymfonyComponentMessengerMessageBus;
use SymfonyComponentMessengerMiddlewareHandleMessageMiddleware;
use SymfonyComponentMessengerMiddlewareSendMessageMiddleware;
use SymfonyComponentMessengerTransportSenderSendersLocator;

require_once __DIR__ . '/../vendor/autoload.php';
/** @var PsrContainerContainerInterface $container */
$container = require_once('config/container.php');

$handlers = [
    DaemonLogMessage::class => [
        new HandlerDescriptor(
            function (DaemonLogMessage $m) {
                error_log('DaemonLogHandler: message handled: / ' . $m->getMessage());
            },
            ['from_transport' => CONTAINER_REDIS_TRANSPORT_LOG]
        )
    ],
];
$middleware = [];
$middleware[] = new HandleMessageMiddleware(new HandlersLocator($handlers));
$sendersLocator = new SendersLocator(['*' => [CONTAINER_REDIS_TRANSPORT_LOG]], $container);
$middleware[] = new SendMessageMiddleware($sendersLocator);

$bus = new MessageBus($middleware);
$receivers = [
    CONTAINER_REDIS_TRANSPORT_LOG => $container->get(CONTAINER_REDIS_STREAM_RECEIVER_LOG),
];
$w = new SymfonyComponentMessengerWorker($receivers, $bus, $container->get(CONTAINER_EVENT_DISPATCHER));
$w->run();

Ilovada ushbu infratuzilmadan foydalanish

Avtobusni orqa qismimda amalga oshirgandan so'ng, men eski sinxron buyruqdan alohida bosqichlarni ajratdim va har biri o'z ishini bajaradigan alohida ishlov beruvchilarni yaratdim.

Ma'lumotlar bazasiga yangi sayt qo'shish sxemasi quyidagicha ko'rinishga ega edi:

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Va shundan so'ng darhol menga yangi funksiyalarni qo'shish, masalan, Rss-ni ajratib olish va tahlil qilish osonroq bo'ldi. Chunki bu jarayon asl tarkibni ham talab qiladi, keyin WebsiteIndexHistoryPersistor kabi RSS havolasini chiqarish moslamasi "Content/HtmlContent" xabariga obuna bo'ladi, uni qayta ishlaydi va kerakli xabarni quvur liniyasi bo'ylab uzatadi.

PHP backendini Redis oqimlari avtobusiga o'tkazish va ramkadan mustaqil kutubxonani tanlash

Oxir-oqibat, biz bir nechta demonlarga ega bo'ldik, ularning har biri faqat kerakli manbalarga ulanishni ta'minlaydi. Masalan, jin brauzerlarning kontent uchun Internetga kirishni talab qiladigan barcha ishlov beruvchilarni va demonni o'z ichiga oladi davom eting ma'lumotlar bazasiga ulanishga ega.

Endi, ma'lumotlar bazasidan tanlash o'rniga, persister tomonidan kiritilgandan so'ng, kerakli identifikatorlar avtobus orqali barcha manfaatdor ishlovchilarga uzatiladi.

Manba: www.habr.com

a Izoh qo'shish