ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

ΠŸΡ€Π΅Π΄Π³ΠΎΠ²ΠΎΡ€

ΠœΠΎΡΡ‚ сайт, ΠΊΠΎΠΉΡ‚ΠΎ ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ°ΠΌ ΠΊΠ°Ρ‚ΠΎ Ρ…ΠΎΠ±ΠΈ, Π΅ ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ Π·Π° ΡΡŠΡ…Ρ€Π°Π½Π΅Π½ΠΈΠ΅ Π½Π° интСрСсни Π»ΠΈΡ‡Π½ΠΈ страници ΠΈ пСрсонални сайтовС. Π’Π°Π·ΠΈ Ρ‚Π΅ΠΌΠ° ΠΌΠ΅ заинтСрСсува Π² самото Π½Π°Ρ‡Π°Π»ΠΎ Π½Π° моята ΠΊΠ°Ρ€ΠΈΠ΅Ρ€Π° Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ, ΠΊΠΎΠ³Π°Ρ‚ΠΎ бях Π²ΠΏΠ΅Ρ‡Π°Ρ‚Π»Π΅Π½ ΠΎΡ‚ Π²Ρ€ΡŠΠ·ΠΊΠΈΡ‚Π΅ с Π³ΠΎΠ»Π΅ΠΌΠΈ профСсионалисти, ΠΊΠΎΠΈΡ‚ΠΎ ΠΏΠΈΡˆΠ°Ρ‚ Π·Π° сСбС си, своитС увлСчСния ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈ. ΠΠ°Π²ΠΈΠΊΡŠΡ‚ Π΄Π° Π³ΠΈ ΠΎΡ‚ΠΊΡ€ΠΈΠ²Π°ΠΌ остава ΠΈ Π΄ΠΎ днСс: ΠΏΠΎΡ‡Ρ‚ΠΈ Π½Π° всСки Ρ‚ΡŠΡ€Π³ΠΎΠ²ΡΠΊΠΈ ΠΈ Π½Π΅ ΠΌΠ½ΠΎΠ³ΠΎ сайт ΠΏΡ€ΠΎΠ΄ΡŠΠ»ΠΆΠ°Π²Π°ΠΌ Π΄Π° Π½Π°Π΄Π½ΠΈΡ‡Π°ΠΌ Π² Ρ„ΡƒΡ‚Π΅Ρ€Π°, Π² Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅ Π½Π° Π»ΠΈΠ½ΠΊΠΎΠ²Π΅ към Π°Π²Ρ‚ΠΎΡ€ΠΈΡ‚Π΅.

РСализация Π½Π° идСята

ΠŸΡŠΡ€Π²Π°Ρ‚Π° вСрсия бСшС просто html-страница Π½Π° моя Π»ΠΈΡ‡Π΅Π½ сайт, ΠΊΡŠΠ΄Π΅Ρ‚ΠΎ ΡΡŠΠ±ΠΈΡ€Π°Ρ… Π»ΠΈΠ½ΠΊΠΎΠ²Π΅ с описания Π² списък ul. Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ Π½Π°Π±Ρ€Π°Ρ… ΠΎΠΊΠΎΠ»ΠΎ 20 страници Π·Π° извСстно Π²Ρ€Π΅ΠΌΠ΅, Π·Π°ΠΏΠΎΡ‡Π½Π°Ρ… Π΄Π° мисля, Ρ‡Π΅ Ρ‚ΠΎΠ²Π° Π½Π΅ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Π΅Ρ„Π΅ΠΊΡ‚ΠΈΠ²Π½ΠΎ ΠΈ Ρ€Π΅ΡˆΠΈΡ… Π΄Π° ΠΎΠΏΠΈΡ‚Π°ΠΌ Π΄Π° Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΠ·ΠΈΡ€Π° процСса. На stackoverflow забСлязвах, Ρ‡Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Ρ…ΠΎΡ€Π° посочват сайтовС Π² ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ‚Π΅ си, Π·Π°Ρ‚ΠΎΠ²Π° написах парсСр Π½Π° php, ΠΊΠΎΠΉΡ‚ΠΎ просто минавашС ΠΏΡ€Π΅Π· ΠΏΡ€ΠΎΡ„ΠΈΠ»ΠΈΡ‚Π΅, Π·Π°ΠΏΠΎΡ‡Π²Π°ΠΉΠΊΠΈ ΠΎΡ‚ ΠΏΡŠΡ€Π²ΠΈΡ (адрСситС Π½Π° SO ΠΈ Π΄ΠΎ днСшСн Π΄Π΅Π½ са ΠΎΡ‚ Ρ‚ΠΎΠ·ΠΈ Π²ΠΈΠ΄: `\/users\/1`), ΠΈΠ·Π²Π»ΠΈΡ‡Π°ΡˆΠ΅ Π»ΠΈΠ½ΠΊΠΎΠ²Π΅Ρ‚Π΅ ΠΎΡ‚ нСобходимия Ρ‚Π°Π³ ΠΈ Π³ΠΈ ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°ΡˆΠ΅ Π² SQLite.

ΠœΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π½Π°Ρ€Π΅Ρ‡Π΅Ρ‚Π΅ Ρ‚ΠΎΠ²Π° Π²Ρ‚ΠΎΡ€Π° вСрсия: колСкция ΠΎΡ‚ дСсСтки хиляди url адрСси Π² Ρ‚Π°Π±Π»ΠΈΡ†Π° SQLite, която Π·Π°ΠΌΠ΅Π½ΠΈ статичния списък Π² html. На Π±Π°Π·Π°Ρ‚Π° Π½Π° Ρ‚ΠΎΠ·ΠΈ списък Π½Π°ΠΏΡ€Π°Π²ΠΈΡ… прост Ρ‚ΡŠΡ€ΡΠ°Ρ‡. Въй ΠΊΠ°Ρ‚ΠΎ имашС само url адрСси, Ρ‚ΡŠΡ€ΡΠ΅Π½Π΅Ρ‚ΠΎ бСшС просто ΠΏΠΎ тях.

На Ρ‚ΠΎΠ·ΠΈ Π΅Ρ‚Π°ΠΏ зарязах ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° ΠΈ сС Π²ΡŠΡ€Π½Π°Ρ… към Π½Π΅Π³ΠΎ слСд дълго Π²Ρ€Π΅ΠΌΠ΅. На Ρ‚ΠΎΠ·ΠΈ Π΅Ρ‚Π°ΠΏ ΠΎΠΏΠΈΡ‚ΡŠΡ‚ ΠΌΠΈ Π² Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚Π° Π²Π΅Ρ‡Π΅ надминавашС Ρ‚Ρ€ΠΈ Π³ΠΎΠ΄ΠΈΠ½ΠΈ ΠΈ чувствах, Ρ‡Π΅ ΠΌΠΎΠ³Π° Π΄Π° направя Π½Π΅Ρ‰ΠΎ ΠΏΠΎ-ΡΡŠΡ‰Π΅ΡΡ‚Π²Π΅Π½ΠΎ. ОсвСн Ρ‚ΠΎΠ²Π° имашС голямо ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ Π΄Π° усвои относитСлно Π½ΠΎΠ²ΠΈ Π·Π° ΠΌΠ΅Π½ Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ.

Π‘ΡŠΠ²Ρ€Π΅ΠΌΠ΅Π½Π½Π° вСрсия

ΠŸΡ€ΠΎΠ΅ΠΊΡ‚ Ρ€Π°Π·Π²ΠΈΡ‚Π° Π² Docker, Π±Π°Π·Π°Ρ‚Π° Π΅ прСмСстСна Π½Π° MongoDB, ΠΈ ΠΎΡ‚ относитСлно скоро Π΅ Π΄ΠΎΠ±Π°Π²Π΅Π½ Redis, ΠΊΠΎΠΉΡ‚ΠΎ ΠΏΡŠΡ€Π²ΠΎΠ½Π°Ρ‡Π°Π»Π½ΠΎ бСшС само Π·Π° ΠΊΠ΅ΡˆΠΈΡ€Π°Π½Π΅. Π—Π° основа сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π΅Π΄ΠΈΠ½ ΠΎΡ‚ ΠΌΠΈΠΊΡ€ΠΎΡ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅ Π½Π° PHP.

ΠŸΡ€ΠΎΠ±Π»Π΅ΠΌΠ°

