Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

Π›ΠΎΠ³Π²Π°Π½Π΅Ρ‚ΠΎ Π΅ ΠΌΠ½ΠΎΠ³ΠΎ Π²Π°ΠΆΠ΅Π½ инструмСнт Π·Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Ρ‡ΠΈΠΊΠ°, Π½ΠΎ ΠΏΡ€ΠΈ ΡΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ€Π°Π·ΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½ΠΈ систСми, Ρ‚ΠΎ става основСн Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚, ΠΊΠΎΠΉΡ‚ΠΎ трябва Π΄Π° бъдС Π·Π°Π»ΠΎΠΆΠ΅Π½ Π² основата Π½Π° ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π²ΠΈ, Π² ΠΏΡ€ΠΎΡ‚ΠΈΠ²Π΅Π½ случай слоТността Π½Π° Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚ΠΊΠ°Ρ‚Π° Π½Π° микросСрвизи Π±ΡŠΡ€Π·ΠΎ Ρ‰Π΅ сС прояви.

Π’ .Net Core 3 бСшС Π΄ΠΎΠ±Π°Π²Π΅Π½Π° ΠΎΡ‚Π»ΠΈΡ‡Π½Π° Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ Π·Π° ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅ Π½Π° ΠΊΠΎΡ€Π΅Π»Π°Ρ†ΠΈΠΎΠ½Π΅Π½ контСкст Π² HTTP заглавията, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Π°ΠΊΠΎ прилоТСнията Π²ΠΈ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚ Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΈ HTTP извиквания Π·Π° взаимодСйствиС ΠΌΠ΅ΠΆΠ΄Ρƒ услуги, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° сС Π²ΡŠΠ·ΠΏΠΎΠ»Π·Π²Π°Ρ‚Π΅ ΠΎΡ‚ Ρ‚Π°Π·ΠΈ Π²Π³Ρ€Π°Π΄Π΅Π½Π° функция. Π’ΡŠΠΏΡ€Π΅ΠΊΠΈ Ρ‚ΠΎΠ²Π°, Π°ΠΊΠΎ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π°Ρ‚Π° Π½Π° Π±Π΅ΠΊΠ΅Π½Π΄Π° Π²ΠΈ ΠΏΡ€Π΅Π΄Π²ΠΈΠΆΠ΄Π° взаимодСйствиС Ρ‡Ρ€Π΅Π· Π±Ρ€ΠΎΠΊΠ΅Ρ€ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ (RabbitMQ, Kafka ΠΈ Ρ‚.Π½.), всС ΠΏΠ°ΠΊ Ρ‰Π΅ трябва сами Π΄Π° сС ΠΏΠΎΠ³Ρ€ΠΈΠΆΠΈΡ‚Π΅ Π·Π° ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° корСлационния контСкст ΠΏΡ€Π΅Π· Ρ‚Π΅Π·ΠΈ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ.

Π’ Ρ‚Π°Π·ΠΈ статия Ρ‰Π΅ Π²Π·Π΅ΠΌΠ΅ΠΌ просто ΡƒΠ΅Π± API ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈ Ρ‰Π΅ ΠΎΡ€Π³Π°Π½ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ Π»ΠΎΠ³Π²Π°Π½Π΅, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅

  • Π·Π°ΠΏΠ°Π·Π²Π° сквозната корСлация ΠΌΠ΅ΠΆΠ΄Ρƒ Π»ΠΎΠ³ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° нСзависимитС услуги, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ Π΄Π° ΠΌΠΎΠΆΠ΅Ρ‚Π΅ лСсно Π΄Π° Π²ΠΈΠ΄ΠΈΡ‚Π΅ всички активности, ΠΊΠΎΠΈΡ‚ΠΎ са Π±ΠΈΠ»ΠΈ ΠΏΡ€Π΅Π΄ΠΈΠ·Π²ΠΈΠΊΠ°Π½ΠΈ ΠΎΡ‚ ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½Π° заявка ΠΎΡ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°.

  • Π΄Π° ΠΈΠΌΠ°Ρ‚Π΅ Π΅Π΄ΠΈΠ½Π½Π° Ρ‚ΠΎΡ‡ΠΊΠ° Π½Π° Π²Ρ…ΠΎΠ΄ с ΡƒΠ΄ΠΎΠ±Π΅Π½ Π°Π½Π°Π»ΠΈΠ·, Ρ‚Π°ΠΊΠ° Ρ‡Π΅ ΠΈΠ½ΡΡ‚Ρ€ΡƒΠΌΠ΅Π½Ρ‚ΡŠΡ‚ Π·Π° Π»ΠΎΠ³Π²Π°Π½Π΅ Π΄Π° ΠΌΠΎΠΆΠ΅ Π΄Π° сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π΄ΠΎΡ€ΠΈ ΠΎΡ‚ ΠŸΠΎΠ΄Π΄Ρ€ΡŠΠΆΠΊΠ°Ρ‚Π°, която ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π° Π²ΡŠΠΏΡ€ΠΎΡΠΈ ΠΊΠ°Ρ‚ΠΎ "Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΌΠΈ Π΅ изскочила Π³Ρ€Π΅ΡˆΠΊΠ° с Ρ‚Π°ΠΊΡŠΠ²-Ρ‚ΠΎ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π½Π° заявка".

