window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster już istnieje') } else { w.LeadBooster = { q: [], on: function (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: function (n) { this.q.push({ t: 't', n: n }) }, } } })() Symfony: Komunikacja mikrousług część I - The Codest
The Codest
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Branże
    • Fintech i bankowość
    • E-commerce
    • Adtech
    • Healthtech
    • Produkcja
    • Logistyka
    • Motoryzacja
    • IOT
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
  • O nas
  • Nasze Usługi
    • Software Development
      • Frontend Development
      • Backend Development
    • Zespoły IT
      • Programiści frontendowi
      • Backend Dev
      • Inżynierowie danych
      • Inżynierowie rozwiązań chmurowych
      • Inżynierowie QA
      • Inne
    • Konsultacje IT
      • Audyt i doradztwo
  • Wartość dla
    • CEO
    • CTO
    • Delivery Managera
  • Nasz zespół
  • Case Studies
  • Nasze Know How
    • Blog
    • Meetups
    • Webinary
    • Raporty
Kariera Skontaktuj się z nami
Strzałka w tył WSTECZ
2022-06-28
Software Development

Symfony: Komunikacja mikrousług część I

The Codest

Sebastian Łuczak

Lider jednostki PHP

Przeczytaj pierwszą część naszej serii PHP poświęconej komunikacji mikroserwisów we frameworku Symfony i najpopularniejszemu sposobowi - komunikacji AMQP z wykorzystaniem RabbitMQ.

Nowoczesna architektura aplikacji wymusiła na deweloperach zmianę sposobu myślenia o komunikacji pomiędzy poszczególnymi komponentami systemów informatycznych. Kiedyś sprawa była prostsza - większość systemów tworzona była jako monolityczne struktury połączone ze sobą siecią powiązań logiki biznesowej. Utrzymanie takich zależności w PHP projekt było ogromnym wyzwaniem dla Programiści PHPa także rosnąca popularność rozwiązań SaaS i ogromny wzrost popularności chmura Usługi spowodowały, że dziś coraz częściej słyszymy o mikrousługach i modułowości aplikacji.

W jaki sposób, tworząc niezależne mikrousługi, możemy sprawić, by wymieniały one między sobą informacje?

Ten artykuł jest pierwszym z serii postów na temat Komunikacja mikrousług w Symfony i obejmuje najpopularniejszy sposób - komunikację AMQP przy użyciu RabbitMQ.

Cel

Aby utworzyć dwie niezależne aplikacje i osiągnąć komunikację między nimi przy użyciu tylko magistrali komunikatów.

Koncepcja

Mamy dwie wyimaginowane, niezależne aplikacje:
* app1: który wysyła powiadomienia e-mail i SMS do pracowników
* app2który pozwala zarządzać pracą pracowników i przydzielać im zadania.

Chcemy stworzyć nowoczesny i prosty system, dzięki któremu przydzielanie pracy pracownikowi w app2 wyśle powiadomienie do klienta przy użyciu app1. Wbrew pozorom jest to bardzo proste!

Przygotowanie

Na potrzeby tego artykułu użyjemy najnowszej wersji Symfony (wersja 6.1 w momencie pisania) i najnowszej wersji PHP (8.1). W kilku bardzo prostych krokach stworzymy działające lokalne środowisko Docker z dwoma mikroserwisami. Wszystko czego potrzebujesz to:
* działający komputer,
* zainstalowany Docker + Środowisko Docker Compose
* i lokalnie skonfigurowany Symfony CLI i trochę wolnego czasu.

Środowisko uruchomieniowe

Wykorzystamy możliwości Dockera jako narzędzia do wirtualizacji i konteneryzacji aplikacji. Zacznijmy od utworzenia drzewa katalogów, szkieletu dla dwóch Aplikacje Symfonyi opisać infrastrukturę naszych środowisk przy użyciu docker-compose.yml plik.

 cd ~
 mkdir microservices-in-symfony
 cd microservices-in-symfony
 symfony new app1
 symfony new app2
 touch docker-compose.yml

