ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ PHP бэкСнда Π½Π° ΡˆΠΈΠ½Ρƒ Redis streams ΠΈ Π²Ρ‹Π±ΠΎΡ€ нСзависимой ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ

ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ PHP бэкСнда Π½Π° ΡˆΠΈΠ½Ρƒ Redis streams ΠΈ Π²Ρ‹Π±ΠΎΡ€ нСзависимой ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ

ΠŸΡ€Π΅Π΄ΠΈΡΠ»ΠΎΠ²ΠΈΠ΅

Мой сайт, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΌ я занимаюсь Π² качСствС Ρ…ΠΎΠ±Π±ΠΈ, ΠΏΡ€Π΅Π΄Π½Π°Π·Π½Π°Ρ‡Π΅Π½ для хранСния интСрСсных Π΄ΠΎΠΌΠ°ΡˆΠ½ΠΈΡ… страниц ΠΈ ΠΏΠ΅Ρ€ΡΠΎΠ½Π°Π»ΡŒΠ½Ρ‹Ρ… сайтов. Π­Ρ‚Π° Ρ‚Π΅ΠΌΠ° стала ΠΈΠ½Ρ‚Π΅Ρ€Π΅ΡΠΎΠ²Π°Ρ‚ΡŒ мСня Π² самом Π½Π°Ρ‡Π°Π»Π΅ ΠΌΠΎΠ΅Π³ΠΎ ΠΏΡƒΡ‚ΠΈ Π² ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΌΠΈΡ€ΠΎΠ²Π°Π½ΠΈΠΈ, Π² Ρ‚ΠΎΡ‚ ΠΌΠΎΠΌΠ΅Π½Ρ‚ мСня восхищало Π½Π°Ρ…ΠΎΠΆΠ΄Π΅Π½ΠΈΠ΅ Π±ΠΎΠ»ΡŒΡˆΠΈΡ… профСссионалов, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ ΠΏΠΈΡˆΡƒΡ‚ ΠΎ сСбС, своих увлСчСниях ΠΈ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°Ρ…. ΠŸΡ€ΠΈΠ²Ρ‹Ρ‡ΠΊΠ° ΠΎΡ‚ΠΊΡ€Ρ‹Π²Π°Ρ‚ΡŒ ΠΈΡ… для сСбя ΠΎΡΡ‚Π°Π»Π°ΡΡŒ ΠΈ сСйчас: ΠΏΠΎΡ‡Ρ‚ΠΈ Π½Π° ΠΊΠ°ΠΆΠ΄ΠΎΠΌ коммСрчСском ΠΈ Π½Π΅ ΠΎΡ‡Π΅Π½ΡŒ сайтС я ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°ΡŽ Π·Π°Π³Π»ΡΠ΄Ρ‹Π²Π°Ρ‚ΡŒ Π² Ρ„ΡƒΡ‚Π΅Ρ€ Π² поисках ссылок Π½Π° Π°Π²Ρ‚ΠΎΡ€ΠΎΠ².

РСализация ΠΈΠ΄Π΅ΠΈ

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

Π­Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ Π½Π°Π·Π²Π°Ρ‚ΡŒ Π²Ρ‚ΠΎΡ€ΠΎΠΉ вСрсиСй: коллСкция ΠΈΠ· дСсятка тысяч ΡƒΡ€Π»ΠΎΠ² Π² SQLite Ρ‚Π°Π±Π»ΠΈΡ‡ΠΊΠ΅, которая Π·Π°ΠΌΠ΅Π½ΠΈΠ»Π° статичСский список Π² html. По этому списку я сдСлал простой поиск. Π’.ΠΊ. Π±Ρ‹Π»ΠΈ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΡƒΡ€Π»Ρ‹, Ρ‚ΠΎ ΠΈ поиск Π±Ρ‹Π» просто ΠΏΠΎ Π½ΠΈΠΌ.

На этом этапС я забросил ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΈ вСрнулся ΠΊ Π½Π΅ΠΌΡƒ, спустя Π΄ΠΎΠ»Π³ΠΎΠ΅ врСмя. На этом этапС ΠΎΠΏΡ‹Ρ‚ ΠΌΠΎΠ΅ΠΉ Ρ€Π°Π±ΠΎΡ‚Ρ‹ составлял ΡƒΠΆΠ΅ большС Ρ‚Ρ€Ρ‘Ρ… Π»Π΅Ρ‚ ΠΈ я чувствовал, Ρ‡Ρ‚ΠΎ ΠΌΠΎΠ³Ρƒ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ ΠΏΠΎΡΠ΅Ρ€ΡŒΡ‘Π·Π½Π΅Π΅. К Ρ‚ΠΎΠΌΡƒ ΠΆΠ΅ Π±Ρ‹Π»ΠΎ большоС ΠΆΠ΅Π»Π°Π½ΠΈΠ΅ ΠΎΡΠ²Π°ΠΈΠ²Π°Ρ‚ΡŒ ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π½ΠΎΠ²Ρ‹Π΅ для сСбя Ρ‚Π΅Ρ…Π½ΠΎΠ»ΠΎΠ³ΠΈΠΈ.

