Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Pêşniyar

Malpera min, ku ez wekî hobî dimeşînim, ji bo mêvandariya rûpelên malê yên balkêş û malperên kesane hatî çêkirin. Vê mijarê di destpêka rêwîtiya min a bernamesaziyê de dest pê kir ku min eleqedar bike; wê gavê ez bi dîtina pisporên mezin ên ku li ser xwe, hobî û projeyên xwe dinivîsin bala min kişand. Adet ku ez wan ji xwe re vekim heya roja îro jî dimîne: hema hema li ser her malperek bazirganî û ne pir bazirganî, ez berdewam dikim ku di lêgerîna lînkên nivîskaran de li jêrê bigerim.

Ceribandina ramanê

Guhertoya yekem tenê rûpelek html-ê li ser malpera min a kesane bû, ku min lînkên bi îmzeyan danî nav navnîşek ul. Dema ku 20 rûpel di heyamekê de nivîsandin, min dest pê kir ku ez bifikirim ku ev ne pir bandorker e û biryar da ku hewl bidim ku pêvajoyê bixweber bikim. Li ser stackoverflow, min dît ku gelek kes di profîlên xwe de malperan destnîşan dikin, ji ber vê yekê min parsekek di php-ê de nivîsand, ku bi tenê profîlan derbas kir, ji ya yekem dest pê kir (navnîşanên li ser SO-yê heya îro wiha ne: `/bikarhêner/1` ), lînkên ji etîketa xwestinê derxistin û li SQLite zêde kirin.

Ev dikare guhertoya duyemîn were gotin: berhevokek ji deh hezaran URL-yên di tabloyek SQLite de, ku li şûna navnîşa statîk a di HTML-ê de cîh girt. Min li ser vê navnîşê lêgerînek hêsan kir. Bo tenê URL hebûn, paşê lêgerîn bi tenê li ser wan bû.

Di vê qonaxê de min dev ji projeyê berda û piştî demeke dirêj vegeriyam. Di vê qonaxê de, ezmûna xebata min ji sê salan zêdetir bû û min hest kir ku ez dikarim tiştek cidîtir bikim. Wekî din, xwestekek mezin hebû ku meriv teknolojiyên nû yên nû bidest bixe.

Guhertoya nûjen

Projeyê di Docker de hate bicîh kirin, databas ji mongoDb re hate veguheztin, û herî dawî, radish hate zêdekirin, ku di destpêkê de tenê ji bo cachkirinê bû. Yek ji mîkroframeworkên PHP-ê wekî bingehek tê bikar anîn.

pirsgirêka

Malperên nû ji hêla fermanek konsolê ve têne zêdekirin ku bi hevdemî jêrîn dike:

  • Naveroka bi URL-ê dadixe
  • Alayek destnîşan dike ku destnîşan dike ka HTTPS heye an na
  • Esasê malperê diparêze
  • Çavkaniya HTML û sernivîsan di dîroka "indekskirin" de têne tomar kirin
  • Naveroka pars dike, Sernav û Danasînê derdixe
  • Daneyên ji bo berhevokek cuda tomar dike

Ev bes bû ku tenê malperan hilîne û wan di navnîşek de nîşan bide:

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Lê ramana bixweber îndekskirin, kategorîzekirin û rêzkirina her tiştî, rojanekirina her tiştî, di vê paradîgmayê de baş negirt. Tewra bi tenê lê zêdekirina rêbazek malperê ji bo lê zêdekirina rûpelan pêdivî ye ku kodê dubare û asteng bike da ku DDoS-ya potansiyel dûr bixe.

Bi gelemperî, bê guman, her tişt dikare bi hevdemî were kirin, û di rêbaza webê de hûn dikarin URL-ê bi hêsanî hilînin da ku daemonê cinawir hemî karan ji bo URL-ên ji navnîşê pêk bîne. Lê dîsa jî, li vir jî peyva "doz" xwe pêşniyar dike. Û heke rêzek were bicîh kirin, wê hingê hemî peywir dikarin bi kêmî ve asynchronous bêne dabeş kirin û bêne kirin.

biryar

Ji bo pêvajoykirina hemî peywiran rêzan bicîh bikin û pergalek bûyer-rêveber çêbikin. Û ez demek dirêj dixwazim ku Redis Streams biceribînim.

Di PHP-ê de herikên Redis bikar tînin

Bo Ji ber ku çarçoweya min ne yek ji sê dêwên Symfony, Laravel, Yii ye, ez dixwazim pirtûkxaneyek serbixwe bibînim. Lê, wekî ku derket (di ceribandina yekem de), ne gengaz e ku meriv pirtûkxaneyên ciddî yên kesane bibîne. Her tiştê ku bi rêzan ve girêdayî ye an projeyek ji 3 peywirên pênc sal berê ye, an jî bi çarçoveyê ve girêdayî ye.

Min li ser Symfony wekî dabînkerê hêmanên kêrhatî yên takekesî pir bihîstiye, û ez berê hin ji wan bikar tînim. Û her weha hin tiştên ji Laravel jî dikarin bêne bikar anîn, mînakî ORM-ya wan, bêyî hebûna çarçoweyê bixwe.

senfonî / peyamnêr