Нови сайтовС сС добавят с ΠΊΠΎΠ½Π·ΠΎΠ»Π½Π° ΠΊΠΎΠΌΠ°Π½Π΄Π°, която синхронно ΠΏΡ€Π°Π²ΠΈ слСдното:

  • Π‘Π²Π°Π»ΠΈ ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Π½ΠΈΠ΅Ρ‚ΠΎ ΠΏΠΎ URL
  • ΠŸΠΎΡΡ‚Π°Π²Ρ Ρ„Π»Π°Π³ Π·Π° Ρ‚ΠΎΠ²Π° Π΄Π°Π»ΠΈ HTTPS Π΅ Π±ΠΈΠ» Π΄ΠΎΡΡ‚ΡŠΠΏΠ΅Π½
  • Π—Π°ΠΏΠ°Π·Π²Π° ΡΡŠΡ‰Π΅ΡΡ‚Π²ΡƒΠ²Π°Π½Π΅Ρ‚ΠΎ Π½Π° уСбсайта
  • Π˜Π·Ρ…ΠΎΠ΄Π½ΠΈΡΡ‚ HTML ΠΈ заглавията сС Π·Π°ΠΏΠ°Π·Π²Π°Ρ‚ Π² историята Π½Π° "индСксиранСто"
  • ΠŸΠ°Ρ€ΡΠΈΡ€Π° ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Π½ΠΈΠ΅Ρ‚ΠΎ, ΠΈΠ·Π²Π»ΠΈΡ‡Π° Title ΠΈ Description
  • Π”Π°Π½Π½ΠΈΡ‚Π΅ сС Π·Π°ΠΏΠ°Π·Π²Π°Ρ‚ Π² ΠΎΡ‚Π΄Π΅Π»Π½Π° колСкция

Π’ΠΎΠ²Π° бСшС Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ, Π·Π° Π΄Π° ΡΡŠΡ…Ρ€Π°Π½ΡΠ²Π°ΠΌ сайтовС ΠΈ Π΄Π° Π³ΠΈ ΠΏΠΎΠΊΠ°Π·Π²Π°ΠΌ Π² списък:

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

Но идСята Π·Π° Π°Π²Ρ‚ΠΎΠΌΠ°Ρ‚ΠΈΡ‡Π½ΠΎ индСксиранС, ΠΊΠ°Ρ‚Π΅Π³ΠΎΡ€ΠΈΠ·ΠΈΡ€Π°Π½Π΅ ΠΈ класиранС Π½Π° всичко, Π·Π° Π΄Π° сС Π·Π°ΠΏΠ°Π·ΠΈ актуалността, Π½Π΅ сС вписвашС особСно Π² Ρ‚Π°Π·ΠΈ ΠΏΠ°Ρ€Π°Π΄ΠΈΠ³ΠΌΠ°. Π”Π°ΠΆΠ΅ простото добавянС Π½Π° ΡƒΠ΅Π±-ΠΌΠ΅Ρ‚ΠΎΠ΄ Π·Π° добавянС Π½Π° страници изисквашС Π΄ΡƒΠ±Π»ΠΈΡ€Π°Π½Π΅ Π½Π° ΠΊΠΎΠ΄ ΠΈ Π±Π»ΠΎΠΊΠΈΡ€ΠΎΠ²ΠΊΠΈ, Π·Π° Π΄Π° сС ΠΈΠ·Π±Π΅Π³Π½Π°Ρ‚ ΠΏΠΎΡ‚Π΅Π½Ρ†ΠΈΠ°Π»Π½ΠΈ DDoS Π°Ρ‚Π°ΠΊΠΈ.

Π’ΡΡŠΡ‰Π½ΠΎΡΡ‚, Ρ€Π°Π·Π±ΠΈΡ€Π° сС, всичко ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΠΏΡ€Π°Π²ΠΈ ΠΈ синхронно, Π° Π² ΡƒΠ΅Π±-ΠΌΠ΅Ρ‚ΠΎΠ΄Π° просто Π΄Π° сС запишС URL, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ монструозният Π΄Π΅ΠΌΠΎΠ½ Π΄Π° изпълнява всички Π·Π°Π΄Π°Ρ‡ΠΈ Π·Π° URL ΠΎΡ‚ списъка. Но Π΄ΠΎΡ€ΠΈ ΠΈ Ρ‚ΡƒΠΊ Π½Π°Ρ‚Ρ€Π°ΠΏΡ‡ΠΈΠ²ΠΎ ΠΈΠ·Π½ΠΈΠΊΠ²Π° Π΄ΡƒΠΌΠ°Ρ‚Π° β€žΠΎΠΏΠ°ΡˆΠΊΠ°β€œ. А Π°ΠΊΠΎ сС Π²Π½Π΅Π΄Ρ€ΠΈ опашка, ΠΌΠΎΠΆΠ΅ Π΄Π° сС раздСлят всички Π·Π°Π΄Π°Ρ‡ΠΈ ΠΈ Π΄Π° сС ΠΈΠ·ΠΏΡŠΠ»Π½ΡΠ²Π°Ρ‚ ΠΏΠΎΠ½Π΅ асинхронно.

РСшСниС

