Дата: 2019-09-08
System.IO.Pipelines — это новая библиотека, упрощающая организацию кода в .NET. Трудно обеспечить высокую производительность и точность, если приходится иметь дело со сложным кодом. Задача System.IO.Pipelines — упростить код. Подробнее под катом!
Библиотека появилась в результате усилий команды разработчиков .NET Core, которые стремились сделать Kestrel одним из самых быстрых веб-серверов в отрасли. Она изначально задумывалась как часть реализации Kestrel, но превратилась в повторно используемый API, доступный в версии 2.1 в качестве BCL API первого класса (System.IO.Pipelines).
Чтобы правильно анализировать данные из потока или сокета, требуется написать большой объем стандартного кода. При этом существует множество подводных камней, которые усложняют и сам код, и его поддержку.
Начнем с простой задачи. Нам необходимо написать TCP-сервер, который получает от клиента сообщения с разделителями строк ().
ОТСТУПЛЕНИЕ: как и в любой задаче, требующей высокой производительности, каждый конкретный случай стоит рассматривать исходя из особенностей вашего приложения. Возможно, тратить ресурсы на использование различных подходов, о которых пойдет речь далее, не имеет смысла, если масштаб сетевого приложения не очень велик. Обычный код .NET перед использованием конвейеров выглядит примерно так:
// Process a single line from the buffer
см. sample1.cs на GitHub Вероятно, этот код будет работать при локальном тестировании, но он имеет ряд ошибок:
Это наиболее распространенные ошибки чтения потоковых данных. Чтобы их избежать, необходимо внести ряд изменений:
Нужно буферизовать входящие данные, пока не будет найдена новая строка.
Необходимо проанализировать все строки, возвращаемые в буфер.
// EOF// Keep track of the amount of buffered bytes// Look for a EOL in the buffered data// Calculate the length of the line based on the offset// Process the line// Move the bytesConsumed to skip past the line we consumed (including )
см. sample2.cs на GitHub Повторюсь: это могло бы сработать при локальном тестировании, но иногда встречаются строки длиной больше 1 Кб (1024 байта). Необходимо увеличить размер входного буфера до тех пор, пока не будет найдена новая строка. Кроме того, мы собираем буферы в массив при обработке длинных строк. Мы можем улучшить этот процесс с помощью ArrayPool, что позволит избежать повторного распределения буферов во время анализа длинных строк, поступающих от клиента.
// Calculate the amount of bytes remaining in the buffer// Double the buffer size and copy the previously buffered data into the new buffer// Return the old buffer to the pool// EOF// Keep track of the amount of buffered bytes// Look for a EOL in the buffered data// Calculate the length of the line based on the offset// Process the line// Move the bytesConsumed to skip past the line we consumed (including \\n)