.NET Разработчик
6.51K subscribers
427 photos
2 videos
14 files
2.04K links
Дневник сертифицированного .NET разработчика.

Для связи: @SBenzenko

Поддержать канал:
- https://boosty.to/netdeveloperdiary
- https://patreon.com/user?u=52551826
- https://pay.cloudtips.ru/p/70df3b3b
Download Telegram
День 2181. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 39. Даже небольшие физические расстояния препятствуют общению и совместной работе


Совместная работа наиболее эффективна, когда коллеги находятся рядом и могут общаться беспрепятственно. Даже если ваш коллега находится в другом кабинете, до которого нужно дойти, либо использовать телефон, коммуникация уже усложняется. Тем более, если вы разделены географически.

Чем больше часовых поясов разделяют общающихся, тем серьёзнее проблема со связью и тем меньше количество рабочих часов, в течение которых люди могут взаимодействовать. В одном проекте участвовали люди из нескольких стран, находящихся в 12 часовых поясах. Какое бы время они ни выбрали для созвонов, оно было для кого-то неудобным. Время постоянно менялось, чтобы неудобства причинялись всем примерно в равной степени. Эта любезность демонстрировала уважение к каждому и давала важный культурный сигнал, указывающий, что все люди независимо от их места работы одинаково важны для проекта.

Организация офисных помещений так, чтобы внутренняя обстановка способствовала совместной работе и при этом сохранялась приватность, позволяющая сотрудникам быть сосредоточенными на работе, — тонкое искусство. Работники умственного труда наиболее продуктивны, когда входят в состояние потока, глубоко погружаясь в задачу. Поддержание состояния потока требует отсутствия отвлекающих факторов. Т.е. люди должны быть достаточно близко друг к другу, чтобы легко взаимодействовать, но им также необходимо личное пространство для сосредоточения внимания.

Виртуальные команды: максимальное разделение
Все больше IT-специалистов работают удалённо. Однако возникновение виртуальных команд порождает новый набор сложностей с организацией культуры сотрудничества. Трудно узнать и понять коллег, которых вы никогда не встречали лично, не говоря уже о том, чтобы выстроить общую культуру ценностей и практик или интегрироваться в неё.

Гугл по запросу «сложности виртуальных команд разработчиков ПО» выдаёт множество статей, посвященных соответствующим проблемам. Если вы работаете удалённо, стоит потратить время, чтобы изучить основные сложности и подумать, как добиться успеха.

Дверь, дверь, королевство за дверь!
Закрытая дверь — барьер для общения. В зависимости от того, по какую сторону двери вы находитесь, она может быть преимуществом или неудобством. Если двери нет, то это способствует общению, с другой стороны, как сообщить коллегам, что вы заняты? Можно использовать табличку с надписью «Свободен» с одной стороны и «Занят» с другой. Либо какой-то другой похожий сигнал. Но надо договориться об этом с коллегами, потому что далеко не все понимают намёки. В некоторых компаниях эксперты, оказывающие консультативные услуги, устанавливают рабочие часы, в течение которых к ним можно прийти для обсуждения назревших вопросов.

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

Не существует идеального решения для организации рабочих пространств, которые могли бы удовлетворить противоречивые потребности в приватности, свободе от отвлекающих факторов, близости к коллегам в целях быстрого взаимодействия и общем пространстве для совещаний по проекту. Географическое разделение вызывает проблемы, а культурные различия значительно усложняют эффективное сотрудничество. Инструменты для совместной работы помогают сотрудничать людям, разделённым большими расстояниями, но не могут полностью заменить личное взаимодействие в реальном времени. По мере возможности руководители должны позволять членам команды самим организовывать их рабочие места, чтобы наилучшим образом сбалансировать эти противоречивые цели. А если это невозможно, есть наушники или беруши.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍12👎9
День 2182. #МоиИнструменты
SQL-запросы к Логам и Другим Текстовым Данным
Сразу оговорюсь, что этот инструмент древний как мамонты, и в эпоху структурированных логов, Seq и прочих мощных визуализаторов, он, возможно, мало кому будет интересен, но вдруг. Я обнаружил его только недавно по необходимости.

Попросил меня коллега выбрать из логов IIS айпишники клиентов, которые обращались к определённому URL. Логи IIS – это текстовые файлы вот такого формата:
#Fields: date time s-sitename s-computername s-ip cs-method cs-uri-stem cs-uri-query s-port cs-username c-ip cs-version cs(User-Agent) cs(Referer) sc-status sc-substatus sc-win32-status time-taken
2025-01-15 00:00:00 W3SVC7 WWW-VM-B 127.0.0.1 GET /page.html - 443 - 8.8.8.8 HTTP/1.1 Mozilla/5.0+… - 200 0 0 103

И дальше ещё много таких строчек (в моём случае было 2 файла по 1Гб+).

Так вот, у Microsoft есть утилита Log Parser которая обеспечивает универсальный доступ через SQL-запросы к текстовым данным, таким как файлы логов, XML-файлы и CSV-файлы, а также к источникам данных в операционной системе Windows, таким как журнал событий, реестр, файловая система и Active Directory.

Но это утилита для командной строки, а для любителей GUI, есть Log Parser Studio (скачать можно здесь), которая работает поверх Log Parser. На картинке выше пример результата запроса к логам IIS. Кроме того, Log Parser Studio позволяет выполнять разные запросы на нескольких вкладках, экспортировать результаты, сгенерировать скрипт для PowerShell, чтобы выполнять его на любой машине, где установлен Log Parser, а также содержит десятки шаблонов популярных запросов.

В общем, небольшая, но полезная в отдельных случаях утилитка.

Источник: https://techcommunity.microsoft.com/blog/exchange/log-parser-studio-2-0-is-now-available/593266
👍30
День 2183. #TipsAndTricks
Упрощаем Отладку Сторонних Типов

При отладке часто бывает полезно иметь возможность проверять состояние объектов в отладчике. Однако, когда вы работаете со сторонними типами, которые не имеют форматирования при отладке, вы не можете легко посмотреть их внутреннее состояние. Сегодня посмотрим, как добавить форматирование при отладке к сторонним типам.

DebuggerDisplayAttribute — очень полезный атрибут, позволяющий указать, как объект должен отображаться в отладчике. Ваши типы можно пометить этим атрибутом так:
[DebuggerDisplay("Name = {Name}, Age = {Age}")]
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
}

var person = new Person { Name = "John", Age = 42 };


Это заставит отладчик отображать экземпляр объекта Person как Name = John, Age = 42.