Π”Π° Π²Π½Π΅Π΄Ρ€ΠΈΠΌ опашки ΠΈ Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌ ΡΡŠΠ±ΠΈΡ‚ΠΈΠΉΠ½ΠΎ-ΠΎΡ€ΠΈΠ΅Π½Ρ‚ΠΈΡ€Π°Π½Π° систСма Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° всички Π·Π°Π΄Π°Ρ‡ΠΈ. И ΠΎΡ‚Π΄Π°Π²Π½Π° исках Π΄Π° ΠΎΠΏΠΈΡ‚Π°ΠΌ Redis Streams.

ИзползванС на Redis streams в PHP

Въй ΠΊΠ°Ρ‚ΠΎ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΡŠΡ‚ ΠΌΠΈ Π½Π΅ Π΅ ΠΎΡ‚ Ρ‚Ρ€ΠΈΡ‚Π΅ Π³ΠΈΠ³Π°Π½Ρ‚Π° Symfony, Laravel, Yii, исках Π΄Π° намСря нСзависима Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°. Но, ΠΊΠ°ΠΊΡ‚ΠΎ сС ΠΎΠΊΠ°Π·Π° (ΠΏΡ€ΠΈ ΠΏΡŠΡ€Π²ΠΎΠ½Π°Ρ‡Π°Π»Π½ΠΎΡ‚ΠΎ Ρ€Π°Π·Π³Π»Π΅ΠΆΠ΄Π°Π½Π΅) β€” Π½Π΅ Π΅ възмоТно Π΄Π° сС намСрят ΠΎΡ‚Π΄Π΅Π»Π½ΠΈ сСриозни Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ. Всичко, ΡΠ²ΡŠΡ€Π·Π°Π½ΠΎ с опашки, ΠΈΠ»ΠΈ Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ с 3 ΠΊΠΎΠΌΠΈΡ‚Π° ΠΎΡ‚ΠΏΡ€Π΅Π΄ΠΈ ΠΏΠ΅Ρ‚ Π³ΠΎΠ΄ΠΈΠ½ΠΈ, ΠΈΠ»ΠΈ Π΅ ΡΠ²ΡŠΡ€Π·Π°Π½ΠΎ с Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠ°.

Π§ΡƒΡ… ΠΌΠ½ΠΎΠ³ΠΎ Π·Π° Symfony, ΠΊΠ°Ρ‚ΠΎ Π·Π° доставчик Π½Π° ΠΎΡ‚Π΄Π΅Π»Π½ΠΈ ΠΏΠΎΠ»Π΅Π·Π½ΠΈ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΈ, Π° ΠΈ някои ΠΎΡ‚ тях Π²Π΅Ρ‡Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌ. А ΡΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ° част ΠΎΡ‚ Laravel ΡΡŠΡ‰ΠΎ ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°, Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€ тСхният ORM, Π±Π΅Π· Π΄Π° ΠΏΡ€ΠΈΡΡŠΡΡ‚Π²Π° самият Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ.

symfony/messenger

ΠŸΡŠΡ€Π²ΠΈΡΡ‚ ΠΊΠ°Π½Π΄ΠΈΠ΄Π°Ρ‚ Π²Π΅Π΄Π½Π°Π³Π° изглСТдашС ΠΈΠ΄Π΅Π°Π»Π΅Π½ ΠΈ Π±Π΅Π· Π½ΠΈΠΊΠ°ΠΊΠ²ΠΎ съмнСниС Π³ΠΎ инсталирах. Но Π΄Π° намСря ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈ Π·Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅ извън Symfony сС ΠΎΠΊΠ°Π·Π° ΠΏΠΎ-слоТно. Как Π΄Π° сглобя ΠΎΡ‚ ΠΊΡƒΠΏ класовС с унивСрсални, Π½ΠΈΡ‰ΠΎ Π½Π΅Π·Π½Π°Ρ‡Π΅Ρ‰ΠΈ ΠΈΠΌΠ΅Π½Π° шина Π·Π° ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ, ΠΈ Ρ‚ΠΎ Π½Π° Redis?

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

ДокумСнтацията Π½Π° официалния сайт бСшС Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ ΠΏΠΎΠ΄Ρ€ΠΎΠ±Π½Π°, Π½ΠΎ инициализацията бСшС описана само Π·Π° Symfony, с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° тСхния любим YML ΠΈ Π΄Ρ€ΡƒΠ³ΠΈ магичСски ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΈ Π·Π° нСсимфонисти. НямашС интСрСс Ρƒ ΠΌΠ΅Π½ към самия процСс Π½Π° инсталиранС, особСно ΠΏΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π½Π° Π½ΠΎΠ²ΠΎΠ³ΠΎΠ΄ΠΈΡˆΠ½ΠΈΡ‚Π΅ ΠΏΡ€Π°Π·Π½ΠΈΡ†ΠΈ. Но Ρ‚Ρ€ΡΠ±Π²Π°ΡˆΠ΅ Π΄Π° сС Π·Π°Π½ΠΈΠΌΠ°Π²Π°ΠΌ с Ρ‚ΠΎΠ²Π° ΠΈ Π½Π΅ΠΎΡ‡Π°ΠΊΠ²Π°Π½ΠΎ дълго.