ΠŸΡŠΡ€Π²ΠΎ, трябва Π΄Π° сС ΠΎΠΏΡ€Π΅Π΄Π΅Π»ΠΈΠΌ с доставчика Π½Π° Π»ΠΎΠ³Π²Π°Π½Π΅ Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½ΠΈ. ΠžΡΠ½ΠΎΠ²Π½ΠΎΡ‚ΠΎ изискванС Π·Π° ΡΡŠΠ²Ρ€Π΅ΠΌΠ΅Π½Π½ΠΎΡ‚ΠΎ Π»ΠΎΠ³Π²Π°Π½Π΅ Π΅ структурността, Ρ‚.Π΅. трябва Π΄Π° Ρ€Π°Π±ΠΎΡ‚ΠΈΠΌ Π½Π΅ с плоски тСкстови ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ, Π° с ΠΎΠ±Π΅ΠΊΡ‚ΠΈ. Π‘ Ρ‚Π°ΠΊΠΈΠ²Π° Π»ΠΎΠ³ΠΎΠ²Π΅ лСсно ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΈΠ·Π³Ρ€Π°Π΄ΠΈΠΌ прСдставлСния Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡΡ‚Π° Π½ΠΈ ΠΎΡ‚ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ ъгли ΠΈ Π΄Π° ΠΏΡ€ΠΎΠ²Π΅ΠΆΠ΄Π°ΠΌΠ΅ Π°Π½Π°Π»ΠΈΠ·.

Π—Π° Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Serilog, ΠΊΠΎΠΉΡ‚ΠΎ ΠΈΠΌΠ° ΠΎΡ‚Π»ΠΈΡ‡Π½Π° ΠΏΠΎΠ΄Π΄Ρ€ΡŠΠΆΠΊΠ° Π·Π° структурно Π»ΠΎΠ³Π²Π°Π½Π΅ ΠΈ Π±ΠΎΠ³Π°Ρ‚Π° систСма ΠΎΡ‚ Π΄ΠΎΠ±Π°Π²ΠΊΠΈ. Π©Π΅ пропусна основнитС ΡΡ‚ΡŠΠΏΠΊΠΈ Π·Π° настройка (ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Π½Π°ΠΌΠ΅Ρ€ΠΈΡ‚Π΅ ΠΌΠ½ΠΎΠ³ΠΎ статии ΠΏΠΎ Ρ‚Π΅ΠΌΠ°Ρ‚Π°) ΠΈ Ρ‰Π΅ ΠΏΡ€Π΅Π΄ΠΏΠΎΠ»ΠΎΠΆΠ°, Ρ‡Π΅

  • Serilog Π²Π΅Ρ‡Π΅ Π΅ ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°Π½ ΠΈ Π΅ Π»ΠΎΠ³Π΅Ρ€ ΠΏΠΎ ΠΏΠΎΠ΄Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅ Π½Π° вашия доставчик Π·Π° внСдряванС Π½Π° зависимости.

  • Π² конфигурацията ΠΌΡƒ Π΅ Π²ΠΊΠ»ΡŽΡ‡Π΅Π½ΠΎ обогатяванС Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡΡ‚Π° със свойства ΠΎΡ‚ контСкста (Enrich.FromLogContext).

Π‘Π»Π΅Π΄Π²Π°Ρ‰Π°Ρ‚Π° ΡΡ‚ΡŠΠΏΠΊΠ° Π΅ Π΄Π° ΠΈΠ·Π±Π΅Ρ€Π΅ΠΌ Π² коя систСма Π·Π° Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΈΡ€Π°Π½ΠΎ ΡΡŠΠ±ΠΈΡ€Π°Π½Π΅ Π½Π° Π»ΠΎΠ³ΠΎΠ²Π΅ Π΄Π° ΠΈΠ·ΠΏΡ€Π°Ρ‰Π°ΠΌΠ΅ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ ΠΎΡ‚ Serilog. Най-разпространСната опция Π² ΠΌΠΎΠΌΠ΅Π½Ρ‚Π° срСд ΠΎΡ‚Π²ΠΎΡ€Π΅Π½ΠΈΡ‚Π΅ Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ Π΅ стСка ELK (Elasticsearch, Logstash ΠΈ Kibana), ΠΈ Ρ‚ΠΎΡ‡Π½ΠΎ Π½Π΅Π³ΠΎ Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅. Π—Π° Ρ‚ΠΎΠ²Π° Ρ‰Π΅ сС възползвамС ΠΎΡ‚ ΠΏΡ€Π΅Π΄Π»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° Logz.IO β€” слСд рСгистрация Π½Π° бСзплатния Ρ‚Π°Ρ€ΠΈΡ„ ΠΈΠΌΠ°ΠΌΠ΅ Π½Π° Ρ€Π°Π·ΠΏΠΎΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ цялата ΠΌΠΎΡ‰ Π½Π° Ρ‚ΡŠΡ€ΡΠ°Ρ‡ΠΊΠ°Ρ‚Π° Lucene.