БоврСмСнная вСрсия

ΠŸΡ€ΠΎΠ΅ΠΊΡ‚ Ρ€Π°Π·Π²Π΅Ρ€Π½ΡƒΡ‚ Π² Π΄ΠΎΠΊΠ΅Ρ€Π΅, Π±Π°Π·Π° ΠΏΠ΅Ρ€Π΅Π²Π΅Π΄Π΅Π½Π° Π½Π° mongoDb, ΠΈ с ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ Π½Π΅Π΄Π°Π²Π½ΠΈΡ… ΠΏΠΎΡ€, Π΄ΠΎΠ±Π°Π²Π»Π΅Π½ рСдис, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ сначала Π±Ρ‹Π» просто для ΠΊΡΡˆΠΈΡ€ΠΎΠ²Π°Π½ΠΈΡ. Π’ качСствС основы ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ ΠΎΠ΄ΠΈΠ½ ΠΈΠ· ΠΌΠΈΠΊΡ€ΠΎΡ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² PHP.

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

НовыС сайты Π΄ΠΎΠ±Π°Π²Π»ΡΡŽΡ‚ΡΡ консольной ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ, которая синхронно Π΄Π΅Π»Π°Π΅Ρ‚ ΡΠ»Π΅Π΄ΡƒΡŽΡ‰Π΅Π΅:

  • Π‘ΠΊΠ°Ρ‡ΠΈΠ²Π°Π΅Ρ‚ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ ΠΏΠΎ URL
  • ВыставляСт Ρ„Π»Π°Π³ ΠΎ Ρ‚ΠΎΠΌ, доступСн Π»ΠΈ Π±Ρ‹Π» HTTPS
  • БохраняСт ΡΡƒΡ‰Π½ΠΎΡΡ‚ΡŒ Π²Π΅Π±-сайта
  • Π˜ΡΡ…ΠΎΠ΄Π½Ρ‹ΠΉ HTML ΠΈ Π·Π°Π³ΠΎΠ»ΠΎΠ²ΠΊΠΈ сохраняСт Π² ΠΈΡΡ‚ΠΎΡ€ΠΈΡŽ «индСксирования»
  • ΠŸΠ°Ρ€ΡΠΈΡ‚ ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚, ΠΈΠ·Π²Π»Π΅ΠΊΠ°Π΅Ρ‚ Title ΠΈ Description
  • Π”Π°Π½Π½Ρ‹Π΅ сохраняСт Π² ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½ΡƒΡŽ ΠΊΠΎΠ»Π»Π΅ΠΊΡ†ΠΈΡŽ

Π­Ρ‚ΠΎΠ³ΠΎ Π±Ρ‹Π»ΠΎ достаточно, Ρ‡Ρ‚ΠΎΠ±Ρ‹ просто Ρ…Ρ€Π°Π½ΠΈΡ‚ΡŒ сайты ΠΈ ΠΎΡ‚ΠΎΠ±Ρ€Π°ΠΆΠ°Ρ‚ΡŒ ΠΈΡ… Π² спискС:

ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ PHP бэкСнда Π½Π° ΡˆΠΈΠ½Ρƒ Redis streams ΠΈ Π²Ρ‹Π±ΠΎΡ€ нСзависимой ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ

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

Π’ΠΎΠΎΠ±Ρ‰Π΅, ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎ, всё ΠΌΠΎΠΆΠ½ΠΎ Π΄Π΅Π»Π°Ρ‚ΡŒ ΠΈ синхронно, Π° Π² web-ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ΄ΠΈΡ‚ΡŒ просто сохранСниС Π£Π Π›Π° для Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎΠ±Ρ‹ монструозный Π΄Π΅ΠΌΠΎΠ½ выполнял всС Π·Π°Π΄Π°Ρ‡ΠΈ для Π£Π Π›ΠΎΠ² ΠΈΠ· списка. Но всё Ρ€Π°Π²Π½ΠΎ Π΄Π°ΠΆΠ΅ Ρ‚ΡƒΡ‚ Π½Π°ΠΏΡ€Π°ΡˆΠΈΠ²Π°Π΅Ρ‚ΡΡ слово Β«ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒΒ». А Ссли ΠΎΡ‡Π΅Ρ€Π΅Π΄ΡŒ Π²Π½Π΅Π΄Ρ€ΠΈΡ‚ΡŒ, Ρ‚ΠΎ ΠΌΠΎΠΆΠ½ΠΎ всС Π·Π°Π΄Π°Ρ‡ΠΈ Ρ€Π°Π·Π΄Π΅Π»ΠΈΡ‚ΡŒ ΠΈ Π²Ρ‹ΠΏΠΎΠ»Π½ΡΡ‚ΡŒ ΠΏΠΎ ΠΊΡ€Π°ΠΉΠ½Π΅ΠΉ ΠΌΠ΅Ρ€Π΅ асинхронно.