ΠžΠΏΠΈΡ‚ΡŠΡ‚ Π΄Π° сС Ρ€Π°Π·Π±Π΅Ρ€Π° с инстанциранСто Π½Π° систСмата ΠΏΠΎ изходния ΠΊΠΎΠ΄ Π½Π° Symfony ΡΡŠΡ‰ΠΎ Π½Π΅ Π΅ Π½Π°ΠΉ-Ρ‚Ρ€ΠΈΠ²ΠΈΠ°Π»Π½Π°Ρ‚Π° Π·Π°Π΄Π°Ρ‡Π° Π·Π° ΠΊΡ€Π°Ρ‚ΠΊΠΈ сроковС:

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ сС ΠΏΠΎΠΌΠ°ΠΉΠ²Π°Ρ… Π² Ρ‚ΠΎΠ²Π° всичко ΠΈ ΠΎΠΏΠΈΡ‚Π°Ρ… Π½Π΅Ρ‰ΠΎ Π΄Π° направя със собствСни Ρ€ΡŠΡ†Π΅, стигнах Π΄ΠΎ ΠΈΠ·Π²ΠΎΠ΄Π°, Ρ‡Π΅ сС Π·Π°Π½ΠΈΠΌΠ°Π²Π°ΠΌ с някакви костилки ΠΈ Ρ€Π΅ΡˆΠΈΡ… Π΄Π° ΠΎΠΏΠΈΡ‚Π°ΠΌ Π½Π΅Ρ‰ΠΎ Π΄Ρ€ΡƒΠ³ΠΎ.

illuminate/queue

Оказа сС, Ρ‡Π΅ Ρ‚Π°Π·ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° Π΅ ΠΏΠ»ΡŠΡ‚Π½ΠΎ ΡΠ²ΡŠΡ€Π·Π°Π½Π° с инфраструктурата Π½Π° Laravel ΠΈ ΠΌΠ½ΠΎΠ³ΠΎ Π΄Ρ€ΡƒΠ³ΠΈ зависимости, Π·Π°Ρ‚ΠΎΠ²Π° Π½Π΅ ΠΎΡ‚Π΄Π΅Π»ΠΈΡ… ΠΌΠ½ΠΎΠ³ΠΎ Π²Ρ€Π΅ΠΌΠ΅ Π·Π° нСя: инсталирах я, ΠΏΠΎΠ³Π»Π΅Π΄Π½Π°Ρ…, видях зависимоститС ΠΈ я ΠΈΠ·Ρ‚Ρ€ΠΈΡ….

yiisoft/yii2-queue

Π’ΡƒΠΊ Π²Π΅Π΄Π½Π°Π³Π° сС ΠΏΠΎΠ΄Ρ€Π°Π·Π±ΠΈΡ€Π°ΡˆΠ΅ ΠΎΡ‚ ΠΈΠΌΠ΅Ρ‚ΠΎ, ΠΎΡ‚Π½ΠΎΠ²ΠΎ строга ΠΏΡ€ΠΈΠ²ΡŠΡ€Π·Π°Π½ΠΎΡΡ‚ към Yii2. Π’Π°Π·ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° ΠΌΠΈ сС Π΅ Π½Π°Π»Π°Π³Π°Π»ΠΎ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌ ΠΈ бСшС Π΄ΠΎΠ±Ρ€Π°, Π½ΠΎ Π½Π΅ мислСх, Ρ‡Π΅ Π΅ напълно зависима ΠΎΡ‚ Yii2.

ΠžΡΡ‚Π°Π½Π°Π»ΠΈΡ‚Π΅

Всичко Π΄Ρ€ΡƒΠ³ΠΎ, ΠΊΠΎΠ΅Ρ‚ΠΎ Π½Π°ΠΌΠ΅Ρ€ΠΈΡ… Π² GitHub β€” Π½Π΅Π½Π°Π΄Π΅ΠΆΠ΄Π½ΠΈ остарСли ΠΈ изоставСни ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈ Π±Π΅Π· Π·Π²Π΅Π·Π΄ΠΈ, Ρ„ΠΎΡ€ΠΊΠΎΠ²Π΅ ΠΈ голям Π±Ρ€ΠΎΠΉ ΠΊΠΎΠΌΠΈΡ‚ΠΈ.