ΠžΡΡ‚Π°Π²Π° Π΄Π° Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π² нашия ΠΏΡ€ΠΎΠ΅ΠΊΡ‚ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Serilog.Sinks.Logzio

Install-Package Serilog.Sinks.Logzio

И Π΄Π° Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ Π΅Π½Ρ€ΠΈΡ‡Π΅Ρ€ Π² конфигурацията Π½Π° нашия Π»ΠΎΠ³Π΅Ρ€, Π·Π°Π΄Π°Π²Π°ΠΉΠΊΠΈ ΠΌΡƒ Ρ‚ΠΎΠΊΠ΅Π½Π° Π·Π° Π΄ΠΎΡΡ‚ΡŠΠΏ

LoggerConfiguration loggerConfig = new LoggerConfiguration();
loggerConfig.WriteTo.Logzio(secrets.LogzioToken, 10, TimeSpan.FromSeconds(10), null, LogEventLevel.Debug);

Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ стартирамС ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, Ρ‰Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° наблюдавамС Π½Π°ΡˆΠΈΡ‚Π΅ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ Π½Π΅ само Π² ΠΊΠΎΠ½Π·ΠΎΠ»Π°Ρ‚Π°, Π½ΠΎ ΠΈ Π² Кибана.

Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡΠΈ

Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

Π’ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ ΠΎΡ‚ Ρ‚ΠΈΠΏ услуга ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΎΡ‚ΠΊΡ€ΠΎΠΈΠΌ Π΄Π²Π° основни интСрфСйса Π·Π° взаимодСйствиС с външния свят, ΠΊΠΎΠΈΡ‚ΠΎ Π΄Π° Π½Π°Ρ€Π΅Ρ‡Π΅ΠΌ Π²Π΅Ρ€Ρ‚ΠΈΠΊΠ°Π»Π΅Π½ ΠΈ horizontal. ВСртикалният интСрфСйс Π΅ ΡƒΠ΅Π±-API, ΠΏΡ€Π΅Π· ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΈΠ΄Π²Π°Ρ‚ повиквания ΠΎΡ‚ клиСнтското ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅. Π₯оризонталният Π΅ Π±Ρ€ΠΎΠΊΠ΅Ρ€ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ, ΠΊΠΎΠΉΡ‚ΠΎ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° Π·Π° ΠΎΠ±ΠΌΠ΅Π½ Π½Π° Π΄Π°Π½Π½ΠΈ с Π΄Ρ€ΡƒΠ³ΠΈ Π²ΡŠΡ‚Ρ€Π΅ΡˆΠ½ΠΈ услуги.

НСка Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΡΡ‚ΡŠΠΏΠΊΠΈΡ‚Π΅ Π·Π° внСдряванС Π½Π° корСлация Π½Π° всСки ΠΎΡ‚ Ρ‚Π΅Π·ΠΈ интСрфСйси.

ΠšΠΎΡ€Π΅Π»Π°Ρ†ΠΈΡ Π² HTTP заявкитС

Π—Π° Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ Π΅ възмоТно ΠΏΠΎΠ²Π΅Ρ‡Π΅ информация, Π΅ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎ Π΄Π° Π³Π΅Π½Π΅Ρ€ΠΈΡ€Π°ΠΌΠ΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π½Π° корСлацията ΠΊΠΎΠ»ΠΊΠΎΡ‚ΠΎ сС ΠΌΠΎΠΆΠ΅ ΠΏΠΎ-Π±Π»ΠΈΠ·ΠΎ Π΄ΠΎ Π½Π°Ρ‡Π°Π»ΠΎΡ‚ΠΎ Π½Π° активността, Ρ‚.Π΅. Π½Π° шлюза ΠΈΠ»ΠΈ Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ Π½Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π° (ΠΌΠΎΠ±ΠΈΠ»Π΅Π½ ΠΈΠ»ΠΈ ΡƒΠ΅Π±). ΠŸΠΎΡΠΊΠΎΠ»ΡŒΠΊΡƒ днСс ΠΈΠΌΠ°ΠΌΠ΅ Ρ€Π°Π±ΠΎΡ‚Π° с Π±Π΅ΠΊΠ΅Π½Π΄ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅, просто Ρ‰Π΅ ΠΎΠ±ΠΎΠ·Π½Π°Ρ‡ΠΈΠΌ изискванСто Π·Π° Π·Π°Π΄ΡŠΠ»ΠΆΠΈΡ‚Π΅Π»Π΅Π½ Π·Π°Π³Π»Π°Π²ΠΈΠ΅Ρ‚ΠΎ β€žX-Correlation-IDβ€œ Π·Π° всички заявки към ΡƒΠ΅Π±-API.