Но что, если вы работаете со сторонним типом, в который вы не можете добавить атрибут DebuggerDisplay?

Существует менее известное свойство Target, которое определяет, к какому типу следует применять атрибут DebuggerDisplay. Мы можем использовать его в сочетании с атрибутом assembly, чтобы добавить любому типу форматирование при отладке. Для простоты добавим его для типа Task, хотя он уже отображается отформатированным при отладке.
[assembly: DebuggerDisplay("Is Task finished: {IsCompleted}", Target = typeof(Task))]

var task = Task.Delay(100_000);
await task;

Теперь при наведении мыши на переменную task вы увидите результат, похожий на тот, что на картинке ниже.

У этого подхода есть некоторые «естественные» недостатки:
1. Вы можете помечать только публичные типы или интерфейсы по очевидным причинам.
2. Может быть неочевидно, откуда берётся отладочная информация. Особенно, если у вас большая кодовая база, и кто-то в её «потайном уголке» помечает тип атрибутом DebuggerDisplay для форматирования при отладке.

Источник: https://www.meziantou.net/hsts-for-httpclient-in-dotnet.htm
👍35
День 2184. #ЗаметкиНаПолях
Масштабируем Монолиты. Начало
Монолиты среди разработчиков пользуются дурной славой. Считается что они устарели, не масштабируются и что для успеха нам нужны микросервисы. Это неправда. Хорошо спроектированный монолит - часто правильный выбор и может хорошо масштабироваться. Ключ к успеху — понимание ваших потребностей в масштабировании и применение правильных решений в нужное время.

Понимание масштаба
Монолит помещает весь код в одну развёртываемую единицу. Его преимущества - более быстрые циклы разработки, более простые отладка и развёртывания. Но по мере роста системы вы столкнётесь с проблемами масштабирования:
- Запросы к БД замедляются по мере роста объёма данных;
- API, отлично работавшее с сотнями пользователей, начинает тормозить при тысячах;
- Время сборки увеличивается по мере расширения кодовой базы.
Это естественные проблемы роста, с которыми сталкивается каждая успешная система.

1. Вертикальное масштабирование
Это предоставление приложению большего количества ресурсов на одной машине. Самая простая стратегия масштабирования и часто самый эффективный первый шаг. Прежде чем погружаться в сложные распределённые системы, подумайте, может ли модернизация существующей инфраструктуры решить проблемы с производительностью.

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

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

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

За чем следить:

- Расходы растут быстрее, чем пользовательская база;
- Нужна лучшая избыточность и отказоустойчивость;
- Время простоя развёртывания влияет на бизнес-операции;
- Самый большой доступный размер машины приближается к 70% использования.

2. Горизонтальное масштабирование
Запускает несколько экземпляров приложения за балансировщиком нагрузки. Обеспечивает улучшенную отказоустойчивость и почти линейные возможности масштабирования.

Ключ к успешному горизонтальному масштабированию в дизайне приложения. Оно не должно сохранять состояние — каждый запрос должен содержать всю информацию, необходимую для его обработки. Это означает:
- Аутентификация через токены (например, JWT), а не сессии на сервере;
- Распределённое кэширование.

Балансировщик нагрузки играет решающую роль в этой архитектуре. Его задача — распределять трафик по экземплярам приложения. Популярные варианты включают:
- nginx: мощный, с открытым исходным кодом, отлично подходит для пользовательских конфигураций;
- YARP: обратный прокси от Microsoft, отлично подходит для приложений .NET;
- Облако: AWS ALB, Azure Application Gateway, Google Cloud Load Balancing

Преимущества
- Лучшая отказоустойчивость;
- Возможность обрабатывать больше пользователей одновременно;
- Последовательные развёртывания с нулевым временем простоя;
- Экономически эффективное масштабирование (отключение ненужных экземпляров при низком трафике).

Недостатки
В основном не технические, а архитектурные. Приложение должно быть спроектировано для горизонтального масштабирования с самого начала. Преобразование приложения с сохранением состояния в приложение без него часто требует значительного рефакторинга.

Продолжение следует…

Источник:
https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍16👎1
День 2185. #ЗаметкиНаПолях
Масштабируем Монолиты. Продолжение

Начало

3. Масштабирование БД
Производительность БД - то, где большинство монолитов впервые сталкиваются с реальными ограничениями. Рассмотрим подробно стратегии масштабирования.

Реплики чтения
Часто это первый шаг в масштабировании БД. Создаются копии основной БД, которые обслуживают трафик только для чтения. Каждая реплика поддерживает актуальную копию данных посредством репликаций. Изменения передаются в одном направлении: от основной БД к репликам. Т.е. любые данные, записанные в основную БД, в итоге появятся в репликах. «В итоге», т.е. с задержкой. Так вы жертвуете согласованностью данных ради лучшей производительности чтения.
Большинство поставщиков облачных услуг поддерживают реплики чтения с минимальной настройкой, выполняя репликацию, мониторинг и следя за отказоустойчивостью за вас.

Важно:
- Задержка репликации влияет на актуальность данных;
- Объём записи влияет на скорость репликации;
- Географическое местоположение влияет на задержку;
- Каждая реплика увеличивает ваши расходы.

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

Сценарии использования:
- Сложные аналитические запросы;
- Данные, которые обновляются по расписанию;
- Агрегации и сводки;
- Денормализация данных для определённых представлений.

Ключевой компромисс — актуальность против производительности. Нужно решить, как часто обновлять материализованные представления. Слишком часто — нагрузка на БД, слишком редко — данные устаревают.

Шардирование БД
Разделяет данные между несколькими экземплярами БД, причём каждый содержит отдельное подмножество данных.

Шардирование по диапазону разделяет данные на основе диапазонов значений ключа. Например, клиенты А-М – в раздел 1, Н-Я — в раздел 2. Подход хорошо работает с данными, имеющими естественное распределение диапазонов: даты или алфавитный порядок, - но может привести к появлению точек перегрузки, если определённые диапазоны будут запрашиваться чаще, чем другие.

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

Шардирование по клиенту предоставляет каждому клиенту собственную БД. Подход обеспечивает естественную изоляцию и упрощает операции, специфичные для клиента. Хотя он усложняет запросы между клиентами, он часто является самым чистым решением для мультитенантных систем, где важна изоляция данных.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍16
День 2186. #ЗаметкиНаПолях
Масштабируем Монолиты. Окончание

Начало
Продолжение