РСшСниС

Π’Π½Π΅Π΄Ρ€ΠΈΡ‚ΡŒ ΠΎΡ‡Π΅Ρ€Π΅Π΄ΠΈ ΠΈ ΡΠ΄Π΅Π»Π°Ρ‚ΡŒ event-driven систСму ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠΈ всСх Π·Π°Π΄Π°Ρ‡. И ΠΊΠ°ΠΊ Ρ€Π°Π· Π΄Π°Π²Π½ΠΎ Ρ…ΠΎΡ‚Π΅Π»ΠΎΡΡŒ ΠΏΠΎΠΏΡ€ΠΎΠ±ΠΎΠ²Π°Ρ‚ΡŒ 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 я Π½Π΅ Π΄ΡƒΠΌΠ°Π».

ΠžΡΡ‚Π°Π»ΡŒΠ½Ρ‹Π΅

Всё Π΄Ρ€ΡƒΠ³ΠΎΠ΅, Ρ‡Ρ‚ΠΎ я Π½Π°Ρ…ΠΎΠ΄ΠΈΠ» Π½Π° Π³ΠΈΡ‚Ρ…Π°Π±Π΅ β€” Π½Π΅Π½Π°Π΄Π΅ΠΆΠ½Ρ‹Π΅ ΡƒΡΡ‚Π°Ρ€Π΅Π²ΡˆΠΈΠ΅ ΠΈ Π·Π°Π±Ρ€ΠΎΡˆΠ΅Π½Π½Ρ‹Π΅ ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ΠΈΠΊΠΈ Π±Π΅Π· Π·Π²Π΅Π·Π΄, Ρ„ΠΎΡ€ΠΊΠΎΠ² ΠΈ большого количСства ΠΊΠΎΠΌΠΌΠΈΡ‚ΠΎΠ².

Π’ΠΎΠ·Π²Ρ€Π°Ρ‚ ΠΊ 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 => 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);
        },

Π’ΡƒΡ‚ Π²ΠΈΠ΄Π½ΠΎ, Ρ‡Ρ‚ΠΎ Π² SendersLocator для Π΄Π²ΡƒΡ… Ρ€Π°Π·Π½Ρ‹Ρ… сообщСний ΠΌΡ‹ присвоили Ρ€Π°Π·Π½Ρ‹ΠΉ «транспорт», ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… ΠΈΠΌΠ΅Π΅Ρ‚ свой ΠΊΠΎΠ½Π½Π΅ΠΊΡ‚ Π½Π° ΡΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΠ΅ стримы.

Π― сдСлал ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹ΠΉ Π΄Π΅ΠΌΠΎ-ΠΏΡ€ΠΎΠ΅ΠΊΡ‚, дСмонстрирущий ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ· Ρ‚Ρ€Ρ‘Ρ… Π΄Π΅ΠΌΠΎΠ½ΠΎΠ², ΠΎΠ±Ρ‰Π°ΡŽΡ‰ΠΈΡ…ΡΡ ΠΌΠ΅ΠΆΠ΄Ρƒ собой с ΠΏΠΎΠΌΠΎΡ‰ΡŒΡŽ Ρ‚Π°ΠΊΠΎΠΉ ΡˆΠΈΠ½Ρ‹: https://github.com/backend-university/products/tree/master/products/02-redis-streams-bus.

Но ΠΏΠΎΠΊΠ°ΠΆΡƒ ΠΊΠ°ΠΊ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ устроСн ΠΊΠΎΠ½ΡΡŒΡŽΠΌΠ΅Ρ€:

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

ИспользованиС этой инфраструктуры Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠΈ

Π Π΅Π°Π»ΠΈΠ·ΠΎΠ²Π°Π² ΡˆΠΈΠ½Ρƒ Π² своём бэкСндС, я Π²Ρ‹Π΄Π΅Π»ΠΈΠ» ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ ступСни ΠΈΠ· старой синхронной ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ ΠΈ сдСлал ΠΎΡ‚Π΄Π΅Π»ΡŒΠ½Ρ‹Π΅ хэндлСры, ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… занимаСтся своим Π΄Π΅Π»ΠΎΠΌ.