ДобавямС ΠΏΠ°ΠΊΠ΅Ρ‚Π° CorrelationID, чиято функция Π΅ Π΄Π° ΠΈΠ·Π²Π»ΠΈΡ‡Π° стойността ΠΎΡ‚ Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΎΡ‚ΠΎ Π·Π°Π³Π»Π°Π²ΠΈΠ΅

Install-Package CorrelationID

ДобавямС Π³ΠΎ Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π° Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° заявкитС

public class Startup
{
    public void Configure(IApplicationBuilder application)
    {
        application
	    .UseCorrelationId(new CorrelationIdOptions
        {
            Header = "X-Correlation-ID",
            IncludeInResponse = false,
            UpdateTraceIdentifier = false,
            UseGuidForCorrelationId = false
        });
    }
}

Π‘Π΅Π³Π° с Π½Π΅Π³ΠΎΠ²Π°Ρ‚Π° ΠΏΠΎΠΌΠΎΡ‰ Ρ‰Π΅ създадСм прост action-Ρ„ΠΈΠ»Ρ‚ΡŠΡ€:

ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ Π·Π°ΠΏΠ΅Ρ‡Π°Ρ‚Π°Π½ клас ApiRequestFilter : ActionFilterAttribute
{
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ ApiRequestFilter(IApiRequestTracker apiRequestTracker, ICorrelationContextAccessor correlationContextAccessor)
    {
        _correlationContextAccessor = correlationContextAccessor ?? throw new ArgumentNullException(nameof(correlationContextAccessor));
    }
    
    частСн readonly ICorrelationContextAccessor _correlationContextAccessor;
    
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        if (!Guid.TryParse(_correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            context.Result = new BadRequestResult();
            return;
        }
    
        await next.Invoke();
    }
    
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ override async Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next)
    {
        await next.Invoke();
    }
}

И Ρ‰Π΅ Π³ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π² ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅Ρ€Π°

[Route("[controller]")]
[ApiController]
[ServiceFilter(typeof(ApiRequestFilter))]
ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ клас CarsController : ControllerBase
{

}

Π’ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ ΠΊΠΎΠ½Ρ‚Ρ€ΠΎΠ»Π΅Ρ€ΡŠΡ‚ Ρ‰Π΅ ΠΈΠ·Π΄Π°Π²Π° 400 Bad Request Π½Π° всички заявки Π±Π΅Π· Π·Π°Π³Π»Π°Π²ΠΊΠ° със ΡΡŠΠΎΡ‚Π²Π΅Ρ‚Π½ΠΈΡ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€.

Π‘Π»Π΅Π΄ ΠΊΠ°Ρ‚ΠΎ Π·Π°ΠΏΠΎΡ‡Π½Π°Ρ…ΠΌΠ΅ Π΄Π° ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ ΠΎΡ‚ ΠΊΠ»ΠΈΠ΅Π½Ρ‚Π°, трябва Π΄Π° Π³ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ Π² контСкста Π½Π° Π»ΠΎΠ³Π²Π°Π½Π΅Ρ‚ΠΎ, Ρ‰Π΅ създадСм ΠΎΠ±Π³Ρ€ΡŠΡ‰Π°Ρ‰Π° прослойка Π·Π° Ρ†Π΅Π»Ρ‚Π°:

ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ клас CorrelationIdContextLogger
{
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ CorrelationIdContextLogger(RequestDelegate next)
    {
        _next = next ?? throw new ArgumentNullException(nameof(next));
    }
    
    readonly RequestDelegate _next;
    
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ async Task InvokeAsync(HttpContext httpContext, ILogger logger, ICorrelationContextAccessor correlationContextAccessor)
    {
        if (Guid.TryParse(correlationContextAccessor.CorrelationContext.CorrelationId, out Guid correlationId))
        {
            using (logger.BeginScopeWith(("CorrelationId", correlationId)))
            {
                await _next(context);
            }
        }
        else
        {
            await _next(context);
        }
    }
}