4. Кэширование
Один из самых эффективных способов повышения производительности системы. Правильно реализованная стратегия кэширования может значительно снизить нагрузку на БД и улучшить время отклика за счёт хранения часто используемых данных в памяти.

Современное кэширование происходит на нескольких уровнях:
- Кэширование в браузере сокращает ненужные сетевые запросы;
- CDN-кэширование приближает контент к пользователям;
- Кэширование на уровне приложений с помощью таких инструментов, как Redis, сохраняет часто используемые данные в памяти;
- Кэширование запросов к БД сокращает дорогостоящие вычисления.

Ключ к эффективному кэшированию — понимание ваших паттернов доступа к данным. Часто читаемые, редко изменяемые данные получат наибольшую выгоду от кэширования. Такие инструменты, как Redis, Memcached или гибридный кэш, отлично справляются с хранением таких данных в памяти, обеспечивая время доступа менее миллисекунды. Поставщики облачных услуг предлагают управляемые сервисы кэширования, такие как Azure Cache для Redis, которые справляются с эксплуатационной сложностью обслуживания распределённого кэша.

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

Очереди сообщений преобразуют поведение системы под нагрузкой. Вместо того чтобы обрабатывать всё синхронно, вы можете ставить работу в очередь на потом. Этот шаблон особенно хорошо работает для таких операций, как:
- обработка загруженных файлов,
- отправка писем и уведомлений,
- создание отчётов,
- обновление поисковых индексов,
- выполнение пакетных операций.

Настоящая сила очередей сообщений заключается в их способности справляться с пиками трафика. Когда система сталкивается с всплеском запросов, очереди действуют как буфер. Они позволяют вам принимать работу с пиковой скоростью, но обрабатывать её в равномерном темпе.

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

Практический путь масштабирования:
1. Оптимизируйте код и запросы к БД;
2. Добавьте кэширование там, где это наиболее важно;
3. Масштабируйте вертикально, пока это экономически эффективно;
4. Переходите к горизонтальному масштабированию для лучшей отказоустойчивости;
5. Внедряйте очереди сообщений для фоновой работы;
6. Рассмотрите возможность шардирования БД, когда этого требует размер данных.

Хорошо спроектированный монолит может выдерживать значительную нагрузку, используя лишь часть этих методов. Главное — понять фактические узкие места вашей системы и устранить их конкретно. Не позволяйте лучшему быть врагом хорошего. Начните с самого простого решения, которое может работать, измерьте всё и масштабируйте то, что необходимо.

Источник: https://www.milanjovanovic.tech/blog/scaling-monoliths-a-practical-guide-for-growing-systems
👍11
День 2187. #ЗаметкиНаПолях
Используем Roslyn для Анализа и Изменения Кода Решения
Roslyn можно использовать как библиотеку для анализа и генерации кода. Например, вы можете создать консольное приложение, которое загружает решение, находит шаблоны и переписывает код.

Создадим консольное приложение и добавим необходимые NuGet-пакеты:
dotnet new console
dotnet add package Microsoft.Build.Locator
dotnet add package Microsoft.CodeAnalysis.CSharp
dotnet add package Microsoft.CodeAnalysis.CSharp.Workspaces
dotnet add package Microsoft.CodeAnalysis.Workspaces.Common
dotnet add package Microsoft.CodeAnalysis.Workspaces.MSBuild


Вот пример, как создать рабочее пространство Roslyn из решения:
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;

// Находим сборки MSBuild в вашей системе
Microsoft.Build.Locator.MSBuildLocator.RegisterDefaults();

// Возможно надо восстановить NuGet-пакеты решения перед тем, как открыть его в Roslyn.
// Зависимости могут потребоваться для корректного анализа.

// Создаём рабочее пространство Roslyn и загружаем решение
var ws = MSBuildWorkspace.Create();
var solution =
await ws.OpenSolutionAsync(@"my_solution.sln");


Теперь можно работать с решением. Вот несколько примеров.

Анализируем синтаксические деревья:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var syntree = await doc.GetSyntaxTreeAsync();
var semanticModel = await doc.GetSemanticModelAsync();
// Анализируем дерево
// Можно использовать CSharpSyntaxWalker для обхода
}
}


Получаем все ссылки на символ:
var comp = 
await solution.Projects.First().GetCompilationAsync();
var symbol =
comp.GetSpecialType(SpecialType.System_String);
var refs =
await SymbolFinder.FindReferencesAsync(symbol, solution);


Переименовываем символ
var symbol = comp
.GetSymbolsWithName(s => s == "MyMethod")
.Single();
var newSolution = await Renamer
.RenameSymbolAsync(
solution,
symbol,
new SymbolRenameOptions()
{
RenameOverloads = true
},
"MyMethod2");
ws.TryApplyChanges(newSolution);


Обновляем документ с помощью DocumentEditor
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
var editor =
await DocumentEditor.CreateAsync(doc);

// Изменяем
foreach(var emptyStatement in
editor
.OriginalRoot
.DescendantNodes()
.OfType<EmptyStatementSyntax>())
{
editor.RemoveNode(emptyStatement);
}

// Применяем изменения
var newDoc = editor.GetChangedDocument();
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}


Обновляем документ с помощью CSharpSyntaxRewriter:
foreach (var proj in solution.Projects)
{
foreach (var doc in proj.Documents)
{
// Изменяем
var root = await doc.GetSyntaxRootAsync();
if(root is null)
continue;

var newRoot =
new CustomRewriter().Visit(root);
var newDoc =
doc.WithSyntaxRoot(newRoot);

// Применяем изменения
if (!ws.TryApplyChanges(newDoc.Project.Solution))
{
Console.WriteLine("Failed!");
}
}
}

internal sealed class CustomRewriter :
CSharpSyntaxRewriter
{
public override SyntaxNode?
VisitIfStatement(IfStatementSyntax node)
=> node.WithLeadingTrivia(
SyntaxFactory
.ParseLeadingTrivia("/* comment */"));
}


Выберите любое «подопытное» решение, введите его путь вместо my_solution.sln, вставьте любой из примеров и можете пройти его в отладчике пошагово, чтобы посмотреть, как работает анализатор.

Источник: https://www.meziantou.net/using-roslyn-to-analyze-and-rewrite-code-in-a-solution.htm
👍19
День 2188. #УрокиРазработки
Уроки 50 Лет Разработки ПО


Урок 40. Неформальные подходы, используемые небольшими сплоченными командами, плохо масштабируются

