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/%2f/messages
(…)
“
After preparing the application framework and configuring the libraries, it is time to implement the business logic. We know that our application must respond to the assignment of a job to a worker. We also know that assigning a job in theapp2system changes the status of the job. So let's create a model that mimics the status change and save it in theapp1/Message/StatusUpdate.php` path:
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/%2f/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 is_ISIcelandic pl_PLPolish