Получи случайную криптовалюту за регистрацию!

.NET backend study

Логотип телеграм канала @dotnetbackendstudy — .NET backend study N
Логотип телеграм канала @dotnetbackendstudy — .NET backend study
Адрес канала: @dotnetbackendstudy
Категории: Технологии
Язык: Русский
Количество подписчиков: 413
Описание канала:

Образовательный канал для backend .NET разработчиков.
Вместе изучаем .NET, SQL, DevOps и немного Web.
Сотрудничество:
@sterlyukin
sterlyukinnikita@gmail.com

Рейтинги и Отзывы

2.00

3 отзыва

Оценить канал dotnetbackendstudy и оставить отзыв — могут только зарегестрированные пользователи. Все отзывы проходят модерацию.

5 звезд

0

4 звезд

0

3 звезд

1

2 звезд

1

1 звезд

1


Последние сообщения

2022-06-22 17:05:03 Конспект по теме "Circuit breaker" из категории "Паттерны"

Данный паттерн рассчитан на ошибки, исправление которых требует много времени. Например, обрыв соединения, отказ оборудования. В данном случае отправка повторных запросов (паттерн Retry), скорее всего, будет бессмысленна. А если к тому же представить, что к какому-то сервису обращается много клиентов и этот сервис становится недоступным, каждый клиент начинает делать повторные попытки своих запросов к неработающему сервису - существенно увеличивается исходящий трафик от пользователей этого сервиса.

Вместо этого есть смысл реализовать некий защитный барьер, который будет останавливать повторяющиеся запросы, если в них нет смысла. В идеале этот паттерн должен использоваться совместно с Retry-паттерном. Данный паттерн проверяет причину, почему запрос провалился и если эта причина не является временной - запрещает отправлять повторные запросы. Если же ошибка является временной - то в дело вступает Retry-паттерн.

Алгоритм работы

Название данного паттерна можно перевести на русский, как “размыкатель цепи”. И в нем существует 3 состояния:

1. Замкнутое
Все запросы корректно проходят, либо успешно завершаются с помощью Retry-паттерна. Есть определенный лимит ошибочных ситуаций, которые не удается обойти с помощью Retry-паттерна(т.е. либо даже после повторных запросов приходит ошибочный результат, либо код ошибки сразу дает понять о бесполезности повторного запроса). В случае, если данный лимит превышается, то приложение переходит в следующее состояние;
2. Разомкнутое
В данном состоянии приложение запрещает отправлять какие-либо запросы. При переходе в это состояние запускается таймер. Как только он истекает, приложение переходит в следующее состояние;
3. Ограниченное
В данном состоянии приложение разрешает отправлять только определенный процент запросов - чтобы сразу не повышать нагрузку до максимума. Если все они завершаются успешно, то считается, что внешний сервис восстановился и приложение переходит в замкнутое состояние. Если же хотя бы один из запросов не проходит, то приложение возвращается в разомкнутое состояние и таймер перезапускается, чтобы дать время внешнему сервису на восстановление.

В качестве реализации также подойдет библиотека Polly.


Вернуться в бэклог категории Паттерны
276 views14:05
Открыть/Комментировать
2022-06-20 18:13:39 Конспект по теме "Retry pattern" из категории "Паттерны"

Retry- это архитектурный паттерн, который позволяет определять стратегию по восстановлению после временных ошибок.
Вот несколько примеров таких ошибок:
⦁ проблемы с интернет соединением;
⦁ повышенная нагрузка на внешний компонент/систему.

Как видно, все эти ошибки являются восстанавливаемыми. И в случае их возникновения есть смысл повторить запрос немедленно или через некоторую задержку вместо логирования ошибки и возвращения ошибочного результата.

Чек лист для внедрения