Гейтс и Аллен, Джобс и Возняк, Хьюлетт и Паккард. Скорее всего, ни у кого из них не было письменных инструкций для их ранних проектов. Двум гениям, работающим бок о бок, не нужна документация. Но это трудно масштабировать во времени и пространстве, когда этих разумов сотни и при этом они думают на разных языках и погружены в разные культуры. Большие задачи требуют соответствующих подходов.

Процессы и инструменты
По мере роста команд, проектов и организаций возникает потребность в совершенствовании процессов. Люди могут осознать необходимость в этом, только столкнувшись с проблемой. Может случиться, что две команды выполнят одну и ту же работу или какая-то задача будет пропущена, поскольку все думали, что её сделает кто-то другой. Эти проблемы требуют более чёткого планирования, координации и коммуникации между членами команды и группами. По мере вовлечения в работу все большего количества людей, особенно если они находятся в разных местах, появляются все новые источники разногласия и задержек.

Более крупные проекты требуют письменного оформления большего объёма информации, чтобы другие участники знали, что происходит и что ждёт впереди. Сохранение требований, решений, бизнес-правил, оценок и метрик в групповом хранилище начинает приносить всё большую пользу. Чем дальше друг от друга члены команды, тем больше внимания нужно уделять организации процесса и тем больше люди вынуждены полагаться на инструменты удалённой совместной работы.

Командам, расположенным в разных местах, нужны инструменты управления задачами, моделирования, тестирования, непрерывной интеграции и особенно управления кодом. ПО с открытым кодом — яркий пример распределённой разработки. Как отмечает Эндрю Леонард: «Ни одно нововведение не было столь же важным для разработки ПО распределенными командами, как появление систем управления версиями.»

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

Нереально ожидать, что каждый член команды будет в полной мере владеть всеми этими ролями. По мере роста проектов и команд специализация некоторых навыков становится естественной и полезной. В какой-то момент даже Билл Гейтс и Пол Аллен решили, что им нужно нанять людей, обладающих навыками, отличными от программирования, чтобы обеспечить рост своей молодой компании: технического писателя, математика, руководителя проекта и т.д. Я слышал, у них это сработало.

Коммуникационные конфликты
По мере увеличения группы количество коммуникационных каналов растёт экспоненциально. Участникам проекта нужны механизмы, позволяющие быстро и точно обмениваться информацией. При этом люди инстинктивно выбирают способы общения, наиболее удобные для них, но не удобные для другой стороны. В проектах с участием нескольких команд важно заранее заложить основу для общения, используя те способы, которые будут эффективно работать для всех. Кроме того, команды могут по-разному принимать решения. Участникам следует обсудить, как будут приниматься совместные решения до того, как все столкнутся с первой серьезной проблемой.

Успешные организации растут и берутся за всё более сложные задачи. Не стоит ожидать, что методы, эффективные для нескольких человек в гараже, подойдут и для больших распределённых команд. Они могут избежать некоторых трудностей, заранее предусмотрев процессы и инструменты, необходимые для интеграции различных команд. Если участники будут иметь возможность общаться и понимать коллег, работающих удалённо, на ранних этапах проекта, это поможет избежать культурных конфликтов. Потратив время на то, чтобы заложить эти основы, можно предотвратить много неприятностей в будущем.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍5👎3
День 2189. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Начало
Рассмотрим, что было бы, если бы можно было использовать JavaScript для вызова конечной точки из другого источника.

См. определение источника тут.

Ниже показан простой веб-API https://localhost:3000 с конечными точками Get() и Post():
using Microsoft.AspNetCore.Mvc;
namespace MyApp.Controllers;

[Route("api/[controller]")]
[ApiController]
public class CorsController : ControllerBase
{
// GET: api/Cors
[HttpGet]
public int Get()
{
return new Random().Next(10, 100);
}
// POST api/Cors
[HttpPost]
public void Post([FromBody] int value)
{
}
}

Get() возвращает случайное число между 10 и 99, а Post() ничего не делает.

В другом проекте, в другом источнике https://localhost:5000, для вызова этого API страница Razor использует JavaScript:
@page
<div class="text-center">
<h1 class="display-4">CORS</h1>
<div id="output"></div>
</div>
@section Scripts {
<script>
fetch("https://localhost:3000/api/Cors")
.then(response => response.json())
.then(data => {
document
.getElementById("output")
.textContent = data;
});
</script>
}

Код JavaScript использует метод fetch() для вызова API на другом сервере, а затем выводит результаты. Однако выполнение кода приводит тому, что случайное число не отображается, а на вкладке Сеть в инструментах разработчика браузера вызов API выделен красным цветом.

Запрос был отправлен, но JS не получил никаких данных. Причина: правило ограничения домена (CORS). Если бы этот запрос работал, злоумышленник мог бы использовать JS для отправки HTTP-запроса на страницу, чтобы, например, добавить товар в корзину – автоматически с использованием файла cookie идентификатора сессии пользователя (см. также Безопасность Cookie). В рамках анализа ответа злоумышленник мог бы извлечь antiforgery-токен из формы и, следовательно, обойти защиту от межсайтовой подделки запросов.

И хотя данный вид атаки ещё не был изобретен, когда появилось правило ограничения домена, поскольку JS не может получить доступ к HTTP-ответу (спасибо правилу ограничения домена), атака не сработает.

Продолжение следует…

Источник: Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
👍16
День 2190. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Продолжение

Начало

Но что, если у JavaScript кода приложения есть законный интерес к вызову API? Если внимательно посмотреть на HTTP-запрос, который был сгенерирован, когда мы попытались вызвать API из JavaScript, мы заметим кое-что любопытное. Заголовок Origin не отправляется с каждым запросом, но браузер автоматически добавляет его при кросс-доменном запросе:
GET /api/Cors HTTP/2
Host: localhost:3000

Origin: https://localhost:5000

Заголовок содержит источник – протокол, домен, порт – вызывающей страницы. Тогда сервер может взять эту информацию и решить, может ли код JavaScript иметь доступ к данным, поступающим с сервера. Если да, то HTTP-ответ должен включать заголовок Access-Control-Allow-Origin и в качестве значения для него должен использовать отправленный источник (или заполнитель *, но, как всегда, лучше быть как можно более явным):
Access-Control-Allow-Origin: https://localhost:5000