Π’ Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ стандартния ILogger ΠΎΡ‚ ΠΏΠ°ΠΊΠ΅Ρ‚Π° Microsoft.Extensions.Logging.Abstractions, Π·Π°Ρ‚ΠΎΠ²Π° стойността Ρ‰Π΅ добавямС с простo Ρ€Π°Π·ΡˆΠΈΡ€Π΅Π½ΠΈΠ΅ към Π½Π΅Π³ΠΎ.

ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ static IDisposable BeginScopeWith(this ILogger logger, params (string key, object value)[] keys)
{
    return logger.BeginScope(keys.ToDictionary(x => x.key, x => x.value));
}

ДобавямС прослойка Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π° Π·Π° ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° Π½Π° заявки ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°ΠΌΠ΅ ТСлания Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚.

ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ клас Startup
{
    ΠΏΡƒΠ±Π»ΠΈΡ‡Π΅Π½ void Configure(IApplicationBuilder application)
    {
        application.UseMiddleware();
    }
}

Π‘Π΅Π³Π° всички активности, ΠΏΡ€ΠΎΠΈΠ·Ρ‚ΠΈΡ‡Π°Ρ‰ΠΈ ΠΎΡ‚ заявкитС към Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΡƒΠ΅Π± API, ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Ρ‚ ΠΊΠΎΡ€Π΅Π»Π°Ρ†ΠΈΠΎΠ½Π΅Π½ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€, ΠΏΠΎ ΠΊΠΎΠΉΡ‚ΠΎ лСсно ΠΌΠΎΠ³Π°Ρ‚ Π΄Π° Π±ΡŠΠ΄Π°Ρ‚ ΡΠ²ΡŠΡ€Π·Π°Π½ΠΈ.

Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

ΠšΠΎΡ€Π΅Π»Π°Ρ†ΠΈΡ Π² ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡΡ‚Π° Π½Π° Π±Ρ€ΠΎΠΊΠ΅Ρ€Π°

Π‘Π»Π΅Π΄Π²Π°Ρ‰Π°Ρ‚Π° ΡΡ‚ΡŠΠΏΠΊΠ° Π΅ Π΄Π° настроим ΠΏΡ€Π΅Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ ΠΈ ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° корСлационния ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Ρ‡Ρ€Π΅Π· Π±Ρ€ΠΎΠΊΠ΅Ρ€ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ. Π’ нашия ΠΏΡ€ΠΈΠΌΠ΅Ρ€ Ρ‰Π΅ ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ RabbitMQ, Π° Π·Π° ΠΊΠ»ΠΈΠ΅Π½Ρ‚ Ρ‰Π΅ Π²Π·Π΅ΠΌΠ΅ΠΌ Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠ° MassTransit. ΠžΡ‚Π½ΠΎΠ²ΠΎ, Ρ‰Π΅ пропуснСм Π½Π°Ρ‡Π°Π»Π½Π°Ρ‚Π° конфигурация Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с MassTransit ΠΈ Ρ‰Π΅ ΠΏΡ€Π΅ΠΌΠΈΠ½Π΅ΠΌ Π½Π°ΠΏΡ€Π°Π²ΠΎ към настройкитС Π·Π° Π»ΠΎΠ³Π²Π°Π½Π΅.

Π—Π° Π½Π°Ρ‡Π°Π»ΠΎ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π°ΠΊΡ‚ΠΈΠ²ΠΈΡ€Π°ΠΌΠ΅ Π»ΠΎΠ³ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° самия MassTransit, Π·Π° Ρ‚Π°Π·ΠΈ Ρ†Π΅Π» добавямС Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½ΠΈ ΠΏΠ°ΠΊΠ΅Ρ‚Π° MassTransit.SerilogIntegration

Install-Package MassTransit.SerilogIntegration

Π‘Π΅Π³Π° слСд добавянСто Π½Π° Π»ΠΎΠ³Π΅Ρ€Π° Π² настройкитС Π½Π° MassTransit Ρ‰Π΅ ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° Π²ΠΈΠΆΠ΄Π°ΠΌΠ΅ Π»ΠΎΠ³ΠΎΠ²Π΅Ρ‚Π΅ Π½Π° Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊΠ°.

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
            });
        });

НСка Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅ Π΄Π° ΠΈΠ·ΠΏΡ€Π°Ρ‰Π° ΡΡŠΠ±ΠΈΡ‚ΠΈΠ΅ SomethingDoneMessage със стойност β€ždoneβ€œ Π² ΠΎΡ‚Π³ΠΎΠ²ΠΎΡ€ Π½Π° POST заявка. ΠšΠΎΠ½Ρ‚Ρ€Π°ΠΊΡ‚ΡŠΡ‚ Π½Π° Ρ‚Π°ΠΊΠΎΠ²Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ ΠΌΠΎΠΆΠ΅ Π΄Π° бъдС описан Ρ‚Π°ΠΊΠ°:

namespace MbMessages
{
    public interface ISomethingDoneMessageV1
    {
        string Value { get; }
    }
}

Π‘ΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡΡ‚Π° Π½Π° MassTransit Π²ΡΡŠΡ‰Π½ΠΎΡΡ‚ прСдставляват ΠΏΠ»ΠΈΠΊ, Π² ΠΊΠΎΠΉΡ‚ΠΎ са Π²Π»ΠΎΠΆΠ΅Π½ΠΈ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡΡ‚Π° Π½Π° Π±Ρ€ΠΎΠΊΠ΅Ρ€Π°. ΠŸΠ»ΠΈΠΊΡŠΡ‚ ΠΈΠ·Π³Π»Π΅ΠΆΠ΄Π° ΠΏΡ€ΠΈΠ±Π»ΠΈΠ·ΠΈΡ‚Π΅Π»Π½ΠΎ Ρ‚Π°ΠΊΠ°:

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "value": "done"
  }
}

Π’ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ сС Π²ΠΈΠΆΠ΄Π°Ρ‚ слуТСбнитС ΠΏΠΎΠ»Π΅Ρ‚Π°, ΠΊΠΎΠΈΡ‚ΠΎ са Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΈ Π·Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΠΎΠ½ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° самия Ρ„Ρ€Π΅ΠΉΠΌΠ²ΠΎΡ€ΠΊ, Π½ΠΎ ΠΈΠΌΠ°ΠΌΠ΅ Π²ΡŠΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ Π΄Π° добавямС ΠΈ наши собствСни Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΈ свойства. ОсвСн Ρ‚ΠΎΠ²Π°, MassTransit Ρ€Π°Π·ΠΏΠΎΠ»Π°Π³Π° с Π²Π³Ρ€Π°Π΄Π΅Π½ΠΈ срСдства Π·Π° Ρ€Π°Π±ΠΎΡ‚Π° с някои ΠΎΠΏΡ†ΠΈΠΎΠ½Π°Π»Π½ΠΈ ΠΏΠΎΠ»Π΅Ρ‚Π°, ΠΎΡ‚ ΠΊΠΎΠΈΡ‚ΠΎ Π½Π°ΠΉ-интСрСсно Π·Π° нас Π΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΡŠΡ‚ Π½Π° корСлационност CorrelationId.

ДобавямС към Π΄ΠΎΠ³ΠΎΠ²ΠΎΡ€Π° Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ интСрфСйса CorrelatedBy:

namespace MbMessages
{
    public interface ISomethingDoneMessageV1 : CorrelatedBy
    {
        string Value { get; }
    }
}

Π Π΅Π°Π»ΠΈΠ·ΠΈΡ€Π°ΠΌΠ΅ Π³ΠΎ ΠΈ Ρ‰Π΅ Π·Π°Π΄Π°Π²Π°ΠΌΠ΅ стойност Π½Π° свойството CorrelationId ΠΏΡ€ΠΈ ΡΡŠΠ·Π΄Π°Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅:

internal class SomethingDoneMessageV1 : ISomethingDoneMessageV1
{
    internal SomethingDoneMessageV1(Guid correlationId, string value)
    {
        CorrelationId = correlationId;
        Value = value;
    }
    
    public Guid CorrelationId { get; private set; }
    public string Value { get; private set; }
}

ΠšΠΎΠ³Π°Ρ‚ΠΎ Ρ€Π°Π·Π³Π»Π΅Π΄Π°ΠΌΠ΅ ΠΎΠ±Π½ΠΎΠ²Π΅Π½ΠΎΡ‚ΠΎ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅, Ρ‰Π΅ Π²ΠΈΠ΄ΠΈΠΌ, Ρ‡Π΅ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ΡŠΡ‚ Π½Π° корСлацията Π΅ станал Π½Π΅ само част ΠΎΡ‚ Π½Π°ΡˆΠ΅Ρ‚ΠΎ ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅, Π½ΠΎ ΠΈ част ΠΎΡ‚ ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π° β€” Ρ‚ΠΎΠ·ΠΈ ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ сСга Ρ‰Π΅ сС ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π° ΠΈ във всички Π»ΠΎΠ³ΠΎΠ²Π΅ Π½Π° MassTransit, ΠΊΠΎΠ΅Ρ‚ΠΎ Ρ‰Π΅ улСсни Ρ€Π°Π·Π±ΠΈΡ€Π°Π½Π΅Ρ‚ΠΎ Π½Π° ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈΡ‚Π΅ Π½Π° Π½ΠΈΠ²ΠΎ Π±Ρ€ΠΎΠΊΠ΅Ρ€ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ.