Stworzyliśmy dwa katalogi dla dwóch oddzielnych aplikacji Symfony i utworzyliśmy pusty katalog docker-compose.yml aby uruchomić nasze środowisko.

Dodajmy następujące sekcje do docker-compose.yml file:

version: '3.8'

usługi:
app1:
containername: app1
build: app1/.
restart: on-failure
envfile: app1/.env
environment:
APPNAME: app1
tty: true
stdinopen: true

app2:
containername: app2
build: app2/.
restart: on-failure
envfile: app2/.env
environment:
APPNAME: app2
tty: true
stdinopen: true

rabbitmq:
containername: rabbitmq
image: rabbitmq:management
ports:
- 15672:15672
- 5672:5672
environment:
- RABBITMQDEFAULTUSER=user
- RABBITMQDEFAULT_PASS=hasło

Źródło kod dostępne bezpośrednio: thecodest-co/microservices-in-symfony/blob/main/docker-compose.yml

Ale zaraz, co tu się stało? Dla osób niezaznajomionych z Dockerem powyższy plik konfiguracyjny może wydawać się enigmatyczny, jednak jego cel jest bardzo prosty. Korzystając z Docker Compose budujemy trzy "usługi":

  • app1: który jest kontenerem dla pierwszej aplikacji Symfony
  • app2: który jest kontenerem dla drugiej aplikacji Symfony
  • rabbitmq: obraz aplikacji RabbitMQ jako warstwa pośrednicząca komunikacji

Do prawidłowego działania nadal potrzebujemy Plik Docker które są źródłem do tworzenia obrazów. Więc stwórzmy je:

 touch app1/Dockerfile
 touch app2/Dockerfile

Oba pliki mają dokładnie taką samą strukturę i wyglądają następująco:

Z php:8.1

COPY --from=composer:latest /usr/bin/composer /usr/local/bin/composer
COPY . /app/
WORKDIR /app/

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && sync &&
install-php-extensions amqp

RUN apt-get update
&& apt-get install -y libzip-dev wget --no-install-recommends
&& apt-get clean
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*

RUN docker-php-ext-install zip;

CMD bash -c "cd /app && composer install && php -a"

Kod źródłowy dostępny bezpośrednio: /thecodest-co/microservices-in-symfony/blob/main/app1/Dockerfile

Powyższy plik jest używany przez Docker Compose do zbudowania kontenera z obrazu PHP 8.1 z zainstalowanym Composerem i rozszerzeniem AMQP. Dodatkowo uruchamia intepreter PHP w trybie append, aby kontener działał w tle.

Drzewo katalogów i plików powinno teraz wyglądać następująco:

 .
 ├── app1
 │ └── Dockerfile
 | (...) # Struktura aplikacji Symfony
 ├── app2
 │ └── Dockerfile
 | (...) # Struktura aplikacji Symfony
 └── docker-compose.yml

Pierwszy mikroserwis Symfony

Zacznijmy od app1 i pierwszej aplikacji.
W naszym przykładzie jest to aplikacja, która nasłuchuje i pobiera wiadomości z kolejki wysyłanej przez app2 zgodnie z opisem w wymaganiach:

przypisanie zadania do pracownika w app2 wyśle powiadomienie do klienta

Zacznijmy od dodania wymaganych bibliotek. AMQP jest natywnie obsługiwany dla symfony/messenger rozszerzenie. Dodatkowo zainstalujemy monolog/monolog do śledzenia dzienników systemowych w celu łatwiejszej analizy zachowania aplikacji.

 cd app1/
 symfony composer req amqp ampq-messenger monolog

Po instalacji dodano dodatkowy plik w sekcji config/packages/messenger.yaml. Jest to plik konfiguracyjny dla komponentu Symfony Messenger i nie potrzebujemy go pełna konfiguracja.
Zastąp go poniższym plikiem YAML:

ramy:
messenger:
# Usuń ten komentarz (i nieudany transport poniżej), aby wysyłać nieudane wiadomości do tego transportu w celu późniejszej obsługi.
# failure_transport: failed

    transports:
        # https://symfony.com/doc/current/messenger.html#transport-configuration
        external_messages:
            dsn: "%env(MESSENGER_TRANSPORT_DSN)%
            options:
                auto_setup: false
                exchange:
                    name: messages
                    typ: bezpośredni
                    default_publish_routing_key: from_external
                queues:
                    messages:
                        binding_keys: [from_external]

Kod źródłowy dostępny bezpośrednio: thecodest-co/microservices-in-symfony/blob/main/app1/config/packages/messenger.yaml

Symfony Messenger służy do synchronicznej i asynchronicznej komunikacji w aplikacjach Symfony. Obsługuje wiele różnych transportyczyli źródła prawdy warstwy transportowej. W naszym przykładzie używamy rozszerzenia AMQP, które obsługuje system kolejek zdarzeń RabbitMQ.

Powyższa konfiguracja definiuje nowy transport o nazwie external_messagesktóry odwołuje się do MESSENGER_TRANSPORT_DSN i definiuje bezpośrednie nasłuchiwanie na wiadomości w Message Bus. W tym momencie należy również edytować app1/.env i dodać odpowiedni adres transportowy.

"`env