Этот механизм называется совместное использование ресурсов между источниками (CORS), и ASP.NET Core поддерживает его – нет необходимости реализовывать его вручную. В диспетчере пакетов NuGet по запросу «CORS» выдаётся длинный список результатов. Но все они предназначены для .NET Standard и .NET Framework. ASP.NET Core автоматически использует собственную версию пакета Microsoft.AspNetCore.Cors. CORS поставляется как промежуточное ПО в ASP.NET Core, поэтому нужно сделать его доступным и активировать. В классе Program требуется:
- добавить поддержку CORS (и настроить её), с помощью AddCors();
- активировать CORS - UseCors().

При вызове AddCors() можно указать параметры. Обычно нужно настроить хотя бы одну политику CORS (т.е. определить разрешённые источники):
builder.Services.AddCors(opts =>
{
opts.AddPolicy("CORS API Endpoint",
bldr =>
{
bldr.WithOrigins("https://localhost:5000");
});
});

Важно:
- Используйте источник клиента API, а не URL самого API!
- Источник должен состоять только из протокола, домена и порта, без слеша в конце.

Можно использовать произвольное количество политик, и у каждой может быть любое количество источников. Переменная bldr (типа CorsPolicyBuilder) также предоставляет методы, чтобы явно разрешить другие фрагменты информации для кросс-доменного запроса с использованием JS:
- WithExposedHeaders() – список заголовков HTTP-ответа, которые возвращаются и становятся доступными для JS;
- WithHeaders() – список разрешённых дополнительных заголовков HTTP-запросов, к которым клиент хотел бы получить доступ;
- WithMethods() – список разрешённых дополнительных HTTP-методов.
- AllowAnyHeader() – разрешать все заголовки;
- AllowAnyMethod() – разрешать все методы.

По умолчанию никакие учётные данные, например заголовки Authorization, не отправляются вместе с кросс-доменным запросом. Опять же, причина
тому – защита от межсайтовой подделки запросов. Если вы хотите, чтобы эта информация была доступна для API, метод AllowCredentials() сообщает клиенту, что её можно отправлять, а DisallowCredentials() - явно запрещает.

Наконец, можно использовать политику по умолчанию:
options.AddDefaultPolicy(
bldr =>
{
bldr.WithOrigins("https://localhost:5000");
});


Окончание следует…

Источник: Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
👍16
День 2191. #Безопасность
CORS. Использование Ресурсов Между Разными Источниками. Окончание

Начало
Продолжение

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

1. Глобально
Нужна политика по умолчанию, затем нужно добавить вызов метода UseCors(). Важен порядок. CORS необходимо активировать:
- после маршрутизации (UseRouting()),
- перед кешированием ответов, аутентификацией и авторизацией (UseResponseCaching(), UseAuthentication(), UseAuthorization()).

Для политики по умолчанию достаточно вызвать:
app.UseCors();


2. Для конечных точек
Для именованных политик также нужен вызов UseCors() в Program.cs, но т.к. нет политики по умолчанию, затем необходимо добавить атрибут [EnableCors] с именем политики в класс контроллера (для всех методов) или только в отдельные методы:
[Route("api/[controller]")]
[EnableCors("CORS API Endpoint")]
[ApiController]
public class CorsController : ControllerBase
{

}

Теперь все методы в контроллере API отправляют правильный заголовок Access-Control-Allow-Origin. При этом атрибут [DisableCors] отключит политику для отдельных методов контроллера. Применив эти изменения к API и снова вызвав страницу из изначального примера, мы получаем желаемый результат. На этот раз не только проходит HTTP-запрос, но и код JS может получить доступ к возвращаемому значению и отобразить его.

3. Для маршрутов
Применяе именованные политики к маршрутам. В ASP.NET Core это настраивается в методе app.UseEndpoints() с помощью расширения RequireCors() после вызова MapControllers(), MapControllerRoute(), MapGet() или аналогичных:
app.UseEndpoints(eps =>
{
eps.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}")
.RequireCors("CORS API Endpoint");
});


CORS на стероидах: предварительные запросы
GET-запрос из нашего примера всё равно выполняется, даже если не соответствует политике (т.к. считается, что GET-запросы не изменяют состояния приложения), но код JS не получает доступа к возвращаемым данным. POST (и другие) запросы могут изменить состояние приложения, поэтому их нельзя просто отправить. В таких случаях браузеры отправляют предварительные запросы (preflight request). Сначала - запрос OPTIONS. Он также не должен ничего изменить на сервере. Если ответ содержит заголовок Access-Control-Allow-Origin с соответствующим значением, и ответ положительный, на сервер будет отправлен ещё один запрос, чтобы получить ресурсы.

Поскольку REST API не имеют состояния, сервер должен проверять все запросы. Но мы можем указать (через заголовок Access-Control-Max-Age), что предварительный запрос не обязательно делать каждый раз. В .NET это можно легко настроить внутри метода AddCors:
builder.Services.AddCors(opts =>
{
opts.AddPolicy("CORS API Endpoint",
bldr =>
{
bldr.

.SetPreflightMaxAge(TimeSpan.FromMinutes(10));
});
});

SetPreflightMaxAge даст указание клиенту кэшировать предварительный запрос CORS на 10 минут (т.е. он отправляет заголовок Access-Control-Max-Age со значением 600).

Источники:
- Кристиан Венц “Безопасность
ASP.NET Core”. М.: ДМК Пресс, 2023. Глава 4.
-
https://steven-giesel.com/blogPost/5fc7cb62-d5ad-4f07-831c-d6c1c6974641/cache-cors-preflight-requests
👍13
День 2192. #Оффтоп
Когда Прекратится Поддержка .NET Framework?

Вы когда-нибудь задумывались, когда мы наконец избавимся от старого доброго .NET Framework? Короткий ответ: непонятно. Майкрософт обещают продолжать его поддерживать, но это ведь не может продолжаться бесконечно.

Если посмотреть официальную страницу, вы увидите табличку на картинке выше.

Итак, все версии выше .NET Framework 4.7 по-прежнему поддерживается (и даже у 4.6.2 всё ещё 2 года в запасе). Примечание: эти версии практически не имеют конфликтов, поэтому обновление с 4.6.2 до 4.8.1 не должно вызвать никаких проблем.

В любом случае, одним из основных факторов долговечности фреймворка является то, что он связан с Windows. Windows Server 2022 поставляется с .NET 4.8.1. Дата окончания поддержки Windows Server 2022 — октябрь 2031 года. И даже Windows Server 2025 (последняя версия) содержит фреймворк, а её расширенная поддержка заканчивается ещё через 3 года — в 2034 году.

Поэтому весьма вероятно, что .NET Framework 4.8.x будет существовать плюс-минус до этого момента. Возможно, будут выпущены более новые версии с исправлениями из-за проблем с безопасностью (например, SHA-2 устареет или в нём обнаружится уязвимость, и людям придётся перейти на что-то другое).