{
  "messageId": "59020000-5dba-0015-10b8-08d77ec28593",
  "requestId": "59020000-5dba-0015-5674-08d77ec28592",
  "conversationId": "59020000-5dba-0015-bca8-08d77ec28594",
  "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
  "destinationAddress": "rabbitmq://bear.rmq.cloudamqp.com/aelzlsta/ya.servicetemplate.receiveendpoint",
  "headers": {},
  "messageType": [
    "urn:message:MbMessages:ISomethingDoneMessageV1"
  ],
  "message": {
    "correlationId": "c7ff562a-b639-415b-9add-c9e524a727cc",
    "value": "Hello"
  }
}

ΠžΡΡ‚Ρ€Π°Π½ΡΠ²Π°ΠΌΠ΅ само Π΄Π° ΠΊΠΎΠ½Ρ„ΠΈΠ³ΡƒΡ€ΠΈΡ€Π°ΠΌΠ΅ Π»ΠΎΠ³Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π° Ρ‚Π΅Π·ΠΈ слуТСбни свойства Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ, ΠΊΠ°Ρ‚ΠΎ Π΄ΠΎΠ±Π°Π²ΠΈΠΌ ΠΏΠ°ΠΊΠ΅Ρ‚ Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Serilog.Enrichers.MassTransitMessage. ΠŸΠ°ΠΊΠ΅Ρ‚ΡŠΡ‚ добавя Ρ„ΠΈΠ»Ρ‚ΡŠΡ€ Π² ΠΊΠΎΠ½Π²Π΅ΠΉΠ΅Ρ€Π° Π½Π° MassTransit, ΠΊΠΎΠΉΡ‚ΠΎ поставя контСкста Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅Ρ‚ΠΎ Π² потокобСзопасСн стСк. Serilog Ρ‡Π΅Ρ‚Π΅ контСкста ΠΎΡ‚ стСка ΠΈ добавя Ρ‚Π΅Π·ΠΈ Π΄ΠΎΠΏΡŠΠ»Π½ΠΈΡ‚Π΅Π»Π½ΠΈ свойства Π² Π½Π°ΡˆΠΈΡ‚Π΅ Π»ΠΎΠ³ΠΎΠ²Π΅.

Install-Package Serilog.Enrichers.MassTransitMessage

Π’ MassTransit добавямС Ρ„ΠΈΠ»Ρ‚ΡŠΡ€

services
    .AddSingleton(provider =>
        {
            return Bus.Factory.CreateUsingRabbitMq(cfg =>
            {
                cfg.UseSerilog();
                cfg.UseSerilogMessagePropertiesEnricher();
            });
        });

А Π² конфигурацията Π½Π° Serilog добавямС Π΅Π½Ρ€ΠΈΡ‡Π΅Ρ€

Log.Logger = new LoggerConfiguration()
    .Enrich.FromMassTransitMessage()
    .CreateLogger();

Въй ΠΊΠ°Ρ‚ΠΎ ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ, ΠΊΠΎΠ΅Ρ‚ΠΎ ΠΏΠΎΠ»ΡƒΡ‡Π°Π²Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ ΠΎΡ‚ ΠΎΠΏΠ°ΡˆΠΊΠ°Ρ‚Π° RabbitMQ, ΠΈΠΌΠ° Π΄ΠΎΡΡ‚ΡŠΠΏ Π΄ΠΎ всички свойства Π½Π° ΠΊΠΎΠ½Π²Π΅Ρ€Ρ‚Π° Π½Π° MassTransit, ΠΌΠΎΠΆΠ΅ΠΌ Π΄Π° ΠΈΠ·ΠΏΠΎΠ»Π·Π²Π°ΠΌΠ΅ получСния ΠΈΠ΄Π΅Π½Ρ‚ΠΈΡ„ΠΈΠΊΠ°Ρ‚ΠΎΡ€ Π½Π° корСлацията Π² ΠΏΡ€ΠΈΠ»ΠΎΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ Π½Π° потрСбитСля ΠΈ ΡΡŠΡ‰ΠΎ Ρ‚Π°ΠΊΠ° Π΄Π° Π³ΠΎ ΠΏΡ€Π΅Π΄Π°Π²Π°ΠΌΠ΅ ΠΏΠΎ цялата Π²Π΅Ρ€ΠΈΠ³Π° ΠΎΡ‚ повиквания.

Π’ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Π½Π° Ρ‚ΠΎΠ²Π° Π½Π°ΡˆΠΈΡ‚Π΅ Π»ΠΎΠ³ΠΎΠ²Π΅ ΡΡŠΠ΄ΡŠΡ€ΠΆΠ°Ρ‚ CorrelationId Π½Π΅ само Π² Ρ€Π°ΠΌΠΊΠΈΡ‚Π΅ Π½Π° Π΅Π΄ΠΈΠ½ сСрвис, Π½ΠΎ ΠΈ ΠΏΡ€ΠΈ взаимодСйствиСто с Π΄Ρ€ΡƒΠ³ΠΈ прилоТСния.