Пайплайн добавлСния Π½ΠΎΠ²ΠΎΠ³ΠΎ сайта Π² Π±Π°Π·Ρƒ Π΄Π°Π½Π½Ρ‹Ρ… получился Ρ‚Π°ΠΊΠΈΠΌ:

ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ PHP бэкСнда Π½Π° ΡˆΠΈΠ½Ρƒ Redis streams ΠΈ Π²Ρ‹Π±ΠΎΡ€ нСзависимой ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ

И сразу послС этого ΠΌΠ½Π΅ стало Π³ΠΎΡ€Π°Π·Π΄ΠΎ ΠΏΡ€ΠΎΡ‰Π΅ Π΄ΠΎΠ±Π°Π²Π»ΡΡ‚ΡŒ Π½ΠΎΠ²Ρ‹ΠΉ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½Π°Π», Π½Π°ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΈΠ·Π²Π»Π΅Ρ‡Π΅Π½ΠΈΠ΅ ΠΈ парсинг Rss. Π’.ΠΊ. этот процСсс Ρ‚Π°ΠΊΠΆΠ΅ Ρ‚Ρ€Π΅Π±ΡƒΠ΅Ρ‚ исходный ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚, Ρ‚ΠΎ хэндлСр-ΠΈΠ·Π²Π»Π΅ΠΊΠ°Ρ‚Π΅Π»ΡŒ ссылки Π½Π° rss Ρ‚Π°ΠΊΠΆΠ΅ ΠΊΠ°ΠΊ ΠΈ WebsiteIndexHistoryPersistor подписываСтся Π½Π° сообщСниС Β«Content/HtmlContentΒ», ΠΎΠ±Ρ€Π°Π±Π°Ρ‚Ρ‹Π²Π°Π΅Ρ‚ Π΅Π³ΠΎ ΠΈ ΠΏΠ΅Ρ€Π΅Π΄Π°Π΅Ρ‚ Π½ΡƒΠΆΠ½ΠΎΠ΅ сообщСниС ΠΏΠΎ своСму ΠΏΠ°ΠΉΠΏΠ»Π°ΠΉΠ½Ρƒ дальшС.

ΠŸΠ΅Ρ€Π΅Π²ΠΎΠ΄ PHP бэкСнда Π½Π° ΡˆΠΈΠ½Ρƒ Redis streams ΠΈ Π²Ρ‹Π±ΠΎΡ€ нСзависимой ΠΎΡ‚ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠΎΠ² Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ

Π’ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠΌ ΠΈΡ‚ΠΎΠ³Π΅ ΠΏΠΎΠ»ΡƒΡ‡ΠΈΠ»ΠΎΡΡŒ нСсколько Π΄Π΅ΠΌΠΎΠ½ΠΎΠ², ΠΊΠ°ΠΆΠ΄Ρ‹ΠΉ ΠΈΠ· ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Ρ… Π΄Π΅Ρ€ΠΆΠΈΡ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΡ Ρ‚ΠΎΠ»ΡŒΠΊΠΎ ΠΊ Π½ΡƒΠΆΠ½Ρ‹ΠΌ рСсурсам. НапримСр Π΄Π΅ΠΌΠΎΠ½ crawlers содСрТит Π² сСбС всС ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠΈ, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹Π΅ Ρ‚Ρ€Π΅Π±ΡƒΡŽΡ‚ ΠΏΠΎΡ…ΠΎΠ΄Π° Π² ΠΈΠ½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ Π·Π° ΠΊΠΎΠ½Ρ‚Π΅Π½Ρ‚ΠΎΠΌ, Π° Π΄Π΅ΠΌΠΎΠ½ persister Π΄Π΅Ρ€ΠΆΠΈΡ‚ ΠΏΠΎΠ΄ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΈΠ΅ ΠΊ Π±Π°Π·Π΅ Π΄Π°Π½Π½Ρ‹Ρ….

Π’Π΅ΠΏΠ΅Ρ€ΡŒ вмСсто сСлСктов ΠΈΠ· Π±Π°Π·Ρ‹ Π΄Π°Π½Π½Ρ‹Ρ…, Π½ΡƒΠΆΠ½Ρ‹Π΅ id послС вставки persister’ом просто ΠΏΠ΅Ρ€Π΅Π΄Π°ΡŽΡ‚ΡΡ Ρ‡Π΅Ρ€Π΅Π· ΡˆΠΈΠ½Ρƒ всСм заинтСрСсованным ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°ΠΌ.

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