Конечно, для новой разработки следует использовать .NET 8 и более поздние версии, если возможно. Однако очень маловероятно, что .NET Framework перестанет работать в ближайшее время. Хорошо это или нет, решать вам.

Источник: https://steven-giesel.com/blogPost/6de02565-a08e-4968-b610-22826582c1f1/when-will-net-framework-retire
👍15
День 2193.
Каналу 6 лет!

Минул ещё один год. Вас набралось почти 6000. Спасибо, что читаете, лайкаете и комментируете.

Для тех, кто пришёл недавно, вот здесь подборка тематических тегов. Кроме того, вы просто можете поискать по сообщениям в канале то, что вас интересует. Это, конечно, не гугл, но всё равно за 6 лет набралось довольно много полезной информации.

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

Ещё раз спасибо, что читаете, а те, кто кроме этого хочет поддержать, добро пожаловать в Boosty или Patreon. А ещё монетки в телеге появились, если кому так удобнее)))
3👍70👎1
День 2194. #UX
UX Кодов Доступа

Сегодня поговорим на немного отвлечённую тему. Все мы много раз вводили одноразовый код для входа в систему или подтверждения какой-то операции. Дизайн форм для ввода кода везде разный, каждый изощряется, как может. Если ваш продукт отправляет СМС/e-mail с кодами, то пользовательский опыт должен быть максимально хорошим.

Плохо
СМС/e-mail просто не приходит. Пользователь оказывается в подвешенном состоянии и в итоге вынужден искать ссылку «Не получили код? Отправить повторно», а то и ждать по 30-60 секунд перед повторной отправкой. Потом, конечно, после запроса повторной отправки приходит первый код, который уже не работает.

Сообщения вокруг кода длинные или странные. Иногда код приходит в длинном сообщении, где говорится о спаме/фишинге/безопасности, что в итоге делает весь процесс подозрительным. Куча вариантов испортить жизнь пользователю: неясная тема письма, код, зарытый в текст, код, стилистически не отличающийся от остальной части сообщения, код в конце длинного текста, так, что он не показывается в сокращённом уведомлении телефона и приходится его открывать полностью и т.п.

Странная форма. Мне не нравятся формы, где каждая цифра — отдельное поле. Всё должно быть понятно и просто, но тут это не так. Можно ли вставить скопированный код? Куда его вставлять? Перенесутся ли при вставке цифры в отдельные ячейки или вставится только первая? Часто в коде есть дефис он вставится? И т.п.

Код странный. Цифры – хорошо. Буквенно-цифровой код - пойдёт. 12 знаков, включая специальные символы, – зачем?

Запрет копирования/вставки кода. Зачем так делают? Вынуждать пользователя переключаться туда-сюда между приложениями, пытаясь удержать в голове случайные цифры – это издевательство!

Лучше
Код приходит быстро. Не нужно сидеть у почтового ящика, постоянно его обновляя.

Сообщение ясное и краткое. Вероятность того, что я не буду читать этот текст, составляет 99%, поэтому не пишите длинные тексты (ещё лучше – уберите текст вообще!)

Код выделен. Мой разум настроен на «где этот чёртов код?», поэтому выделение кода — это здорово. В Android есть удобная кнопка «Скопировать 434345», которая значительно упрощает процесс. В e-mail используйте форматирование и другие базовые методы дизайна, чтобы выделить код.

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

Отлично
Лучше всего, если этого кошмарного процесса вообще не существует. Я знаю, знаю, безопасность и вот это всё, но это лишние сложности и боль, как смерть от 1000 порезов бумагой. Это похоже на наказание.

Существуют и другие методы двухфакторной аутентификации, но каждый метод приносит свою боль. Приложения для аутентификации только добавляют стресса. А от того, что методы аутентификации в разных приложениях разные: смс/пуш/e-mail/приложение аутентификации, - вообще голова кругом идёт. Я ловлю себя на том, что нажимаю случайные кнопки, в метаниях между Chrome, e-mail клиентом и телефоном, не совсем понимая, что происходит, но в конце концов я нажимаю достаточно, чтобы меня впустили. И не дай бог вернуться спустя годы к какому-то приложению, где был зарегистрирован давно заброшенный e-mail!

В любом случае, между отличным UX и отличной безопасностью есть реальное противоречие, и понятно, что существует множество трудностей и компромиссов, необходимых для достижения баланса. Но, кажется, с этим надо что-то делать. Как минимум, если уж заставлять пользователей проходить через это, то упростить процесс максимально.

Источник: https://bradfrost.com/blog/post/the-ux-of-login-codes/
👍12
День 2195. #УрокиРазработки
Уроки 50 Лет Разработки ПО

Урок 41. Не стоит недооценивать сложность изменения культуры организации при переходе к новым методам работы


Глупо думать, что стоит просто описать новые процедуры работы в крупной компании – и всё получится само собой. Нужно объявить и объяснить политики, как они должны применяться, определить пути перехода к новым методам работы и спрашивать с людей за несоблюдение правил. Вы не можете просто отправить всех на тренинг, а затем ожидать, что люди начнут эффективно работать по-новому. Это только сбивает с толку, вызывает напряжение и подрывает доверие к руководству.

Ценности, модели поведения и практика
Здоровая культура разработки ПО подразумевает общие ценности, модели поведения и технические приёмы. Вовлечение членов команды в процесс совершенствования повышает их заинтересованность. Лидеры изменений должны объяснить:
- почему изменения необходимы;
- какие проблемы они решат;
- каких результатов организация надеется достичь;
- как изменения повлияют на организационную структуру;
- любые новые роли, должности или обязанности;
- сроки реализации;
- ответственность за конструктивный вклад в инициативу.

Меняться тяжело. Не все хотят этого, и вы не сможете развивать культуру, если люди активно сопротивляются, не желая покидать свою зону комфорта. Но нельзя позволять сопротивляющимся сдерживать всю инициативу. Старайтесь найти баланс между навязыванием нового образа жизни в приказном порядке и попытками сделать всех счастливыми. Преданные делу лидеры действуют и подают пример: ставят конкретные цели, предоставляют ресурсы и демонстрируют поведение, соответствующее намеченному результату. Руководители, стремящиеся к устойчивым изменениям, умеют формировать их так, чтобы они вызывали положительные эмоции и стимулировали действия. Общие фразы, предписания и директивы не помогут.