Π›ΠΎΠ³ΠΈΡ€Π°Π½Π΅ Π² микросСрвисна срСда .Net Π½Π° ΠΏΡ€Π°ΠΊΡ‚ΠΈΠΊΠ°

Π’Π°ΠΊΠ° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π°Ρ‚Π° систСма Π·Π° Π»ΠΎΠ³Π²Π°Π½Π΅ Π² .Net прилоТСния позволява Π±Π΅Π· особСни ΠΏΡ€ΠΎΠ±Π»Π΅ΠΌΠΈ Π΄Π° ΠΊΠΎΡ€Π΅Π»ΠΈΡ€Π°ΠΌΠ΅ Π»ΠΎΠ³ΠΎΠ²Π΅Ρ‚Π΅ ΠΎΡ‚ Π°Π±ΡΠΎΠ»ΡŽΡ‚Π½ΠΎ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ микросСрвизи β€” Π΄ΠΎΡ€ΠΈ ΠΈ Ρ‚Π΅Π·ΠΈ, ΠΊΠΎΠΈΡ‚ΠΎ работят Ρ‡Ρ€Π΅Π· Π±Ρ€ΠΎΠΊΠ΅Ρ€ Π½Π° ΡΡŠΠΎΠ±Ρ‰Π΅Π½ΠΈΡ. А с ΠΏΠΎΠΌΠΎΡ‰Ρ‚Π° Π½Π° Elasticsearch ΠΌΠΎΠΆΠ΅ΠΌ Π±ΡŠΡ€Π·ΠΎ ΠΈ ΡƒΠ΄ΠΎΠ±Π½ΠΎ Π΄Π° ΠΈΠ·Π²ΡŠΡ€ΡˆΠ²Π°ΠΌΠ΅ Π°Π½Π°Π»ΠΈΠ· Π½Π° Π»ΠΎΠ³ΠΎΠ²Π΅Ρ‚Π΅, ΠΊΠ°Ρ‚ΠΎ ΠΈΠ·Π³Ρ€Π°Π΄ΠΈΠΌ Π² Kibana Π½Π΅ΠΎΠ±Ρ…ΠΎΠ΄ΠΈΠΌΠΈΡ‚Π΅ Π½ΠΈ Ρ‚Π°Π±Π»Π° Π·Π° ΡƒΠΏΡ€Π°Π²Π»Π΅Π½ΠΈΠ΅ (ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΡŠΡ‚ Π΅ ΠΏΠΎΠΊΠ°Π·Π°Π½ Π½Π° ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠ΅Ρ‚ΠΎ към публикацията).

Π Π°Π·Π±ΠΈΡ€Π° сС, Π² Ρ‚ΠΎΠ·ΠΈ Π²ΠΈΠ΄ Π»ΠΎΠ³Π²Π°Π½Π΅Ρ‚ΠΎ Π½Π΅ ΠΏΠΎΠΊΡ€ΠΈΠ²Π° слоТнитС Π²Π°Ρ€ΠΈΠ°Π½Ρ‚ΠΈ Π·Π° взаимодСйствиС ΠΌΠ΅ΠΆΠ΄Ρƒ Π²Π°ΡˆΠΈΡ‚Π΅ услуги ΠΈ Ρ€Π°Π·Π»ΠΈΡ‡Π½ΠΈ външни систСми, Π½ΠΎ установяванСто Π½Π° Ρ‚Π°ΠΊΡŠΠ² Ρ€Π΅Π΄ Π² самото Π½Π°Ρ‡Π°Π»ΠΎ Π½Π° Ρ€Π°Π·Π²ΠΈΡ‚ΠΈΠ΅Ρ‚ΠΎ Π½Π° ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π° Π΅ Π΅Π΄Π½Π° ΠΎΡ‚ Ρ‚Π΅Π·ΠΈ Π½Π΅Ρ‰Π°, Π·Π° ΠΊΠΎΠΈΡ‚ΠΎ сами Ρ‰Π΅ си Π±Π»Π°Π³ΠΎΠ΄Π°Ρ€ΠΈΡ‚Π΅.

ΠœΠΎΠΆΠ΅Ρ‚Π΅ Π΄Π° Ρ€Π°Π·Π³Π»Π΅Π΄Π°Ρ‚Π΅ изходния ΠΊΠΎΠ΄ Π½Π° ΠΏΠΎΠ»ΡƒΡ‡Π΅Π½Π°Ρ‚Π° систСма Π² ΠΏΡ€ΠΎΠ΅ΠΊΡ‚Π°: github.com/a-postx/YA.ServiceTemplate

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

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