(...)
MESSENGERTRANSPORTDSN=amqp://user:password@rabbitmq:5672//messages
(...)
"
Po przygotowaniu szkieletu aplikacji i skonfigurowaniu bibliotek, nadszedł czas na implementację logiki biznesowej. Wiemy, że nasza aplikacja musi reagować na przypisanie zadania do pracownika. Wiemy również, że przypisanie zadania wapp2system zmienia status zadania. Stwórzmy więc model, który naśladuje zmianę statusu i zapiszmy go w ścieżceapp1/Message/StatusUpdate.php`:
obraz
 {
public function __construct(protected string $status){}

public function getStatus(): string
{
    return $this->status;
}

}

Kod źródłowy dostępny bezpośrednio: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Message/StatusUpdate.php

Nadal potrzebujemy klasy, która zaimplementuje logikę biznesową, gdy nasza mikrousługa otrzyma powyższe zdarzenie z kolejki. Stwórzmy więc klasę Obsługa komunikatów w app1/Handler/StatusUpdateHandler.php ścieżka:

obraz
 use PsrLogLoggerInterface;
use SymfonyComponentMessengerAttributeAsMessageHandler;

[AsMessageHandler]

class StatusUpdateHandler
{
public function __construct(
protected LoggerInterface $logger,
) {}

public function __invoke(StatusUpdate $statusUpdate): void
{
    $statusDescription = $statusUpdate->getStatus();

    $this->logger->warning('APP1: {STATUS_UPDATE} - '.$statusDescription);

    // reszta logiki biznesowej, tj. wysyłanie wiadomości e-mail do użytkownika
    // $this->emailService->email()
}

}

Kod źródłowy dostępny bezpośrednio: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Handler/StatusUpdateHandler.php

PHP znacznie ułatwiają sprawę i oznaczają, że w tym konkretnym przypadku nie musimy martwić się o autowiring czy deklarację usługi. Nasza mikrousługa do obsługi zdarzeń domenowych jest gotowa, czas zabrać się za drugą aplikację.

Drugi mikroserwis Symfony

Przyjrzymy się app2 i drugi katalog Aplikacja Symfony. Naszą ideą jest wysyłanie wiadomości do kolejki, gdy pracownik otrzyma zadanie w systemie. Wykonajmy więc szybką konfigurację AMQP i uruchommy naszą drugą mikrousługę, aby rozpocząć publikację StatusUpdate zdarzeń do magistrali komunikatów.

Instalacja bibliotek przebiega dokładnie tak samo, jak w przypadku pierwszej aplikacji.

 cd ..
 cd app2/
 symfony composer req amqp ampq-messenger monolog

Upewnijmy się, że app2/.env zawiera prawidłowy wpis DSN dla RabbitMQ:

(...)
 MESSENGER_TRANSPORT_DSN=amqp://user:password@rabbitmq:5672//messages
 (...)

Pozostaje tylko skonfigurować Symfony Messenger w sekcji app2/config/packages/messenger.yaml file:

ramy:
messenger:
# Usuń ten komentarz (i nieudany transport poniżej), aby wysyłać nieudane wiadomości do tego transportu w celu późniejszej obsługi.
# failure_transport: failed

    transports:
        # https://symfony.com/doc/current/messenger.html#transport-configuration
        async:
            dsn: '%env(MESSENGER_TRANSPORT_DSN)%'

    routing:
        # Przekieruj wiadomości do transportów
        'AppMessageStatusUpdate': async

Jak widać, tym razem definicja transportu wskazuje bezpośrednio na asynchroniczny i definiuje routing w postaci wysyłania naszych StatusUpdate do skonfigurowanej sieci DSN. Jest to jedyny obszar konfiguracji, pozostaje tylko stworzyć logikę i warstwę implementacji kolejki AMQP. W tym celu utworzymy bliźniaczą StatusUpdateHandler i StatusUpdate zajęcia w app2.

obraz
 use PsrLogLoggerInterface;
use SymfonyComponentMessengerAttributeAsMessageHandler;

[AsMessageHandler]

class StatusUpdateHandler
{
public function __construct(
private readonly LoggerInterface $logger,
) {}

public function __invoke(StatusUpdate $statusUpdate): void
{
    $statusDescription = $statusUpdate->getStatus();

    $this->logger->warning('APP2: {STATUS_UPDATE} - '.$statusDescription);

    ## logika biznesowa, tj. wysyłanie wewnętrznych powiadomień lub kolejkowanie innych systemów
}

}
obraz
 {
public function __construct(protected string $status){}

public function getStatus(): string
{
    return $this->status;
}

}


Kod źródłowy dostępny bezpośrednio: /thecodest-co/microservices-in-symfony/blob/main/app2/src/Message/StatusUpdate.php

Na koniec wszystko, co należy zrobić, to stworzyć sposób wysyłania wiadomości do magistrali komunikatów. Stworzymy prostą aplikację Polecenie Symfony za to:

obraz
use SymfonyComponentConsoleAttributeAsCommand;
use SymfonyComponentConsoleCommandCommand;
use SymfonyComponentConsoleInputInputInterface;
use SymfonyComponentConsoleOutputOutputInterface;
use SymfonyComponentMessengerMessageBusInterface;

[AsCommand(

name: "app:send"

)]
class SendStatusCommand extends Command
{
public function construct(private readonly MessageBusInterface $messageBus, string $name = null)
{
parent::construct($name);
}

protected function execute(InputInterface $input, OutputInterface $output): int
{
    $status = "Pracownik X przypisany do Y";

    $this->messageBus->dispatch(
        message: new StatusUpdate($status)
    );

    return Command::SUCCESS;
}

}

Dzięki Wstrzykiwanie zależności możemy użyć instancji MessageBusInterface w naszym poleceniu i wysłać StatusUpdate wiadomość za pośrednictwem dispatch() do naszej kolejki. Dodatkowo używamy tutaj również atrybutów PHP.

To wszystko - pozostaje tylko uruchomić nasze środowisko Docker Compose i sprawdzić, jak zachowują się nasze aplikacje.

Uruchamianie środowiska i testowanie

Dzięki Docker Compose kontenery z naszymi dwiema aplikacjami zostaną zbudowane i uruchomione jako oddzielne instancje, a jedyną warstwą pośredniczącą będzie rabbitmq i nasza implementacja magistrali komunikatów.

Z katalogu głównego projektu uruchommy następujące polecenia:

cd ../ # upewnij się, że jesteś w katalogu głównym
 docker-compose up --build -d

To polecenie może zająć trochę czasu, ponieważ buduje dwa oddzielne kontenery z PHP 8.1 + AMQP i pobiera obraz RabbitMQ. Bądź cierpliwy. Po zbudowaniu obrazów możesz uruchomić naszą komendę z poziomu app2 i wysłać kilka wiadomości do kolejki.

 docker exec -it app2 php bin/console app:send

Można to robić dowolną liczbę razy. Tak długo, jak nie ma konsument wiadomości nie będą przetwarzane. Jak tylko uruchomisz aplikację app1 i konsumować wszystkie wiadomości, które będą wyświetlane na ekranie.

docker exec -it app1 php bin/console messenger:consume -vv external_messages
obraz

Kompletny kod źródłowy wraz z plikiem README można znaleźć w naszym publicznym repozytorium The Codest Github

Podsumowanie

Symfony ze swoimi bibliotekami i narzędziami pozwala na szybkie i wydajne podejście do tworzenia nowoczesnych aplikacji. aplikacje internetowe. Za pomocą kilku komend i kilku linijek kodu jesteśmy w stanie stworzyć nowoczesny system komunikacji pomiędzy aplikacjami. Symfony, podobnie jak PHPjest idealny dla tworzenie aplikacji internetowych a dzięki swojemu ekosystemowi i łatwości wdrożenia osiąga jedne z najlepszych wskaźników czasu wprowadzenia na rynek.

Jednak szybko nie zawsze znaczy dobrze - w powyższym przykładzie przedstawiliśmy najprostszy i najszybszy sposób komunikacji. Co bardziej dociekliwi z pewnością zauważą, że brakuje odłączenia zdarzeń domenowych poza warstwę aplikacji - w obecnej wersji są one dublowane, nie ma też pełnego wsparcia dla Kopertamiędzy innymi nie ma Znaczki. Tych i innych zapraszam do lektury części II, w której poruszymy temat ujednolicania struktury domenowej aplikacji Symfony w środowisku mikrousług oraz omówimy drugą popularną metodę komunikacji mikrousług - tym razem synchroniczną, opartą o REST API.

baner współpracy

Powiązane artykuły

Software Development

PHP 8.2: Co nowego?

Nowa wersja PHP jest tuż za rogiem. Jakie są nowe implementacje, o których powinieneś wiedzieć? Sprawdź ten artykuł, aby się dowiedzieć!

The Codest
Sebastian Łuczak Lider jednostki PHP
Software Development

PHP Development. Komponent konsoli Symfony - porady i wskazówki

Ten artykuł został stworzony w celu pokazania najbardziej przydatnych i przydatnych wskazówek i sztuczek dotyczących rozwoju konsoli Symfony.

The Codest
Sebastian Łuczak Lider jednostki PHP

Subskrybuj naszą bazę wiedzy i bądź na bieżąco!

    O nas

    The Codest - Międzynarodowa firma programistyczna z centrami technologicznymi w Polsce.

    Wielka Brytania - siedziba główna

    • Office 303B, 182-184 High Street North E6 2JA
      Londyn, Anglia

    Polska - lokalne centra technologiczne

    • Fabryczna Office Park, Aleja
      Pokoju 18, 31-564 Kraków
    • Brain Embassy, Konstruktorska
      11, 02-673 Warszawa, Polska

      The Codest

    • Strona główna
    • O nas
    • Nasze Usługi
    • Case Studies
    • Nasze Know How
    • Kariera
    • Słownik

      Nasze Usługi

    • Konsultacje IT
    • Software Development
    • Backend Development
    • Frontend Development
    • Zespoły IT
    • Backend Dev
    • Inżynierowie rozwiązań chmurowych
    • Inżynierowie danych
    • Inne
    • Inżynierowie QA

      Raporty

    • Fakty i mity na temat współpracy z zewnętrznym partnerem programistycznym
    • Z USA do Europy: Dlaczego amerykańskie startupy decydują się na relokację do Europy?
    • Porównanie centrów rozwoju Tech Offshore: Tech Offshore Europa (Polska), ASEAN (Filipiny), Eurazja (Turcja)
    • Jakie są największe wyzwania CTO i CIO?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • Warunki korzystania z witryny

    Copyright © 2025 by The Codest. Wszelkie prawa zastrzeżone.

    pl_PLPolish
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench arArabic it_ITItalian jaJapanese ko_KRKorean es_ESSpanish nl_NLDutch etEstonian elGreek pl_PLPolish