Направлять членов команды к новым ценностям и поведению означает, что их требуется каждодневно убеждать. У людей в организации есть три варианта: 1) помочь возглавить инициативу, 2) последовать ей и внести посильный вклад или 3) уйти с дороги. Если изменения заставляют кого-то чувствовать себя слишком некомфортно, эти люди, скорее всего, уйдут, но это лучший выход для всех. Хуже, если на словах они поддержат развитие, но будут игнорировать или подрывать его при каждой возможности.

Agile-разработка и изменение культуры
Переход на Agile — серьёзное изменение в работе организации. Многие аспекты культуры должны измениться, в том числе:
- организационная структура и состав команды;
- терминология;
- ценности и принципы;
- роли в проекте;
- методы составления графиков и планов;
- характер сотрудничества и общения;
- оценка прогресса;
- ответственность за результаты.

Переход к Agile предполагает резкое изменение мышления и практик. Простого обучения недостаточно, чтобы добиться приверженности новым ценностям, изменить индивидуальное поведение и принять новые методы. Людям нужно время, чтобы забыть прошлые навыки и освоиться с новыми способами работы.

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

Фундаментальный переход от устоявшихся методов работы к чему-то совершенно иному требует изменения множества аспектов: организационных, управленческих, лидерства в проектах, практики вознаграждения, технических приёмов, индивидуальных привычек и поведения команды. Высшее руководство должно быть привержено изменениям и убедительно доказывать их необходимость всем, кого они касаются. Культурный переход — более сложная задача, чем установка новых приёмов и методов; не решив её, вы почти наверняка потерпите неудачу.

Источник: Карл Вигерс “Жемчужины Разработки”. СПб.: Питер, 2024. Глава 5.
👍4
День 2196. #ЗаметкиНаПолях #Microservices
Отказоустойчивые HTTP-запросы в .NET. Начало
Не всегда всё идет по плану: запросы по сети рандомно завершаются неудачей, серверы приложений перегружаются, и возникают неожиданные ошибки. Отказоустойчивые приложения могут восстанавливаться после временных сбоев и продолжать функционировать. Устойчивость достигается путём проектирования приложений, которые могут корректно обрабатывать сбои и быстро восстанавливаться. Рассмотрим инструменты и методы, которые есть в .NET для создания отказоустойчивых систем.

Зачем?
Отправка HTTP-запросов — распространённый подход к связи между удалёнными сервисами. Но они подвержены сбоям из-за проблем с сетью или сервером. Эти сбои могут нарушить доступность сервисов, особенно по мере увеличения зависимостей и риска каскадных сбоев. Вот несколько стратегий повышения устойчивости:
- Повторные попытки - повторить запрос, который завершился неудачей из-за временной ошибки.
- Тайм-ауты - отмена запросов, которые превышают указанный лимит времени.
- Хэджирование и откаты - альтернативные действия или результаты для неудавшихся операций.
- Аварийное отключение - временное прекращение связи с недоступными сервисами.

Эти стратегии можно использовать по отдельности или в сочетании для оптимальной устойчивости HTTP-запросов.

Конвейеры устойчивости
Начиная с .NET 8 интеграция отказоустойчивости в приложения стала намного проще. Можно использовать Microsoft.Extensions.Resilience и Microsoft.Extensions.Http.Resilience, которые построены на основе Polly. Polly — библиотека отказоустойчивости и обработки временных сбоев в .NET. Она позволяет определять стратегии обеспечения устойчивости, описанные выше. Polly получила новый API в последней версии (V8), который был реализован в сотрудничестве с Microsoft. Если вы ранее использовали Microsoft.Extensions.Http.Polly, теперь рекомендуется использовать следующие пакеты:
Install-Package Microsoft.Extensions.Resilience
Install-Package Microsoft.Extensions.Http.Resilience


Cначала создадим конвейер, состоящий из стратегий отказоустойчивости. Каждая стратегия, которую мы настраиваем как часть конвейера, будет выполняться в порядке конфигурации. Порядок важен! Создадим конвейер с помощью построителя:
ResiliencePipeline pipeline = 
new ResiliencePipelineBuilder()
.AddRetry(new RetryStrategyOptions
{
ShouldHandle = new PredicateBuilder()
.Handle<ConflictException>(),
Delay = TimeSpan.FromSeconds(1),
MaxRetryAttempts = 2,
BackoffType = DelayBackoffType.Exponential,
UseJitter = true
})
.AddTimeout(new TimeoutStrategyOptions
{
Timeout = TimeSpan.FromSeconds(10)
})
.Build();

await pipeline.ExecuteAsync(
async ct => await httpClient
.GetAsync("https://google.com", ct),
cancellationToken);

Вот что мы добавляем в конвейер отказоустойчивости:
- AddRetry — настраивает стратегию повторных попыток, которую мы можем дополнительно настроить, передав экземпляр RetryStrategyOptions. Здесь вы предоставляем предикат для свойства ShouldHandle, чтобы определить, какие исключения (ConflictException) должна обрабатывать стратегия. Также мы указываем максимальное количество попыток повтора и настраиваем время ожидания до следующего повтора. В данном случае оно будет экспоненциально расти (DelayBackoffType.Exponential) и в разных случаях будет немного варьироваться (UseJitter). См. подробнее про настройки стратегии повтора.
- AddTimeout — настраивает стратегию тайм-аута, которая выдаст TimeoutRejectedException, если делегат не завершится до истечения времени ожидания. Мы можем передать нужное ожидания в экземпляр TimeoutStrategyOptions. Время ожидания по умолчанию - 30 секунд.

В конце мы строим конвейер, и используем настроенный экземпляр для HTTP-запроса в методе ExecuteAsync.

Продолжение следует…

Источник:
https://www.milanjovanovic.tech/blog/building-resilient-cloud-applications-with-dotnet
👍30
День 2197. #ЗаметкиНаПолях #Microservices
Отказоустойчивые HTTP-запросы в .NET. Продолжение

Начало

Внедрение зависимостей
.NET 8 представляет новый метод расширения AddResiliencePipeline для интерфейса IServiceCollection, который позволяет регистрировать конвейеры отказоустойчивости. Каждый конвейер должен иметь уникальный ключ, который мы можем использовать для разрешения соответствующего экземпляра конвейера:
services.AddResiliencePipeline("retry", builder =>
{
builder.AddRetry(new RetryStrategyOptions
{
Delay = TimeSpan.FromSeconds(1),
MaxRetryAttempts = 2,
BackoffType = DelayBackoffType.Exponential,
UseJitter = true
});
});