1. Определите список подходящих ошибок
Нужно определить после каких ошибок системе необходимо повторять запрос. Например, если произошла timeout-ошибка, то есть смысл повторить запрос заново. В случае, если была ошибка авторизации, то повторное выполнение запроса с теми же данными - бессмысленно.
2. Определите количество повторных попыток
Это то количество раз, сколько запрос будет пытаться успешно выполниться. Если этого так и не случится - пользователю будет выдана ошибка.
3. Определите задержку перед повторной попыткой
Это то время, через которое будет произведен повторный запрос в случае ошибки, которая попадает под категорию повторного запроса. В случае клиентских систем, которые должны обеспечивать быстрый отклик - задержка должна быть минимально возможной. В случае, если запросы производятся уже без ожидания пользователем результата - задержка должна быть достаточной для того, чтобы система восстановилась.

Зачастую используют одно из трех значений задержки:
⦁ константное время задержки перед каждым запросом(например, мы определяем задержку = 5 секунд. Получается, в случае ошибки - повторный запрос будет отправлен через 5 секунд и после каждого ошибочного запроса будет задержка в 5 секунд, пока не истечет количество попыток);
⦁ увеличивающееся время (например, мы устанавливаем первоначальное время задержки в 5 секунд и перед каждой следующей попыткой это время будет линейно или экспоненциально увеличиваться);
⦁ рандомное время (устанавливаются минимальные и максимальные границы и по определенному алгоритму рассчитывается время задержки перед следующей попыткой).

Алгоритм работы

1. Отправка запроса от нашего приложения к внешнему сервису;
2. Если код ответа успешный - закончить обработку запроса и продолжить работу приложения;
3. В случае, если код ответа не является успешным - сделать задержку на определенное время;
4. Повторить начиная с п.1 заданное количество раз
5. В случае, если успешный код ответа так и не был получен - залогировать, обработать ошибку и продолжить работу приложения.

Наиболее популярной библиотекой для реализации Retry-паттерна является Polly.


Вернуться в бэклог категории Паттерны
281 views15:13
Открыть/Комментировать
2022-06-14 17:27:08 Конспект по теме "Anti-corruption layer" из категории "Паттерны"

Это архитектурный паттерн, который позволяет преобразовывать запросы от одной системы к другой, не вдаваясь в их подробности.

Решаемая проблема

Представим, что мы пишем систему, ведущую учет отпусков сотрудников. И наша система взаимодействует с корневой ERP системой(система планирования ресурсов предприятия - Enterprise resource planning) нашей компании. Наша система вызывает ендпоинты ERP системы напрямую, передавая туда данные.

Теперь представим ситуацию, что наша компания расширяется и у нас появилось много различных небольших систем, которые взаимодействуют с общей ERP системой. Мы хотим обезопасить себя от возможных разрывов соединений, большой нагрузки и принимаем решение сделать взаимодействие асинхронным через RabbitMQ. Т.е. каждая система будет пушить сообщения в RabbitMQ, а ERP система будет их принимать и уже решать, что с ними делать.

В этом случае, если взаимодействие с ERP системой было тесно переплетено с бизнес логикой внутри нашей системы, то нам придется подвергнуть ее сильному рефакторингу. Т.к. нам придется заменять отправку HTTP-запросов на работу с очередями.

Решение

Для решения проблемы необходимо изолировать слой интеграции - называемый Anti-corruption layer (ACL). Данный уровень будет преобразовывать обмен данными между двумя системами, что позволит не зависеть внутреннему устройству приложения от изменений в сторонних системах.

Схематично, взаимодействие выглядит вот таким образом:
Система учета отпусков → ACL → ERP система

Системы ничего не знаю о внутреннем устройстве друг друга (например, о моделях). ACL имеет доступ к обоим системам и их моделям. Также наша бизнес логика ничего не знает о способе взаимодействия со сторонней системой. Нам известно только то, что нужно передать определенный объект в ACL. А каким образом он будет доставлен в ERP систему - нас не интересует. Вся логика взаимодействия с ERP системой (прямой вызов ендпоинта, пуш в RabbitMQ и т.д.) лежит на нашем ACL.

Работа происходит примерно по следующему алгоритму:

1) Система учета отпусков вызывает метод из ACL, передавая туда свою модель;
2) ACL трансформирует модель нашей системы в модель ERP-системы и оправляет данные в ERP систему(синхронно или асинхронно).