Namzedê yekem tavilê îdeal xuya bû û bê guman min ew saz kir. Lê derket holê ku google nimûneyên karanîna li derveyî Symfony dijwartir bû. Meriv çawa ji komek çînên bi navên gerdûnî, bêwate, otobusek ji bo derbaskirina peyaman, û tewra li ser Redis-ê kom dibe?

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Belgekirina li ser malpera fermî pir berfireh bû, lê destpêkkirin tenê ji bo Symfony-ê bi karanîna YML-ya xweya bijare û rêbazên din ên sêrbaz ji bo ne-senfonîst hate vegotin. Tu eleqeya min bi pêvajoya sazkirinê bi xwe re nemabû, nemaze di betlaneyên Sersalê de. Lê ez neçar bûm ku ji bo demek dirêj a neçaverêkirî vê yekê bikim.

Hewldana ku hûn fêr bibin ka meriv çawa pergalek bi karanîna çavkaniyên Symfony-ê destnîşan dike di heman demê de ji bo demek teng ne karê herî piçûk e:

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Piştî ku ez li van hemûyan ketim û hewl da ku ez bi destên xwe tiştek bikim, ez gihîştim wê encamê ku ez celebek kêşan dikim û biryar da ku ez tiştek din biceribînim.

ronî kirin / rêz

Derket holê ku ev pirtûkxane bi binesaziya Laravel û komek girêdanên din ve zexm ve girêdayî bû, ji ber vê yekê min pir wext li ser nehişt: Min ew saz kir, lê nihêrî, pêwendî dîtin û jêbirin.

yiisoft/yii2-queue

Welê, li vir tavilê ji navê, dîsa, pêwendiyek hişk bi Yii2 re hate texmîn kirin. Ez neçar bûm ku vê pirtûkxaneyê bikar bînim û ew ne xirab bû, lê ez li ser vê yekê nefikirîm ku ew bi tevahî bi Yii2 ve girêdayî ye.

Ya mayî

Tiştên din ên ku min li ser GitHub dît, projeyên ne pêbawer, kevnar û dev jê berda bûn, bêyî stêrk, fork û hejmareke mezin ji peywiran.

Vegere symfony/messenger, hûrguliyên teknîkî

Ez neçar bûm ku vê pirtûkxaneyê bibînim û, piştî ku demek din derbas kir, min karîbû. Derket holê ku her tişt pir kurt û sade bû. Ji bo destnîşankirina otobusê, min kargehek piçûk çêkir, ji ber ku ... Diviyabû ku min çend tirên û bi handlerên cihê hebin.

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Tenê çend gavan:

  • Em rêvekerên peyamê diafirînin ku divê bi hêsanî werin bang kirin
  • Em wan di HandlerDescriptor de (pola ji pirtûkxaneyê) dipêçin
  • Em van "Descriptors" di mînakek HandlersLocator de dipêçin
  • Zêdekirina HandlersLocator li mînaka MessageBus
  • Em komek `SenderInterface` ji SendersLocator re derbas dikin, di rewşa min de mînakên dersên `RedisTransport`, ku bi rengek eşkere têne mîheng kirin.
  • Zêdekirina SendersLocator li mînaka MessageBus

MessageBus rêbazek `->dispatch()` heye ku di HandlersLocatorê de rêwerzên guncav digere û peyamê ji wan re derbas dike, bi karanîna `SenderInterface` ya têkildar ji bo şandina bi otobusê (herikên Redis) bikar tîne.

Di veavakirina konteynerê de (di vê rewşê de php-di), ev tevnhev dikare bi vî rengî were mîheng kirin:

        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);
        },

Li vir hûn dikarin bibînin ku di SendersLocator-ê de me ji bo du peyamên cihêreng "veguheztin" veqetandiye, ku her yek ji wan pêwendiya xwe bi pêlên têkildar re heye.

Min projeyek demo ya cihê çêkir ku serîlêdana sê şeytan ku bi otobusa jêrîn bi hevûdu re danûstendinê nîşan didin: https://github.com/backend-university/products/tree/master/products/02-redis-streams-bus.

Lê ez ê nîşanî we bidim ka xerîdarek çawa dikare were saz kirin:

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();

Di serîlêdanê de vê binesaziyê bikar bînin

Piştî ku otobusê di pişta xwe de bicîh kir, min qonaxên ferdî ji fermana hevdemî ya kevn veqetand û rêvebirên cihêreng çêkir, ku her yek ji wan karê xwe dike.

Rêza lê zêdekirina malperek nû li databasê wiha xuya bû:

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Û tavilê piştî wê, ji min re pir hêsantir bû ku ez fonksiyonek nû lê zêde bikim, mînakî derxistin û parkirina Rs. Bo ev pêvajo jî naveroka orîjînal hewce dike, dûv re rêvebirê derxistina girêdana RSS, mîna WebsiteIndexHistoryPersistor, beşdarî peyama "Naverok / HtmlContent" dibe, wê pêvajo dike û peyama xwestî bi xeta xwe ya din re derbas dike.

Veguheztina pişta PHP-ê li otobusa streams Redis û hilbijartina pirtûkxaneyek serbixwe-çarçove

Di dawiyê de, me bi çend şeytan re bi dawî bû, ku her yek ji wan tenê bi çavkaniyên pêwîst re girêdanan diparêze. Mînak cin crawler Hemî hilberên ku ji bo naverokê hewce ne ku biçin Înternetê, û daemon vedihewîne li sersekinîn têkiliyek bi databasê re digire.

Naha, li şûna ku ji databasê were hilbijartin, nasnameyên pêwîst, piştî ku ji hêla domdar ve têne danîn, bi tenê bi otobusê ji hemî hilgirên eleqedar re têne şandin.

Source: www.habr.com

Add a comment