Также можно указать обобщённые аргументы, чтобы настроить типизированный конвейер, используя ResiliencePipelineBuilder<TResult>. Так мы можем получить доступ к стратегиям хеджирования и отката.

В следующем примере мы настраиваем стратегию отката, вызывая AddFallback. Это позволяет предоставить «запасное» значение, которое мы можем вернуть в случае сбоя. Оно может быть статическим значением или поступать из другого HTTP-запроса или БД.
services.AddResiliencePipeline<string, GitHubUser?>(
"gh-fallback", builder =>
{
builder.AddFallback(
new FallbackStrategyOptions<GitHubUser?>
{
FallbackAction = _ =>
Outcome.FromResultAsValueTask<GitHubUser?>(
GitHubUser.Empty
)
});
});


Чтобы использовать конвейеры из DI-контейнера, можно использовать метод GetPipeline класса ResiliencePipelineProvider, передав ему ключ:
app.MapGet("users", async (
HttpClient httpClient,
ResiliencePipelineProvider<string> plProv) =>
{
var pipeline =
plProv.GetPipeline<GitHubUser?>("gh-fallback");

var user = await pipeline.ExecuteAsync(async token =>
await httpClient.GetAsync("api/users", token),
cancellationToken);
});


Стратегии отказоустойчивости и Polly
Стратегии устойчивости являются основным компонентом Polly. Они предназначены для запуска пользовательских обратных вызовов, одновременно добавляя отказоустойчивость. Мы не можем запускать стратегии напрямую, только через конвейер. Polly делит стратегии на:
- Реактивные - обрабатывают определённые исключения или результаты.
- Проактивные - решают прервать или отменить выполнение обратных вызовов с помощью стратегий ограничения скорости или тайм-аута.

Polly имеет следующие встроенные стратегии отказоустойчивости:
- Повтор - классический подход «попробуйте ещё раз». Отлично подходит для временных сетевых сбоев.
- Аварийное отключение - предотвращает нагрузку на отказавшую систему. Если ошибки накапливаются, временное отключение даёт системе время на восстановление.
- Откат - обеспечивает безопасный ответ по умолчанию, если основной вызов не удаётся.
- Хеджирование - выполняет несколько запросов одновременно, принимая первый успешный ответ. Полезно, если в системе есть несколько способов обработки чего-либо.
- Тайм-аут - предотвращает вечное зависание запросов, завершая их, если превышено время ожидания.
- Ограничитель скорости - ограничивает исходящие запросы, чтобы предотвратить перегрузку внешних сервисов.

Окончание следует…

Источник:
https://www.milanjovanovic.tech/blog/building-resilient-cloud-applications-with-dotnet
👍15
День 2198. #ЗаметкиНаПолях #Microservices
Отказоустойчивые HTTP-запросы в .NET. Окончание

Начало
Продолжение

Отказоустойчивость HTTP-клиентов
Библиотека Microsoft.Extensions.Http.Resilience поставляется с готовыми к использованию конвейерами отказоустойчивости для отправки HTTP-запросов. Мы можем добавить отказоустойчивость к исходящим запросам HttpClient с помощью метода AddStandardResilienceHandler, в том числе настроив его по умолчанию для всех HTTP-клиентов приложения:
builder.Services
.AddHttpClient()
.ConfigureHttpClientDefaults(
http => http.AddStandardResilienceHandler(o =>
{
o.Retry.Delay = TimeSpan.FromSeconds(1);

}));

В стандартном обработчике отказоустойчивости можно настроить 5 стратегий: ограничение скорости, тайм-аут попытки, общий таймаут запроса, повторы, автоматическое отключение.

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

Создадим метод расширения, который очищает все обработчики из конвейера отказоустойчивости. Это позволит удалить обработчики по умолчанию и добавить свои:
public static class ResilienceExtensions
{
public static IHttpClientBuilder
RemoveAllResilienceHandlers(
this IHttpClientBuilder builder)
{
builder.ConfigureAdditionalHttpMessageHandlers(
static (handlers, _) =>
{
for (int i = handlers.Count - 1; i >= 0; i--)
{
if (handlers[i] is ResilienceHandler)
{
handlers.RemoveAt(i);
}
}
});

return builder;
}
}

Теперь его можно использовать, чтобы удалить стандартную стратегию отказоустойчивости и добавить другую в случаях, когда стандартная не подходит:
builder.Services
.AddHttpClient("github")
.ConfigureHttpClient(cl =>
{
cl.BaseAddress = new Uri("https://api.github.com");
})
.RemoveAllResilienceHandlers()
.AddResilienceHandler("custom", p =>
{
// Настраиваем другую политику
// для Http-клиента GitHub…
});


Итого
Отказоустойчивость — основной принцип создания надёжных программных систем. Имея в нашем распоряжении такие мощные инструменты, как Microsoft.Extensions.Resilience и Polly, мы можем использовать их для проектирования систем, которые грамотно обрабатывают любые временные сбои.

Источник: https://www.milanjovanovic.tech/blog/overriding-default-http-resilience-handlers-in-dotnet
👍18
Честно, не подглядывая, что произойдёт при попытке компиляции и запуска такого кода? #Quiz #CSharp
var var = new();
class var {};
Anonymous Quiz
74%
ошибка компиляции
3%
ошибка времени выполнения
22%
это валидный код C#
2👍24👎22
День 2199.
Сегодня порекомендую вам интервью, которое взял Ник Чапсас у одного из разработчиков языка C#, Мэдса Торгенсена.

Какие новинки готовит нам C#14? Что случилось с типами-расширениями? Почему в первичные конструкторы не добавили readonly параметры? Когда уже появятся дискриминируемые объединения в C#? И вообще, как команда языка определяет, какие новые функции добавлять в язык и как это делать?

А в конце беседы Мэдс рассказывает, какие функции языка он ненавидит и с радостью убрал бы.

Ко вчерашнему квизу про var. В компиляторе огромное количество логики, которая определяет правильность использования ключевых слов. И в этом конкретном случае var в начале объявления переменной будет рассматриваться как тип, если он существует. А второе слово в объявлении будет именем переменной. И такой код будет прекрасно компилироваться и работать. Более того, Мэдс рассказал, что так в отдельных командах ненавистники ключевого слова var запрещали его использование. Просто создавали пустой класс var, и тогда любое использование var в объявлении приводило к созданию экземпляра этого класса. Таким образом, приходилось отказываться от var и явно объявлять тип переменной.

https://youtu.be/T9UqIkuGnuo
👍19