В случае синхронного взаимодействия (через HTTP-вызов):

3) ERP система обрабатывает запрос и возвращает результат в ACL в виде своей модели;
4) ACL преобразовывает модель из ERP системы в модель нашей системы по учету отпусков и отдает ее нам.

Недостатки подхода

⦁ Наличие ACL ухудшит производительность взаимодействия двух систем по причине добавления нового слоя по обработке;
⦁ Добавляя ACL увеличивается сложность системы, т.к. этот слой также нужно будет поддерживать.

Преимущества подхода

⦁ Не нужно изменять систему под интеграцию с другой системой. Вся логика по адаптации моделей и способа взаимодействия будет находиться в ACL;
⦁ Обработка ошибок по взаимодействию двух систем может быть вынесена в отдельный слой. Таким образом наше приложение не будет хранить в себе обработку специфических ошибок, делегируя это ACL.


Вернуться в бэклог категории Паттерны
420 views14:27
Открыть/Комментировать
2022-06-06 18:54:30 Конспект по теме "Исключения" из категории "C#"
Часть II - рекомендации по использованию

⦁ Использовать правильный проброс исключений
Лучше писать вот таким образом

try {}
catch(Exception ex) { throw; }

Вместо написания:

try {}
catch(Exception ex) { throw ex;}

В первом случае будет полностью сохранён стек-трейс исключения. Дополнительную информацию можно добавлять в объект ex.

⦁ Сначала нужно перехватывать наиболее специфичные исключения. Перехват общего Exception можно сделать самым последним. При обработке исключений можно попасть только в один catch-блок, а не последовательно во все перечисленные.
Как пример, во всех специфичных обработчиках можно выводить конкретные сообщения пользователю, а в обработке общего исключения создавать тикет в службе поддержки, т.к. столкнулись с неожиданным исключением;

⦁ Для специфичных ситуаций нужно реализовывать собственные исключения - это поможет при чтении кода и логов. Также будет легче изучать стек-трейс;

⦁ Нельзя проглатывать исключения. При возникновении любого исключения - его нужно либо логировать, либо корректно пробрасывать его дальше. Нельзя оставлять пустым блок catch;

⦁ Вдумчиво выбирайте тип используемого исключения и сообщение для этого исключения. Нужно учитывать, что это сильно ускорит поиск по логам приложения;

⦁ Нужно логировать весь объект исключения целиком, а не только одно сообщение из исключения. Это даст намного более полную информацию при чтении логов;

⦁ Существует 2 способа работы с ошибками:
- сразу бросать исключение при некорректном поведении (fail fast принцип) - как только происходит ошибочная ситуация - выбрасывать исключение. Плюсом данного подхода является простота и отсутствие обвязок. Не создается никаких лишних абстракций, в случае ошибки сразу выбрасывается исключение. В таких системах создается единая точка обработки исключений, которые перехватывают возникающие исключения и преобразовывают их в корректный вид. Минусом может быть сложность переиспользования отдельных компонентов в другой системе, где единой точки перехвата исключений нет и работа идет с помощью различных оберток;

- использовать обертки для методов, которые потенциально могут сгенерировать исключения.
Пример такой обертки - библиотека OperationResult .
В методе, который потенциально может выбросить исключение, возвращаемый тип указывается как OperationResult и опасный участок кода оборачивается в try-catch блок. В случае успешного выполнения метода возвращается OperationResult.Success , в случае некорректного - OperationResult.Fail . Плюсом данного способа является переиспользуемость - данный код полностью автономен и его можно использовать в любых системах. Он гарантирует, что любые возникающие исключения будут корректно обработаны, залогированы и код продолжит свое выполнение. Минусом является громоздкость данного способа засчет использования абстракций.


Вернуться в бэклог категории C#
501 views15:54
Открыть/Комментировать
2022-06-06 18:54:08 Конспект по теме "Исключения" из категории "C#"
Часть I - основы

Исключения - это ошибки, возникающие в рантайме.

