PHP 8.2: Τι νέο υπάρχει;
Η νέα έκδοση του PHP είναι προ των πυλών. Ποιες είναι οι νέες υλοποιήσεις που πρέπει να γνωρίζετε; Διαβάστε αυτό το άρθρο για να μάθετε!

Διαβάστε το πρώτο μέρος της σειράς PHP που είναι αφιερωμένο στην επικοινωνία μικρουπηρεσιών στο πλαίσιο Symfony και στον πιο δημοφιλή τρόπο - την επικοινωνία AMQP με χρήση του RabbitMQ.
Η σύγχρονη αρχιτεκτονική εφαρμογών ανάγκασε τους προγραμματιστές να αλλάξουν τον τρόπο σκέψης για την επικοινωνία μεταξύ των διαφόρων στοιχείων των συστημάτων πληροφορικής. Κάποτε το θέμα ήταν απλούστερο - τα περισσότερα συστήματα δημιουργήθηκαν ως μονολιθικές δομές που συνδέονταν μεταξύ τους με ένα δίκτυο συνδέσεων επιχειρηματικής λογικής. Η διατήρηση τέτοιων εξαρτήσεων σε ένα PHP έργο ήταν μια τεράστια πρόκληση για PHP προγραμματιστές, και η αυξανόμενη δημοτικότητα των λύσεων SaaS και η τεράστια αύξηση της δημοτικότητας των cloud υπηρεσιών προκάλεσε το γεγονός ότι σήμερα ακούμε όλο και περισσότερο για μικρουπηρεσίες και αρθρωτότητα εφαρμογών.
Πώς ακριβώς, δημιουργώντας ανεξάρτητες μικρουπηρεσίες, μπορούμε να τις κάνουμε να ανταλλάσσουν πληροφορίες μεταξύ τους;
Αυτό το άρθρο είναι το πρώτο μιας σειράς αναρτήσεων σχετικά με επικοινωνία μικρο-υπηρεσιών στο Symfony πλαίσιο και καλύπτει τον πιο δημοφιλή τρόπο - την επικοινωνία AMQP με χρήση του RabbitMQ.
Να δημιουργήσετε δύο ανεξάρτητες εφαρμογές και να επιτύχετε την επικοινωνία μεταξύ τους χρησιμοποιώντας μόνο τον δίαυλο μηνυμάτων.
Έχουμε δύο, φανταστικές, ανεξάρτητες εφαρμογές:
* app1
: που στέλνει ειδοποιήσεις μέσω e-mail και SMS στους υπαλλήλους
* app2
: που σας επιτρέπει να διαχειρίζεστε την εργασία των εργαζομένων και να τους αναθέτετε καθήκοντα.
Θέλουμε να δημιουργήσουμε ένα σύγχρονο και απλό σύστημα με το οποίο η ανάθεση εργασίας σε έναν εργαζόμενο σε app2
θα στείλει μια ειδοποίηση στον πελάτη χρησιμοποιώντας app1
. Παρά τα φαινόμενα, αυτό είναι πολύ απλό!
Για τους σκοπούς αυτού του άρθρου, θα χρησιμοποιήσουμε την τελευταία έκδοση του Symfony (έκδοση 6.1 κατά τη στιγμή της συγγραφής) και την τελευταία έκδοση του PHP (8.1). Με λίγα πολύ απλά βήματα θα δημιουργήσουμε ένα λειτουργικό τοπικό περιβάλλον Docker με δύο μικρουπηρεσίες. Το μόνο που θα χρειαστείτε είναι:
* έναν λειτουργικό υπολογιστή,
* εγκατεστημένο Docker + Περιβάλλον Docker Compose
* και ένα τοπικά διαμορφωμένο Symfony CLI και λίγο ελεύθερο χρόνο.
Θα χρησιμοποιήσουμε τις δυνατότητες του Docker ως εργαλείο εικονικοποίησης εφαρμογών και κοντέινερ. Ας ξεκινήσουμε δημιουργώντας ένα δέντρο καταλόγου, ένα πλαίσιο για δύο Εφαρμογές Symfony, και να περιγράψουμε την υποδομή των περιβαλλόντων μας χρησιμοποιώντας το docker-compose.yml
αρχείο.
cd ~
mkdir microservices-in-symfony
cd microservices-in-symfony
symfony new app1
symfony new app2
touch docker-compose.yml
Δημιουργήσαμε δύο καταλόγους για δύο ξεχωριστές εφαρμογές Symfony και δημιουργήσαμε ένα κενό docker-compose.yml
αρχείο για να ξεκινήσει το περιβάλλον μας.
Ας προσθέσουμε τα ακόλουθα τμήματα στο docker-compose.yml
file:
έκδοση: '3.8'
υπηρεσίες:
app1:
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
εικόνα: rabbitmq:management
ports:
- 15672:15672
- 5672:5672
περιβάλλον:
- RABBITMQDEFAULTUSER=user
- RABBITMQDEFAULT_PASS=password
Πηγή κωδικός διαθέσιμο άμεσα: thecodest-co/microservices-in-symfony/blob/main/docker-compose.yml
Αλλά περιμένετε, τι συνέβη εδώ; Για όσους δεν είναι εξοικειωμένοι με το Docker, το παραπάνω αρχείο ρυθμίσεων μπορεί να φαίνεται αινιγματικό, ωστόσο ο σκοπός του είναι πολύ απλός. Χρησιμοποιώντας το Docker Compose δημιουργούμε τρεις "υπηρεσίες":
Για τη σωστή λειτουργία, χρειαζόμαστε ακόμη Dockerfile
τα οποία αποτελούν την πηγή για την κατασκευή των εικόνων. Ας τα δημιουργήσουμε λοιπόν:
touch app1/Dockerfile
touch app2/Dockerfile
Και τα δύο αρχεία έχουν ακριβώς την ίδια δομή και μοιάζουν ως εξής:
ΑΠΟ 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/*
Εκτελέστε το docker-php-ext-install zip,
CMD bash -c "cd /app && composer install && php -a"
Ο πηγαίος κώδικας διατίθεται άμεσα: /thecodest-co/microservices-in-symfony/blob/main/app1/Dockerfile
Το παραπάνω αρχείο χρησιμοποιείται από το Docker Compose για τη δημιουργία ενός δοχείου από μια εικόνα PHP 8.1 με εγκατεστημένο το Composer και την επέκταση AMQP. Επιπλέον, εκτελεί το PHP intepreter σε λειτουργία append για να συνεχίσει να τρέχει ο περιέκτης στο παρασκήνιο.
Το δέντρο καταλόγων και αρχείων σας θα πρέπει τώρα να έχει την ακόλουθη μορφή:
.
├── app1
│ └── Dockerfile
| (...) # Δομή εφαρμογής Symfony
├── app2
│ └ └── Dockerfile
| (...) # Δομή εφαρμογής Symfony
└── docker-compose.yml
Ας ξεκινήσουμε με το app1
και την πρώτη εφαρμογή.
Στο παράδειγμά μας, πρόκειται για μια εφαρμογή που ακούει και καταναλώνει μηνύματα από την ουρά που στέλνει η app2
όπως περιγράφεται στις απαιτήσεις:
ανάθεση μιας εργασίας σε έναν εργαζόμενο στο
app2
θα στείλει μια ειδοποίηση στον πελάτη
Ας ξεκινήσουμε προσθέτοντας τις απαιτούμενες βιβλιοθήκες. Το AMQP υποστηρίζεται εγγενώς για το symfony/messenger
επέκταση. Θα εγκαταστήσουμε επιπλέον μονόλογος/μονόλογος
να παρακολουθείτε τα αρχεία καταγραφής συστήματος για ευκολότερη ανάλυση της συμπεριφοράς των εφαρμογών.
cd app1/
symfony composer req amqp ampq-messenger monolog
Μετά την εγκατάσταση, ένα επιπλέον αρχείο προστέθηκε στο όνομα config/packages/messenger.yaml
. Είναι ένα αρχείο ρυθμίσεων για το συστατικό Symfony Messenger και δεν χρειαζόμαστε το πλήρης διαμόρφωση.
Αντικαταστήστε το με το παρακάτω αρχείο YAML:
πλαίσιο:
Αγγελιοφόρος:
για να στέλνετε τα αποτυχημένα μηνύματα σε αυτή τη μεταφορά για μεταγενέστερο χειρισμό.
# 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
type: direct
default_publish_routing_key: from_external
ουρές:
messages:
binding_keys: [from_external]
Ο πηγαίος κώδικας διατίθεται άμεσα: thecodest-co/microservices-in-symfony/blob/main/app1/config/packages/messenger.yaml
Το Symfony Messenger χρησιμοποιείται για σύγχρονη και ασύγχρονη επικοινωνία σε εφαρμογές Symfony. Υποστηρίζει μια ποικιλία από μεταφορές, ή πηγές αλήθειας του επιπέδου μεταφοράς. Στο παράδειγμά μας, χρησιμοποιούμε την επέκταση AMQP που υποστηρίζει το σύστημα ουρών γεγονότων RabbitMQ.
Η παραπάνω διαμόρφωση ορίζει μια νέα μεταφορά με όνομα external_messages
, το οποίο παραπέμπει στο MESSENGER_TRANSPORT_DSN
μεταβλητή περιβάλλοντος και ορίζει απευθείας ακρόαση στο μηνύματα
κανάλι στον δίαυλο μηνυμάτων. Σε αυτό το σημείο, επεξεργαστείτε επίσης το app1/.env
και προσθέστε την κατάλληλη διεύθυνση μεταφοράς.
"`env
(...)
MESSENGERTRANSPORTDSN=amqp://user:password@rabbitmq:5672//messages
(...)
"
Μετά την προετοιμασία του πλαισίου της εφαρμογής και τη διαμόρφωση των βιβλιοθηκών, ήρθε η ώρα να υλοποιηθεί η επιχειρησιακή λογική. Γνωρίζουμε ότι η εφαρμογή μας πρέπει να ανταποκρίνεται στην ανάθεση μιας εργασίας σε έναν εργαζόμενο. Γνωρίζουμε επίσης ότι η ανάθεση μιας εργασίας στοapp2system αλλάζει την κατάσταση της εργασίας. Ας δημιουργήσουμε λοιπόν ένα μοντέλο που μιμείται την αλλαγή κατάστασης και ας το αποθηκεύσουμε στη διαδρομήapp1/Message/StatusUpdate.php`:
{
public function __construct(protected string $status){}
public function getStatus(): string
{
return $this->status,
}
}
Ο πηγαίος κώδικας διατίθεται άμεσα: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Message/StatusUpdate.php
Χρειαζόμαστε ακόμα μια κλάση που θα υλοποιεί την επιχειρησιακή λογική όταν η μικρουπηρεσία μας λαμβάνει το παραπάνω συμβάν από την ουρά. Ας δημιουργήσουμε λοιπόν μια Χειριστής μηνυμάτων στο app1/Handler/StatusUpdateHandler.php
μονοπάτι:
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),
// η υπόλοιπη επιχειρησιακή λογική, π.χ. αποστολή ηλεκτρονικού ταχυδρομείου στο χρήστη
// $this->emailService->email()
}
}
Ο πηγαίος κώδικας διατίθεται άμεσα: /thecodest-co/microservices-in-symfony/blob/main/app1/src/Handler/StatusUpdateHandler.php
PHP κάνουν τα πράγματα πολύ πιο εύκολα και σημαίνουν ότι στη συγκεκριμένη περίπτωση δεν χρειάζεται να ανησυχούμε για την αυτόματη καλωδίωση ή τη δήλωση υπηρεσίας. Η μικροϋπηρεσία μας για το χειρισμό των γεγονότων του τομέα είναι έτοιμη, ήρθε η ώρα να ασχοληθούμε με τη δεύτερη εφαρμογή.
Θα ρίξουμε μια ματιά στο app2
κατάλογο και το δεύτερο Εφαρμογή Symfony. Η ιδέα μας είναι να στέλνουμε ένα μήνυμα στην ουρά όταν σε έναν εργαζόμενο ανατίθεται μια εργασία στο σύστημα. Ας κάνουμε λοιπόν μια γρήγορη διαμόρφωση του AMQP και ας ξεκινήσουμε τη δημοσίευση της δεύτερης μικρο-υπηρεσίας μας StatusUpdate
συμβάντα στον δίαυλο μηνυμάτων.
Η εγκατάσταση των βιβλιοθηκών είναι ακριβώς η ίδια όπως και για την πρώτη εφαρμογή.
cd ..
cd app2/
symfony composer req amqp ampq-messenger monolog
Ας βεβαιωθούμε ότι η app2/.env
αρχείο περιέχει μια έγκυρη καταχώρηση DSN για το RabbitMQ:
(...)
MESSENGER_TRANSPORT_DSN=amqp://user:password@rabbitmq:5672//messages
(...)
Το μόνο που απομένει είναι να ρυθμίσετε το Symfony Messenger στο app2/config/packages/messenger.yaml
file:
πλαίσιο:
Αγγελιοφόρος:
για να στέλνετε τα αποτυχημένα μηνύματα σε αυτή τη μεταφορά για μεταγενέστερο χειρισμό.
# failure_transport: failed
transports:
# https://symfony.com/doc/current/messenger.html#transport-configuration
async:
dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
routing:
# Δρομολόγηση των μηνυμάτων σας στις μεταφορές
'AppMessageStatusUpdate': async
Όπως μπορείτε να δείτε, αυτή τη φορά ο ορισμός της μεταφοράς δείχνει απευθείας στο async
και ορίζει τη δρομολόγηση με τη μορφή της αποστολής των StatusUpdate
μήνυμα στο διαμορφωμένο DSN. Αυτή είναι η μόνη περιοχή διαμόρφωσης, το μόνο που απομένει είναι η δημιουργία της λογικής και του επιπέδου υλοποίησης της ουράς AMQP. Για το σκοπό αυτό θα δημιουργήσουμε το δίδυμο StatusUpdateHandler
και StatusUpdate
τάξεις στο app2
.
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),
## επιχειρησιακή λογική, π.χ. αποστολή εσωτερικής ειδοποίησης ή δημιουργία ουράς σε άλλα συστήματα.
}
}
{
public function __construct(protected string $status){}
public function getStatus(): string
{
return $this->status,
}
}
Ο πηγαίος κώδικας διατίθεται άμεσα: /thecodest-co/microservices-in-symfony/blob/main/app2/src/Message/StatusUpdate.php
Τέλος, το μόνο που πρέπει να γίνει είναι να δημιουργηθεί ένας τρόπος αποστολής ενός μηνύματος στον δίαυλο μηνυμάτων. Θα δημιουργήσουμε ένα απλό Εντολή Symfony για αυτό:
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 = "Ο εργαζόμενος X έχει ανατεθεί στον Y",
$this->messageBus->dispatch(
message: new StatusUpdate($status)
);
return Command::SUCCESS,
}
}
Χάρη στην Εγχώνευση εξάρτησης μπορούμε να χρησιμοποιήσουμε μια περίπτωση της MessageBusInterface
στην εντολή μας και να στείλουμε ένα StatusUpdate
μήνυμα μέσω του αποστολή()
στην ουρά μας. Επιπλέον, χρησιμοποιούμε επίσης τα χαρακτηριστικά PHP εδώ.
Αυτό είναι όλο - το μόνο που απομένει είναι να τρέξουμε το περιβάλλον Docker Compose και να δούμε πώς συμπεριφέρονται οι εφαρμογές μας.
Με το Docker Compose τα κοντέινερ με τις δύο εφαρμογές μας θα κατασκευαστούν και θα τρέξουν ως ξεχωριστές περιπτώσεις, το μόνο ενδιάμεσο επίπεδο θα είναι το rabbitmq
και την υλοποίηση του διαύλου μηνυμάτων μας.
Από το ριζικό κατάλογο του έργου, ας εκτελέσουμε τις ακόλουθες εντολές:
cd ../ # βεβαιωθείτε ότι βρίσκεστε στον κύριο κατάλογο
docker-compose up --build -d
Αυτή η εντολή μπορεί να πάρει κάποιο χρόνο, καθώς δημιουργεί δύο ξεχωριστά δοχεία με το PHP 8.1 + AMQP και τραβάει την εικόνα RabbitMQ. Κάντε υπομονή. Αφού χτιστούν οι εικόνες, μπορείτε να πυροδοτήσετε την εντολή μας από το app2
και να στείλετε κάποια μηνύματα σε μια ουρά.
docker exec -it app2 php bin/console app:send
Μπορείτε να το κάνετε όσες φορές μπορείτε. Αρκεί να μην υπάρχει καταναλωτής τα μηνύματά σας δεν θα υποβληθούν σε επεξεργασία. Μόλις ενεργοποιήσετε το app1
και να καταναλώνετε όλα τα μηνύματα που θα εμφανίζονται στην οθόνη σας.
docker exec -it app1 php bin/console messenger:consume -vvv external_messages
Η πλήρης πηγαίος κώδικας μαζί με το README μπορούν να βρεθούν στο δημόσιο αποθετήριο μας The Codest Github
Το Symfony με τις βιβλιοθήκες και τα εργαλεία του επιτρέπει μια γρήγορη και αποτελεσματική προσέγγιση για την ανάπτυξη σύγχρονων διαδικτυακές εφαρμογές. Με μερικές εντολές και λίγες γραμμές κώδικα είμαστε σε θέση να δημιουργήσουμε ένα σύγχρονο σύστημα επικοινωνίας μεταξύ εφαρμογών. Το Symfony, όπως PHP, είναι ιδανικό για ανάπτυξη διαδικτυακών εφαρμογών και χάρη στο οικοσύστημα και την ευκολία εφαρμογής του, το οικοσύστημα αυτό επιτυγχάνει μερικούς από τους καλύτερους δείκτες χρόνου διάθεσης στην αγορά.
Ωστόσο, το γρήγορο δεν σημαίνει πάντα καλό - στο παραπάνω παράδειγμα παρουσιάσαμε τον πιο απλό και γρήγορο τρόπο επικοινωνίας. Αυτό που οι πιο περίεργοι θα παρατηρήσουν σίγουρα είναι ότι υπάρχει έλλειψη αποσύνδεσης των γεγονότων του τομέα εκτός του επιπέδου εφαρμογής - στην τρέχουσα έκδοση διπλασιάζονται, και δεν υπάρχει πλήρης υποστήριξη για Φάκελος
, μεταξύ άλλων δεν υπάρχει Γραμματόσημα
. Για αυτούς και για άλλους, σας προσκαλώ να διαβάσετε το Μέρος ΙΙ, όπου θα καλύψουμε το θέμα της ενοποίησης της δομής του τομέα των εφαρμογών Symfony σε ένα περιβάλλον μικρουπηρεσιών και θα συζητήσουμε τη δεύτερη δημοφιλή μέθοδο επικοινωνίας μικρουπηρεσιών - αυτή τη φορά σύγχρονη, βασισμένη στο REST API.