Опытный разработчик не так давно зашёл в .Net и поставил цель получить сертификат Microsoft. Свой ежедневный прогресс он описывает на канале .Net Разработчик. Заметки об изученном материале, советы по повышению производительности и поддержке мотивации, ин
ct, переданный методу GetWithTimeoutAsync, представляет отмену, запрошенную конечным пользователем, а сам метод также применяет тайм-аут к запросу:
async Task<HttpResponseMessage>
GetWithTimeoutAsync(
HttpClient client,
string url,
CancellationToken ct)
{
using var cts =
CancellationTokenSource
.CreateLinkedTokenSource(ct);
cts.CancelAfter(TimeSpan.FromSeconds(2));
var combinedToken = cts.Token;
return await client.GetAsync(url, combinedToken);
}
Полученный токен combinedToken отменяется либо когда пользователь отменяет существующий токен ct, либо при отмене связанного источника вызовом CancelAfter.
Хотя в примере выше используется только один токен ct, метод CreateLinkedTokenSource может получать любое количество токенов отмены в своих параметрах. Это позволяет вам создать один объединённый токен, на базе которого можно реализовать собственную логику отмены.
Например, ASP.NET предоставляет токен отмены, представляющий отключение пользователя. Код обработчика может создать связанный токен, который реагирует либо на отключение пользователя, либо на свои причины отмены (например, тайм-аут).
Помните о сроке существования источника связанного токена отмены. Предыдущий пример является наиболее типичным: один или несколько токенов отмены передаются методу, который связывает их и передаёт как комбинированный токен. Обратите внимание на то, что в примере используется команда using, которая гарантирует, что источник связанного токена отмены будет освобожден, когда операция будет завершена (а комбинированный токен перестанет использоваться). Подумайте, что произойдёт, если код не освободит источник связанного токена отмены: может оказаться, что метод GetWithTimeoutAsync будет вызван несколько раз с одним (долгосрочным) существующим токеном; в этом случае код будет создавать новый источник связанного токена при каждом вызове. Даже после того, как запросы HTTP завершатся (и ничто не будет использовать комбинированный токен), эти связанные источники будут оставаться присоединенными к существующему токену. Чтобы предотвратить подобные утечки памяти, освобождайте источник связанного токена отмены, когда комбинированный токен перестаёт быть нужным.
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 10.+ и * в сложных подвыражениях),
2) внутри этих повторяющихся подвыражений есть дополнительные повторяющиеся символы и выражения, которые соответствуют суффиксу другого совпадения.
Например:
^(a+)+$Подвыражение в скобках ищет повторяющийся символ «a». Оно также проверяет, является ли всё выражение повторением какого-либо из подвыражений! Допустим, злоумышленник вводит такие данные:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaЗдесь каждая дополнительная буква "a" заставит оценщик проверять наличие повторений дополнительного подвыражения, таким образом удваивая количество времени обработки. Иногда приложения позволяют пользователям вводить собственные шаблоны регулярных выражений для выполнения сервером. В этом случае злоумышленник может внедрить вредоносное регулярное выражение по своему выбору, а затем отправить строку ввода, которая заставит злонамеренное регулярное выражение выполняться в течение длительного времени. Другой пример - при регистрации проверка, содержит ли пароль пользователя его имя. Если приложение вслепую использует имя пользователя как шаблон без очистки спецсимволов, например:
var r = new Regex(username, RegexOptions.IgnoreCase); var result = r.Match(password);Тогда злоумышленник может внедрить вредоносное регулярное выражение через поле имени пользователя:
username: ^(a+)+$ password: aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaСпособы борьбы с ReDoS: 1. Многопоточность и упреждающее уничтожение процессов. Многопоточное приложение может одновременно обрабатывать другие запросы, даже если вычисление регулярного выражения занимает слишком много времени. Также нужно останавливать выполнение любой оценки регулярного выражения, если она занимает больше времени, чем ожидалось. 2. Не стоит раскрывать ваши шаблоны регулярных выражений. Иногда они публикуются, потому что проект имеет открытый исходный код, или случайно, т.к. используются одни и те же шаблоны как в серверном, так и в клиентском коде. Это облегчает злоумышленникам поиск вредоносных регулярных выражений и создание соответствующих строк для атаки. 3. Избегайте использования опасных регулярных выражений для оценки пользовательского ввода (см. критерии выше) и вместо этого используйте более простые. Поищите проверенные и защищённые шаблоны вместо того, чтобы писать свои собственные. 4. Исключите возможность для пользователей предоставлять свои шаблоны регулярных выражений оценщику. Если вам нужна такая функциональность, используйте предварительно отформатированные регулярные выражения, которые допускают минимальную настройку. Источник: https://sec.okta.com/articles/2020/04/attacking-evil-regex-understanding-regular-expression-denial-service
dotnet MyApp.dll first second third fourth fifthТеперь проведём настройку хоста в файле program.cs, сначала создавая построитель по умолчанию с аргументами командной строки, а затем настраиваем конфигурацию приложения как коллекцию в памяти, основанную на парах ключ-значение из словаря:
using IHost host = Host
.CreateDefaultBuilder(args)
.ConfigureAppConfiguration((_, conf) =>
conf.AddInMemoryCollection(
new Dictionary<string, string> {
["First"] = args[0],
["Second"] = args[1],
["Third"] = args[2],
["Fourth"] = args[3],
["Fifth"] = args[4]
}))
.ConfigureServices((_, srvc) =>
srvc.AddScoped<TestClass>())
.Build();
Теперь мы можем передать эту конфигурацию как параметр в любой конструктор:
using System.Text;
using Microsoft.Extensions.Configuration;
namespace MyApp;
public class TestClass
{
private readonly string First;
private readonly string Second;
private readonly string Third;
private readonly string Fourth;
private readonly string Fifth;
public TestClass(IConfiguration conf)
{
First = conf["First"];
Second = conf["Second"];
Third = conf["Third"];
Fourth = conf["Fourth"];
Fifth = conf["Fifth"];
}
public void Run()
{
//…
}
}
И далее вызвать класс как сервис:
var test = host.Services.GetService<TestClass>(); test.Run();Этот пример не проверяет список параметров и не предлагает какой-либо гибкости. Здесь мы просто превращаем параметры командной строки в удобную конфигурацию, передаваемую через внедрение зависимостей. Важно отметить, что в нашей конфигурации хоста мы сопоставляем параметры с элементами словаря, а в классе извлекаем настроенные значения, как и в любом другом случае использования интерфейса
IConfiguration.
Полный код примера здесь
Источник: https://dev.to/alfetta159/configuration-using-command-line-parameters-in-net-console-applications-21e4AdditionalRequestHeaders() в W3CLoggerOptions:
services.AddW3CLogging(l =>
{
l.AdditionalRequestHeaders.Add("x-forwarded-for");
l.AdditionalRequestHeaders.Add("x-client-ssl-protocol");
});
7. Пустые шаблоны проектов Blazor
В Blazor появились два новых шаблона проектов для старта «с чистого листа». Новые шаблоны такие же, как их непустые аналоги, но без дополнительного демонстрационного кода, только с очень простой домашней страницей. Также удалён Bootstrap, чтобы можно было использовать любой CSS-фреймворк.
Новые шаблоны доступны в Visual Studio после установки пакета SDK для .NET 7, а также из командной строки:
dotnet new blazorserver-empty dotnet new blazorwasm-empty8. Поддержка System.Security.Cryptography в WebAssembly .NET 6 поддерживал семейство алгоритмов хеширования SHA при работе на WebAssembly. .NET 7 позволяет использовать больше криптографических алгоритмов, используя преимущества SubtleCrypto, когда это возможно, и возвращаясь к реализации .NET, когда SubtleCrypto использовать нельзя. Теперь поддерживаются следующие алгоритмы: - SHA1 - SHA256 - SHA384 - SHA512 - HMACSHA1 - HMACSHA256 - HMACSHA384 - HMACSHA512 Поддержка AES-CBC, PBKDF2 и HKDF запланирована в будущих обновлениях .NET 7. 9. Поддержка пользовательских элементов Blazor больше не является экспериментальной Ранее экспериментальный пакет Microsoft.AspNetCore.Components.CustomElements для создания пользовательских элементов Blazor на основе стандартных больше не является экспериментальным и теперь является частью выпуска .NET 7. Подробности см. в документации о создании пользовательских элементов Blazor. 10. Экспериментальный компонент QuickGrid для Blazor QuickGrid — это новый экспериментальный компонент Blazor для быстрого и эффективного отображения данных в табличной форме. QuickGrid предоставляет простой и удобный компонент сетки данных для наиболее распространённых случаев. Чтобы использовать компонент, добавьте пакет Microsoft.AspNetCore.Components.QuickGrid. Примеры использования можно посмотреть здесь. Источник: https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-6/
Content-Encoding для автоматической идентификации и распаковки запросов со сжатым содержимым, чтобы разработчику серверной части не приходилось заниматься этим самостоятельно.
Это промежуточное ПО добавляется с помощью метода расширения UseRequestDecompression в IApplicationBuilder и метода расширения AddRequestDecompression в IServiceCollection.
Поддерживаются Brotli (br), Deflate (deflate) и GZip (gzip). Другие кодировки можно добавить, зарегистрировав собственный класс поставщика распаковки, который реализует интерфейс IDecompressionProvider вместе со значением заголовка Content-Encoding в RequestDecompressionOptions.
2. Промежуточное ПО для кэширования вывода
Помогает вам сохранять результаты вашего веб-приложения и обслуживать их из кэша, а не вычислять их каждый раз, что повышает производительность и высвобождает ресурсы для других действий.
Используйте метод расширения AddOutputCache в IServiceCollection и метод расширения UseOutputCache в IApplicationBuilder. После можно настроить кэширование на конечных точках:
app.MapGet("/notcached",
() => DateTime.Now.ToString());
app.MapGet("/cached",
() => DateTime.Now.ToString()).CacheOutput();
Запросы к /notcached вернут текущее время. А каждый запрос к /cached после первого будет возвращать кешированный ответ.
Существует множество более продвинутых способов:
- по параметру строки запроса
….CacheOutput(p => p.VaryByQuery("culture"));
- по заголовкам (VaryByHeader)
- по произвольному значению (VaryByValue).
Промежуточное ПО кэширования имеет встроенную защиту от некоторых распространённых ошибок. Например, при инвалидации значения кэша только первый запрос будет вызывать серверный код получения значения. Остальные запросы в это время будут ждать появления значения в кэше, чтобы не перегружать сервер избыточной работой. Вот здесь есть пример использования кэширования.
3. Обновления промежуточного ПО ограничений
Промежуточное ПО ограничений (rate limiting) теперь поддерживает ограничения на определённых конечных точках, которое можно комбинировать с глобальным ограничителем, работающим для всех запросов. Также теперь поддерживается добавление пользовательских политик ограничений с помощью новых методов AddPolicy в RateLimiterOptions.
Подробнее об ограничениях в этом посте (см. пункт 4).
4. Поддержка WebSockets через HTTP/2 в Kestrel
Использование WebSockets через HTTP/2 позволяет использовать преимущества новых функций, таких как сжатие заголовков и мультиплексирование, которые сокращают время и ресурсы, необходимые при выполнении нескольких запросов к серверу. Эта поддержка теперь доступна в Kestrel на всех платформах с поддержкой HTTP/2.
Согласование версии HTTP выполняется автоматически в браузерах и Kestrel. Вы можете использовать существующие примеры для ASP.NET Core.
Примечание: WebSockets в HTTP/2 использует запросы CONNECT, а не GET, поэтому ваши маршруты и контроллеры возможно придётся обновить.
5. Улучшения производительности Kestrel на многоядерных компьютерах
Разделение ConcurrentQueue, используемой в Kestrel, по связанному с ней сокету уменьшает конкуренцию и увеличивает пропускную способность на машинах с большим количеством ядер. В превью 6 пул памяти Kestrel теперь разделён так же, как и очередь ввода-вывода. Наблюдалось более чем 500% улучшение RPS в тестах TechEmpower на 80-ядерных виртуальных машинах ARM64 (доступных теперь в Azure) и почти 100-процентное улучшение на 48-ядерных виртуальных машинах AMD в тесте HTTPS JSON.
Источник: https://devblogs.microsoft.com/dotnet/asp-net-core-updates-in-dotnet-7-preview-6/class MyClass
{
public MyClass(out bool succeeded)
{
// что-то делаем
succeeded = true;
}
}
Живите теперь с этим…
На Stackoverflow есть даже попытка объяснения:
«В некоторых случаях может быть необходимо при неудаче в конструкторе передать информацию о том, какие части операции были успешными, а какие нет. Рассмотрим, например, объект, конструктор которого принимает дескриптор неуправляемого объекта и должен его инкапсулировать. Если конструктор выдаёт исключение, может быть обязательно, чтобы вызывающий объект освобождал неуправляемый объект, если этого не сделал конструктор, и в равной степени обязательно, чтобы вызывающий объект не освобождал объект, если это сделал конструктор. Наличие в конструкторе параметра out или ref может быть наименее опасным способом для класса сообщить вызывающему объекту, что нужно будет очистить, если конструктор завершится ошибкой.
Хотя передавать конструктору параметры ref или out некрасиво, существуют некоторые типы, в которых попытки создать пригодный для использования экземпляр будут иметь побочные эффекты и могут завершиться неудачей после того, как некоторые из этих побочных эффектов уже произошли. Если невозможно создать допустимый объект, вот способы передать информацию вызывающей стороне:
- сохранение в поле ThreadStatic,
- инкапсуляция в выброшенном исключении,
- сохранение или передача в параметр в виде объекта или делегата,
- запись в параметр ref/out.
Из них только параметр ref/out делает очевидным наличие информации, с которой клиентский код должен что-то делать.
Существование параметров ref или out часто является признаком того, что конструктор должен быть помечен как protected, и что внешний код должен проходить через фабричные методы, которые гарантируют, что в случае возникновения исключения переданный объект будет использоваться соответствующим образом. Однако, чтобы класс мог разумно поддерживать наследование, он должен предлагать по крайней мере один конструктор, видимый вне его.»
Источник: https://stackoverflow.com/questions/19633251/constructor-with-output-parameterChannels предоставляет самые простые средства применения выборки к элементам ввода. Типичный пример — всегда брать последние n элементов с потерей самых старых элементов при заполнении очереди:
var queue = Channel.CreateBounded<int>(
new BoundedChannelOptions(1)
{
FullMode = BoundedChannelFullMode.DropOldest,
});
var writer = queue.Writer;
// Операция записи завершается немедленно.
await writer.WriteAsync(7);
// Операция записи тоже завершается немедленно.
// Элемент 7 теряется, если только он не был
// немедленно извлечен потребителем.
await writer.WriteAsync(13);
Это самый простой механизм контроля входных потоков и предотвращения «затопления» потребителей.
Есть и другие режимы BoundedChannelFullMode. Например, если вы хотите, чтобы самые старые элементы сохранялись, можно при заполнении канала терять новые элементы:
var queue = Channel.CreateBounded<int>(
new BoundedChannelOptions(1)
{
FullMode = BoundedChannelFullMode.DropWrite,
});
var writer = queue.Writer;
// Операция записи завершается немедленно
await writer.WriteAsync(7);
// Операция записи тоже завершается немедленно
// Элемент 13 теряется, если только элемент 7 не был
// немедленно извлечен потребителем.
await writer.WriteAsync(13);
Пояснение
Библиотека Channels отлично подходит для простой выборки. Во многих ситуациях полезен режим BoundedChannelFullMode.DropOldest. Более сложная выборка должна выполняться самими потребителями.
Если выборка должна выполняться по времени (например, «только 10 элементов в секунду»), используйте System.Reactive. В System.Reactive предусмотрены естественные операторы для работы со временем.
См. также
- Асинхронные очереди
- Блокирующие очереди
- Регулировка очередей
Источник: Стивен Клири “Конкурентность в C#”. 2-е межд. изд. — СПб.: Питер, 2020. Глава 9.dotnet tool install --global Aws.Deploy.ToolsПосле установки можно выполнить следующую команду для начала развёртывания:
dotnet aws deployКоманда
deploy поможет вам выбрать правильный сервис AWS для развёртывания и настроить параметры. Развёртывание через CLI и через Visual Studio очень похоже, что позволяет развёртывать один и тот же проект из любой среды. Вы также можете составить список своих развёртываний с помощью команды dotnet aws list-deployments или удалить развёртывание с помощью dotnet aws delete-deployment. Используйте команду dotnet aws --help, чтобы найти дополнительные сведения о других доступных командах.
Подробности о работе инструмента есть в документации AWS.
Источник: aws.amazon.com/blogs/d…icationspublic int ParseWithString()
{
var prev = 0;
var curr = 0;
var rows = 0;
foreach (char c in _text)
{
if (c == '\n')
{
curr += 1;
var line = _text[prev..curr];
if (line.Equals(Environment.NewLine))
rows++;
prev = curr;
continue;
}
curr++;
}
return rows;
}
В первом примере мы используем строки. Мы пытаемся определить, сколько пустых строк в нашем файле. Текст хранится в строковой переменной _text. Мы перебираем каждый символ строке, если находим символ новой строки, с помощью Substring() проверяем предыдущую строку. Если эта строка пустая, увеличиваем счётчик. Ключевым моментом здесь является то, что Substring() создаёт строку в куче. Сборщику мусора потребуется время, чтобы уничтожить эти строки.
Теперь тот же процесс с использованием Span:
public int ParseWithSpan()
{
var textSpan = _text.AsSpan();
var prev = 0;
var curr = 0;
var rows = 0;
foreach (char c in textSpan)
{
if (c == '\n')
{
curr += 1;
var slice = textSpan[prev..curr];
if (slice.Equals(Environment.NewLine, StringComparison.OrdinalIgnoreCase))
rows++;
prev = curr;
continue;
}
curr++;
}
return rows;
}
Здесь процесс такой же, за исключением того, что мы не создаем дополнительные строки. Мы преобразуем текстовую строку в ReadOnlySpan, вызывая метод AsSpan(). Кроме того, вместо Substring() мы используем метод Slice, который создаёт ReadOnlySpan, представляющий подстроку. В этом случае в куче ничего не выделяется.
Вспомним, что сборка мусора напрямую влияет на производительность приложения. Используя ReadOnlySpan вместо строк везде, где возможно, мы можем сильно повысить производительность приложения. ReadOnlySpan поддерживает множество функций, знакомых по работе со строками: Contains(), EndsWith(), StartsWith(), IndexOf(), LastIndexOf(), ToString(), Trim() и т.п.
Тестирование
Оценим результаты теста производительности двух примеров выше:
ParseWithSpan - 23.55 us, без аллокаций.
ParseWithString - 36.12 us, аллоцировано 70,192 B
Результаты ожидаемы. Span работает быстрее и не выделяет дополнительной памяти (но см. ограничения в предыдущем посте).
Итого
Использование Span повышает производительность. Важно отметить, что команда разработчиков .NET активно использует Span во внутренних библиотеках и максимально упрощает их использование для разработчиков. Команда .NET уже имеет поддержку Span<T> для DateTime, TimeSpan, Int32, GUID, StringBuilder, System.Random и многих других типов. Важно понимать, как использовать Span, потому что они будут больше и больше присутствовать в будущем коде .NET и в некоторых случаях могут стать стандартом.
Источник: code-maze.com/csharp-…formanceSpan в C#, как он реализован и как мы можем использовать его для повышения производительности.
Span<T> — это типобезопасное и безопасное по памяти представление непрерывной области памяти. Span<T> реализован как ref-структурa (подробнее о ref-структурах), которая содержит ссылку на объект T и длину для чтения. Т.е. Span в C# всегда будет размещаться на стеке, а не в куче. Рассмотрим упрощённую реализацию Span<T>:
public readonly ref struct Span<T>
{
private readonly ref T _pointer;
private readonly int _length;
}
Использование Span<T> повышает производительность за счёт размещения на стеке, т.к. сборка мусора не тормозит выполнение приложения. Операции со Span<T> аналогичны операциям с массивами: индексация в Span не требует вычислений для определения адреса памяти.
Другой реализацией Span является ReadOnlySpan<T>. Его отличие от Span в том, что индексатор возвращает ссылку на T только для чтения. Это позволяет использовать ReadOnlySpan<T> для представления неизменяемых типов данных, таких как string.
Span может использовать типы значений, такие как int, byte, ref struct, bool и enum. Span не может использовать такие типы, как object, dynamic или интерфейсы.
Ограничения
Реализация Span ограничивает его использование в коде. Объекты ссылочного типа размещаются в куче, поэтому мы не можем использовать Span в качестве полей в ссылочных типах. Также ref-структуры не могут быть упакованы, и Span нельзя использовать в лямбда-выражениях и асинхронном коде с await, а также с yield. Кроме того, т.к. мы выделяем память в стеке, мы должны помнить, что памяти в стеке меньше, чем в куче.
Span-подобный класс можно использовать в асинхронном коде. Это классы Memory<T> и ReadOnlyMemory<T>.
Примеры
Мы можем использовать Span для работы с массивами и другими типами коллекций:
int[] arr = new[] { 0, 1, 2, 3 };
Span<int> intSpan = arr;
var otherSpan = arr.AsSpan();
C# предлагает неявное приведение от T[] к Span<T>, но мы также можем вызвать AsSpan() для массивов. Как ReadOnlySpan<T>, так и Span<T> предлагают знакомые по работе с массивами функции, не выделяющие дополнительную память.
Теперь рассмотрим на аналогичный пример с List<T>:
List<int> intList = new() { 0, 1, 2, 3 };
var listSpan = CollectionsMarshal.AsSpan(intList);
Использовать Span с коллекцией не так просто, как с массивами. В этом случае мы должны использовать метод CollectionMarshal.AsSpan(), чтобы получить коллекцию в виде Span. Для этого нужно импортировать пространство имён System.Runtime.InteropServices.
Окончание следует…
Источник: code-maze.com/csharp-…formance