Для перехвата исключений используется конструкция:
try { }
catch(Exception ex) { }
finally { }

Разберем каждый из этих блоков:
- try - здесь мы заключаем код, который потенциально может выбросить исключение;
- catch - здесь мы пишем код по обработке конкретного типа исключения. Может быть несколько catch -блоков, каждый из который обрабатывает определенный тип исключения;
- finally - это опциональный блок. Если он присутствует то код внутри него выполнится в любом случае - вне зависимости от того было выброшено исключение или нет.

Помимо перехвата исключений есть также возможность выброса собственных исключений. Это может пригодиться в следующих ситуациях:

- недопустимое значение аргументов;
- обработка некорректного поведения в логике;
- для валидации при вводе данных от пользователя.

Основные типы выбрасываемых исключений:
- ArgumentException - ошибка аргумента (например, значение < 0);
- ArgumentNullException - аргумент является null;
- IndexOutOfRangeException - ошибка индекса в коллекции (например, попытка обратиться к 4-му элементу массива, который состоит из 3 значений);
- InvalidCastException - ошибка преобразования типов.

Фильтры исключений

Фильтры исключений позволяют обрабатывать исключения в зависимости от определённых условий. Пример:

try { }
catch(ExternalApiException ex) when (ex.ResponseCode == 404)
{ }
catch(ExternalApiException ex)
{ }

Обработчиком ошибки будет именно тот блок catch-блок, для которого истинно when-выражение.


Вернуться в бэклог категории C#
389 views15:54
Открыть/Комментировать
2022-05-30 19:00:08 Конспект по теме "JWT" из категории "Web"

JWT(JSON Web Token) - открытый стандарт для создания токенов доступа. Основан на формате JSON. В основном, используется для аутентификации и авторизации в web-приложениях.

Структура

JWT состоит из 3 блоков:
⦁ header(заголовок);
⦁ payload(полезная нагрузка/основные данные);
⦁ signature(подпись).

Header

Заголовок содержит одно обязательное поле:
⦁ alg - алгоритм, используемый для подписи/шифрования. В случае неподписанного JWT используется значение = none .

Необязательные поля:
⦁ typ - тип токена. Используется, когда присутствуют различные виды токенов с JOSE-заголовками (например, JWS и JWE ). По умолчанию имеет значение = JWT .
⦁ cty - тип содержимого. Присутствует только если в содержимом токена есть только служебные поля - в таком случае имеет значение = JWT . В случае наличия пользовательских полей - это поле отсутствует в заголовке.

Payload

Полезная нагрузка содержит пользовательские данные. Также здесь могут присутствовать некоторые служебные поля. Все они являются необязательными:
⦁ iss - издатель токена. Уникальный идентификатор стороны, генерирующей токен;
⦁ sub - уникальный идентификатор объекта, о котором содержится информация в этом токене;
⦁ aud - получатели токена. Список уникальных идентификаторов, являющихся получателями данного токена. Когда принимающая сторона получает токен она должна проверить наличие себя в получателях и при отсутствии - посчитать токен невалидным;
⦁ exp - время в формате Unix Time, определяющее момент, когда токен станет невалидным;
⦁ nbf - время в формате Unix Time, определяющее момент, когда токен станет валидным;
⦁ jti - уникальный идентификатор токена;
⦁ iat - время создания токена в формате Unix Time. Это значение может не совпадать со значением поля nbf , т.к. токен может быть выпущен в одно время, но станет валидным позже.

Какие пользовательские данные будут включены в JWT определяется сервером аутентификации. Туда могут быть включены, например:
⦁ логин пользователя;
⦁ email пользователя;
⦁ AD группа пользователя и т.д.

Signature

Задача данного блока заключается в подтверждении того, что данные из блока Payload не были подменены и являются идентичными изначально переданным.

Виды токенов

⦁ access - токен - это токен, который предоставляет его владельцу доступ к серверу. Обычно выдается на краткий срок;
⦁ refresh - токен - это токен, который позволяет клиентам запрашивать новый access - токен, когда тот истечет. Обычно выдается на долгий срок.