Π’Ρ€ΡŠΡ‰Π°Π½Π΅ ΠΎΠ±Ρ€Π°Ρ‚Π½ΠΎ към symfony/messenger, тСхничСски подробности

Π’Ρ€ΡΠ±Π²Π°ΡˆΠ΅ Π΄Π° сС запозная с Ρ‚Π°Π·ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ° ΠΈ, отдСляйки ΠΎΡ‰Π΅ ΠΌΠ°Π»ΠΊΠΎ Π²Ρ€Π΅ΠΌΠ΅, успях. Оказа сС, Ρ‡Π΅ всичко Π΅ Π΄ΠΎΡΡ‚Π°Ρ‚ΡŠΡ‡Π½ΠΎ Π»Π°ΠΊΠΎΠ½ΠΈΡ‡Π½ΠΎ ΠΈ просто. Π—Π° инстанциранСто Π½Π° шина, Π½Π°ΠΏΡ€Π°Π²ΠΈΡ… ΠΌΠ°Π»ΠΊΠ° Ρ„Π°Π±Ρ€ΠΈΠΊΠ°, Ρ‚ΡŠΠΉ ΠΊΠ°Ρ‚ΠΎ ΠΏΡ€Π΅Π΄Π²ΠΈΠ΄ΠΈΡ…, Ρ‡Π΅ Ρ‰Π΅ ΠΈΠΌΠ°ΠΌ няколко шини с Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΡ†ΠΈ.

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

Π‘Π°ΠΌΠΎ няколко ΡΡ‚ΡŠΠΏΠΊΠΈ:

  • БъздавамС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΡ†ΠΈ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ, ΠΊΠΎΠΈΡ‚ΠΎ трябва Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ просто callable
  • Π—Π°Π²ΠΈΠ²Π°ΠΌΠ΅ Π³ΠΈ Π² HandlerDescriptor (клас ΠΎΡ‚ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°Ρ‚Π°)
  • Π’Π΅Π·ΠΈ β€žΠ”Π΅ΡΠΊΡ€ΠΈΠΏΡ‚ΠΎΡ€ΠΈβ€œ Π·Π°Π²ΠΈΠ²Π°ΠΌΠ΅ Π² инстанс HandlersLocator
  • ДобавямС HandlersLocator Π² инстанс MessageBus
  • ΠŸΡ€Π΅Π΄Π°Π²Π°ΠΌΠ΅ Π² SendersLocator Π½Π°Π±ΠΎΡ€ ΠΎΡ‚ `SenderInterface`, Π² моя случай инстанси Π½Π° класовСтС `RedisTransport`, ΠΊΠΎΠΈΡ‚ΠΎ сС ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Ρ‚ ΠΎΡ‡Π΅Π²ΠΈΠ΄Π½ΠΎ
  • ДобавямС SendersLocator Π² инстанс MessageBus

MessageBus ΠΈΠΌΠ° ΠΌΠ΅Ρ‚ΠΎΠ΄ `->dispatch()`, ΠΊΠΎΠΉΡ‚ΠΎ Ρ‚ΡŠΡ€ΡΠΈ ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ‚Π΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΡ†ΠΈ Π² HandlersLocator ΠΈ ΠΏΡ€Π΅Π΄Π°Π²Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΈΠΌ, ΠΊΠ°Ρ‚ΠΎ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ‚Π΅ `SenderInterface` Π·Π° ΠΈΠ·ΠΏΡ€Π°Ρ‰Π°Π½Π΅ ΠΏΡ€Π΅Π· ΡˆΠΈΠ½Π°Ρ‚Π° (Redis streams).