Алгоритм работы

⦁ Клиент проходит аутентификацию в приложении;
⦁ В случае успешной аутентификации - сервер отправляет клиенту access и refresh токены;
⦁ При обращении к серверу клиент использует access-токен. Сервер проверяет его на валидность (критерии успешности этих проверок можно устанавливать вручную). В случае успешного прохождения проверок - клиент получает доступ к ресурсам;
⦁ Когда access-токен устаревает, клиент отправляет серверу refresh-токен и получает в ответ два обновленных токена;
⦁ Если устаревает refresh-токен, то клиенту будет нужно пройти заново процесс аутентификации в приложении.


Вернуться в бэклог категории Web
556 views16:00
Открыть/Комментировать
2022-05-23 22:34:48 Конспект по теме "Оптимизация SELECT запросов" из категории "SQL"
Часть I - основы

⦁ нужно считывать ровно столько данных, сколько нужно. Не нужно использовать SELECT *, вместо этого надо указать необходимые колонки;

⦁ добавить правильно спроектированные индексы (основы индексов, советы по использованию индексов);

⦁ используйте WHERE вместо HAVING, т.к. WHERE сразу отсекает ненужные строки и в конечный набор попадают те, что попадают под условия, в то время как HAVING сначала формирует конечный набор из всех доступных строк, а только потом его фильтрует;

⦁ при наличии WHERE фильтрации необходимо сначала поставить условие, которое отсечёт наибольшее количество строк, чтобы не убирать понемногу, а разом убрать максимум неподходящих вариантов.
Т.е. запрос в идеале должен быть построен вот так:

SELECT Id
FROM Person
WHERE Surname = 'Ivanov' AND Name= 'Ivan'

Запрос выше отработает быстрее, чем:

SELECT Id
FROM Person
WHERE Name = 'Ivan' and Surname = 'Ivanov'

Т.к. Surname - это поле с более уникальным содержимым, соответственно сразу отсечёт больше нерелевантных строк;

⦁ лучше использовать IN вместо OR при большом количестве сравниваемых значений - т.к. IN сортирует список и использует бинарный поиск, в то время как OR сравнивает значения в произвольном порядке;

WHERE Name IN ('Ivan', 'Petr')

Отработает быстрее, чем:

WHERE Name = 'Ivan' OR Name = 'Petr'

Тут нужно учитывать, что оптимизатор самостоятельно спрогнозирует максимально быстрый вариант выполнения и данная оптимизация нужна только в случае, если запрос является очень большим и оптимизатор просто берёт первый вариант выполнения запроса;

⦁ дублирующийся код лучше инкапсулировать в хранимые процедуры. Во время первого её выполнения она будет оптимизирована и для неё будет создан план запроса. И при выполнении она будет вызываться из кэша, что увеличит производительность;

⦁ используйте EXISTS вместо COUNT > 0, т.к. EXISTS выдаст true как только убедится, что существует хотя бы одна строка. В случае COUNT > 0 - будут подсчитаны все строки.


Вернуться в бэклог категории SQL
649 viewsedited  19:34
Открыть/Комментировать
2021-12-16 18:52:41 Конспект по теме "BFF (Backend for Frontend)" из категории "Паттерны"

Данный паттерн позволяет создать некую прослойку между клиентом и всеми backend-микросервисами. Эта прослойка называется BFF.

Алгоритм работы:

⦁ BFF получает запрос от клиента;
⦁ BFF осуществляет обработку и преобразование/обогащение данных, пришедших от клиента (опционально);
⦁ BFF перенаправляет клиентский запрос нужному backend-микросервису;
⦁ Backend-микросервис обрабатывает запрос и отдаёт ответ BFF;
⦁ BFF осуществляет обработку и преобразование/обогащение данных, пришедших от backend-микросервиса (опционально);
⦁ BFF возвращает итоговый ответ клиенту.

Одна из возможных реализаций BFF на клиенте - язык запросов GraphQL (ссылка на конспект по GraphQL).

Преимущества данного подхода:

⦁ Фильтрация - клиент получает только то, что ему нужно;
⦁ Скрываются детали реализации backend. Клиент не привязывается к архитектуре backend. Работа ведётся с прослойкой;
⦁ На клиенте код получается более чистым и пишется меньше логики и проверок;
⦁ На backend появляется прослойка для модификации, предобработки и постобработки данных.

Минусы этого подхода:

⦁ Добавляется ещё один этап обработки запроса - соответственно, повод для проседания производительности;
⦁ Новый код - повод для багов, увеличение сроков разработки.


Вернуться в бэклог категории Паттерны.
1.2K views15:52
Открыть/Комментировать
2021-12-08 13:12:54 Конспект по теме "Способы преобразования пользовательских типов" из категории "C#"

*В тестовой иерархии класс Dog является наследником класса Animal .

⦁ Прямое преобразование

Данное преобразование осуществляется с помощью указания конечного типа в скобках перед преобразуемой переменной:

var dog = new Dog();
try
{
var animal = (Animal)dog;
}
catch (InvalidCastException ex)
{

}

Этот способ не очень удобен тем, что производится "жёсткое" преобразование. И, в случае, если преобразовать переменную не удалось - выбрасывается исключение.

⦁ Преобразование с помощью ключевого слова as

Данный способ позволяет нам сделать преобразование в одну строчку и при этом избежать проверок исключений.

var dog = new Dog();
var animal = dog as Animal;
if(animal != null)
{
//какая-то логика в случае успешного преобразования
}

В случае неудачного преобразования переменная animal будет null.

⦁ Преобразование с помощью ключевого слова is

Также можно осуществлять преобразование с помощью ключевого слова is без обработки потенциального исключения.

var dog = new Dog();
if(dog is Animal animal)
{
//какая-то логика в случае успешного преобразования
}

В случае неудачного преобразования if-условие будет иметь false-значение.


Вернуться в бэклог категории C#
1.2K viewsedited  10:12
Открыть/Комментировать
2021-12-02 15:40:35 Конспект по теме "Примитивы синхронизации" из категории "C#"
Часть I - общая информация

Существует несколько способов для синхронизации доступа нескольких потоков к общему ресурсу.

⦁ Конструкция lock

Примерный вид конструкции:

static object locker = new object();
lock(locker)
{
//код, доступ к которому должен быть синхронизирован
}

Весь код, который находится внутри этой конструкции является недостижимым (т.е. блокируется) для других потоков, пока с ним не завершит работу текущий поток. Т.е. блокировка является эксклюзивной.

⦁ Мониторы

По сути, конструкция lock является синтаксическим сахаром над мониторами, инкапсулируя в себе синтаксис использования мониторов. Блокировка также является эксклюзивной.

Примерный вид конструкции:

try
{
Monitor.Enter();
//код, доступ к которому должен быть синхронизирован
}
finally
{
Monitor.Exit();
}

⦁ Мьютексы

Примерный вид конструкции:

static Mutex mutex = new Mutex();
mutex.WaitOne();
//код, доступ к которому должен быть синхронизирован
mutex.ReleaseMutex();

Позволяет осуществлять межпроцессорную синхронизацию. Блокировка на код также будет эксклюзивной.

⦁ Семафоры

Примерный вид конструкции:

static Semaphore semaphore = new Semaphore(3, 3);
semaphore.WaitOne();
//код, доступ к которому должен быть синхронизирован
semaphore.Release();

Конструктор класса Semaphore принимает 2 параметра: какому числу будет изначально доступен семафор и какое максимальное число объектов будет использовать этот семафор.

Во время выполнения semaphore.Release() в семафоре освобождается одно место - и один из ожидающих потоков займёт освободившееся место.

Т.е. ,как мы видим, блокировка не является эксклюзивной. Сразу несколько потоков будут иметь доступ к коду. И пока один из этих потоков не закончит работу и не освободит ресурс, новый поток не сможет завладеть этим ресурсом.


Вернуться в бэклог категории C#
1.0K views12:40
Открыть/Комментировать