Π’ конфигурацията Π½Π° ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π° (Π² Ρ‚ΠΎΠ·ΠΈ случай php-di) цялата Ρ‚Π°Π·ΠΈ Π²Ρ€ΡŠΠ·ΠΊΠ° ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½Π° Ρ‚Π°ΠΊΠ°:

        CONTAINER_REDIS_TRANSPORT_SECRET => функция (ContainerInterface $c) {
            return new RedisTransport(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_SECRET),
                $c->get(CONTAINER_SERIALIZER))
            ;
        },
        CONTAINER_REDIS_TRANSPORT_LOG => функция (ContainerInterface $c) {
            return new RedisTransport(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_LOG),
                $c->get(CONTAINER_SERIALIZER))
            ;
        },
        CONTAINER_REDIS_STREAM_RECEIVER_SECRET => функция (ContainerInterface $c) {
            return new RedisReceiver(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_SECRET),
                $c->get(CONTAINER_SERIALIZER)
            );
        },
        CONTAINER_REDIS_STREAM_RECEIVER_LOG => функция (ContainerInterface $c) {
            return new RedisReceiver(
                $c->get(CONTAINER_REDIS_STREAM_CONNECTION_LOG),
                $c->get(CONTAINER_SERIALIZER)
            );
        },
        CONTAINER_REDIS_STREAM_BUS => функция (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 => функция (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 => функция (ContainerInterface $c) {
            $host = 'bu-02-redis';
            $port = 6379;
            $dsn = "redis://$host:$port";
            $options = [
                'stream' => 'log',
                'group' => 'default',
                'consumer' => 'default',
            ];

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

Π’ΡƒΠΊ Π΅ Π²ΠΈΠ΄Π½ΠΎ, Ρ‡Π΅ Π² SendersLocator Π·Π° Π΄Π²Π΅ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ смС Π·Π°Π΄Π°Π»ΠΈ Ρ€Π°Π·Π»ΠΈΡ‡Π΅Π½ β€žΡ‚Ρ€Π°Π½ΡΠΏΠΎΡ€Ρ‚β€œ, всСки ΠΎΡ‚ ΠΊΠΎΠΈΡ‚ΠΎ ΠΈΠΌΠ° собствСна ΡΠ²ΡŠΡ€Π·Π°Π½ΠΎΡΡ‚ Π½Π° ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ‚Π΅ стриймовС.

Π‘ΡŠΠ·Π΄Π°Π΄ΠΎΡ… ΠΎΡ‚Π΄Π΅Π»Π΅Π½ Π΄Π΅ΠΌΠΎ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, дСмонстриращ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ с Ρ‚Ρ€ΠΈ Π΄Π΅ΠΌΠΎΠ½Π°, ΠΊΠΎΠΌΡƒΠ½ΠΈΠΊΠΈΡ€Π°Ρ‰ΠΈ ΠΏΠΎΠΌΠ΅ΠΆΠ΄Ρƒ си Ρ‡Ρ€Π΅Π· Ρ‚Π°ΠΊΠ°Π²Π° шина: https://github.com/backend-university/products/tree/master/products/02-redis-streams-bus.

Но Ρ‰Π΅ ΠΏΠΎΠΊΠ°ΠΆΠ° ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС устроСн ΠΊΠΎΠ½ΡΡŒΡŽΠΌΠ΅Ρ€:

ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ AppMessagesDaemonLogMessage;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerHandlerHandlerDescriptor;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerHandlerHandlersLocator;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerMessageBus;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerMiddlewareHandleMessageMiddleware;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerMiddlewareSendMessageMiddleware;
ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΉΡ‚Π΅ SymfonyComponentMessengerTransportSenderSendersLocator;

изисквайтС вСднъТ __DIR__ . '/.. /vendor/autoload.php';
/** @var PsrContainerContainerInterface $container * /
$container = изисквайтС вСднъТ('config/container.php');

$handlers = [
    DaemonLogMessage::class => [
        Π½ΠΎΠ² HandlerDescriptor(
            функция (DaemonLogMessage $m) {
                error_log('DaemonLogHandler: ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π΅Π½ΠΎ: / ' . $m->getMessage());
            },
            ['from_transport' => CONTAINER_REDIS_TRANSPORT_LOG]
        )
    ],
];
$middleware = [];
$middleware[] = Π½ΠΎΠ² HandleMessageMiddleware(new HandlersLocator($handlers));
$sendersLocator = Π½ΠΎΠ² SendersLocator(['*' => [CONTAINER_REDIS_TRANSPORT_LOG]], $container);
$middleware[] = Π½ΠΎΠ² SendMessageMiddleware($sendersLocator);

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

Π˜Π·ΠΏΠΎΠ»Π·Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚Π°Π·ΠΈ инфраструктура Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ

Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ Ρ€Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°Ρ… ΡˆΠΈΠ½Π°Ρ‚Π° Π² своя Π±Π΅ΠΊΠ΅Π½Π΄, ΠΎΡ‚Π΄Π΅Π»ΠΈΡ… спСцифични ΡΡ‚ΡŠΠΏΠΊΠΈ ΠΎΡ‚ старата синхронна ΠΊΠΎΠΌΠ°Π½Π΄Π° ΠΈ Π½Π°ΠΏΡ€Π°Π²ΠΈΡ… ΠΎΡ‚Π΄Π΅Π»Π½ΠΈ Ρ…Π΅Π½Π΄Π»Π΅Ρ€ΠΈ, всСки ΠΎΡ‚ ΠΊΠΎΠΈΡ‚ΠΎ сС Π·Π°Π½ΠΈΠΌΠ°Π²Π° с ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΈ Π·Π°Π΄Π°Ρ‡ΠΈ.

ΠŸΡ€ΠΎΡ†Π΅ΡΡŠΡ‚ ΠΏΠΎ добавянС Π½Π° Π½ΠΎΠ² сайт Π² Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° Ρ‚Π°ΠΊΠ°:

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

И Π²Π΅Π΄Π½Π°Π³Π° слСд Ρ‚ΠΎΠ²Π° ΠΌΠΈ стана ΠΌΠ½ΠΎΠ³ΠΎ ΠΏΠΎ-лСсно Π΄Π° добавям Π½ΠΎΠ² Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π», Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΈΠ·Π²Π»ΠΈΡ‡Π°Π½Π΅Ρ‚ΠΎ ΠΈ парсинга Π½Π° RSS. Въй ΠΊΠ°Ρ‚ΠΎ Ρ‚ΠΎΠ·ΠΈ процСс ΡΡŠΡ‰ΠΎ изисква ΠΈΠ·Ρ…ΠΎΠ΄Π½ΠΎ ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Π½ΠΈΠ΅, Ρ…Π΅Π½Π΄Π»Π΅Ρ€ΡŠΡ‚, ΠΈΠ·Π²Π»ΠΈΡ‡Π°Ρ‰ Π»ΠΈΠ½ΠΊΠΎΠ²Π΅ към RSS, ΠΊΠ°ΠΊΡ‚ΠΎ ΠΈ WebsiteIndexHistoryPersistor, сС Π°Π±ΠΎΠ½ΠΈΡ€Π° Π·Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ "Content/HtmlContent", ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Π²Π° Π³ΠΎ ΠΈ ΠΏΡ€Π΅Π΄Π°Π²Π° Π½ΡƒΠΆΠ½ΠΎΡ‚ΠΎ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ ΠΏΠΎ своя ΠΏΡŠΡ‚.

ΠŸΡ€Π΅Π²ΠΎΠ΄ Π½Π° PHP Π±Π΅ΠΊΠ΅Π½Π΄Π° към шина Redis streams ΠΈ ΠΈΠ·Π±ΠΎΡ€ Π½Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°, нСзависима ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€Ρ†ΠΈΡ‚Π΅

Π’ ΠΊΡ€Π°ΠΉΠ½Π° смСтка сС ΠΏΠΎΠ»ΡƒΡ‡ΠΈΡ…Π° няколко Π΄Π΅ΠΌΠΎΠ½Π°, всСки ΠΎΡ‚ ΠΊΠΎΠΈΡ‚ΠΎ ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ° Π²Ρ€ΡŠΠ·ΠΊΠΈ само към Π½ΡƒΠΆΠ½ΠΈΡ‚Π΅ рСсурси. НапримСр, Π΄Π΅ΠΌΠΎΠ½ΡŠΡ‚ crawlers ΡΡŠΠ΄ΡŠΡ€ΠΆΠ° всички ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΡ†ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ изискват Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ Π·Π° ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Π½ΠΈΠ΅, Π° Π΄Π΅ΠΌΠΎΠ½ΡŠΡ‚ persister ΠΏΠΎΠ΄Π΄ΡŠΡ€ΠΆΠ° Π²Ρ€ΡŠΠ·ΠΊΠ° с Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ.

Π‘Π΅Π³Π° вмСсто SELECT ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠΈ ΠΎΡ‚ Π±Π°Π·Π°Ρ‚Π° Π΄Π°Π½Π½ΠΈ, Π½ΡƒΠΆΠ½ΠΈΡ‚Π΅ ΠΈΠ΄ слСд вставката ΠΎΡ‚ persister-Π° просто сС ΠΏΡ€Π΅Π΄Π°Π²Π°Ρ‚ ΠΏΡ€Π΅Π· ΡˆΠΈΠ½Π°Ρ‚Π° Π½Π° всички заинтСрСсовани ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΡ†ΠΈ.

Π˜Π·Ρ‚ΠΎΡ‡Π½ΠΈΠΊ: habr.com

ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ πŸ”₯ ΠšΡƒΠΏΠ΅Ρ‚Π΅ Π½Π°Π΄Π΅ΠΆΠ΄Π΅Π½ хостинг Π·Π° сайтовС с Π·Π°Ρ‰ΠΈΡ‚Π° ΠΎΡ‚ DDoS, VPS VDS ΡΡŠΡ€Π²ΡŠΡ€ΠΈ | ProHoster