Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
Разгоняем ASP.NET
Core
Илья Вербицкий
WebStoating s.r.o.
Обо мне
• 15 лет опыта работы в области финансов и электронной
коммерции.
• Совладелец компании WebStoating s.r.o.
• Использую .NET c 2002 года.
Современный .NET
.NET Framework
• Базовая библиотека
• .NET Framework
BCL
• Виды приложений
• WPF
• Windows Forms
• Console
• ASP.NET
.NET Core
• Базовая библиотека
• .NET Core BCL
• Виды приложений
• Console
• ASP.NET Core
• UWP
• Xamarin.Forms
Xamarin
• Базовая библиотека
• Mono BCL
• Виды приложений
• iOS
• Android
• MacOS
.NET Core и .NET Standard
.NET Standard
• Множество реализаций .NET
• Проблема повторного
использования кода
• Спецификация
• 70% существующего кода
совместимо с версией 2.0
• Режим совместимости
.NET Core
• .NET Core 2.0
• Поддержка Windows, Linux и
MacOS
• Open Source (MIT)
• Простота развертывания
• Ваш любимый язык
программирования
• Любой текстовый редактор
Немного философии
• Вы не Google
• ROI
• Не стоит изобретать велосипед без необходимости
• Сконцентрируйтесь на решении конкретной задачи
• Существует много хороших инструментов. Главное – выбрать
правильный для вашего проекта
• Проблемы с производительностью из-за роста популярности – это
ОТЛИЧНЫЕ проблемы! Но сначала надо сделать популярный
продукт.
Наш сайт тормозит!
Сайт недоступен извне - YSlow
Chrome Developer Tools
Сначала реализуем все
рекомендации Google
PageSpeed Insights
Иногда этого бывает достаточно, особенно если у вас на сайте много больших
картинок.
Сборка и минификация JS и CSS
• BuildBundlerMinifier
• bundleconfig.json
• <environment> tag helper
• Gulp
• Grunt
• Webpack
Управление пакетами
• NuGet - https://www.nuget.org/
• Packet - https://fsprojects.github.io/Paket/index.html
Обработка изображений
• ImageMagic - http://imagemagick.org/
• Magic.NET - https://github.com/dlemstra/Magick.NET
• Не используйте System.Drawing и System.Windows.Media
• LibGD - http://libgd.github.io/
• CodeArt.DotnetGD
• ImageProcessor
• DynamicImage
• ImageResizer
Сжатие HTTP-траффика
• Microsoft.AspNetCore.ResponseCompression
• GzipCompressionProvider
• ICompressionProvider
• IIS Dynamic Compression Module
• Прокси-сервер NGINX
Веб-сервер Kestrel
• Microsoft.AspNetCore.Server.Kestrel
• Асинхронный веб-сервер, построенный на основе libuv.
• Можно использовать самостоятельно или через прокси-сервер
• Поддержка сокетов Unix, Web-сокетов и HTTPS
• Параметры веб-сервера
• MaxConcurrentConnections (нет лимита)
• MaxConcurrentUpgradedConnections (нет лимита)
• MaxRequestBodySize (28.6MB) и RequestSizeLimitAttribute
• MinRequestBodyDataRate (240 байт/сек., грейс-период 5 сек.)
Быстро починить не удалось 
Железо стоит дешевле программистов
• Нужен сервер «пожирнее»
• Больше памяти
• Больше ядер
• Поддержка Docker
• Облачные провайдеры, поддерживающие ASP.NET Core “из
коробки”
• Windows Azure
• AWS
• Google App Engine
Теперь посмотрим, что
происходит при обработке
HTTP-запроса
MiniProfiler
• Разработка Stack Exchange
• MiniProfiler
• MiniProfiler.EF6
• SQL-запросы, генерируемые Entity Framework 6
• MiniProfiler.Mvc4
• Views
• Контроллеры
«Копаем» глубже
SQL Server Profiler
SELECT N+1 и выбор ORM для проекта
Маппинг POCO-объектов (SELECT, 500 итераций)
SqlDataReader 47ms
Dapper 49ms
ServiceStack.OrmLite 50ms
PetaPoco 52ms
BLToolkit 80ms
SubSonic CodingHorror 107ms
NHibernate 181ms
Entity Framework 631ms
Источник: https://github.com/StackExchange/Dapper
Базу оптимизировали, но не
помогло
Добавим кэширование?
Кэширование
• IMemoryCache
• Microsoft.Extensions.Caching.Memory
• IDistributedCache
• Microsoft.Extensions.Caching.Redis
• Microsoft.Extensions.Caching.SqlServer
• Cache Tag Helper
• <cache>@DateTime.Now</cache>
• Distributed Cache Tag Helper
• <distributed-cache name=“my-id-1”>@DateTime.Now</distributed-cache>
• ResponseCacheAttribute
• Microsoft.AspNetCore.ResponseCaching
Наша память куда-то утекает
Может быть, проблема в сборщике мусора?
Режимы работы сборщика мусора
• Workstation GC
• Concurrent
• Non-concurrent
• Server GC
• Background
• Non-concurrent
Web.config
<configuration>
<runtime>
<gcServer enabled="true|false"/>
<gcConcurrent enabled="true|false"/>
<gcTrimCommitOnLowMemory enabled="true|false"/>
...
</runtime>
</configuration>
[AppName].runtimeconfig.json
{
"runtimeOptions": {
"configProperties": {
"System.GC.Server": true|false,
"System.GC.Concurrent": true|false,
...
},
...
}
Профилирование памяти в VS 2017
PerfView: Сколько времени тратится на GC?
PerfView: Где выделяется память?
PerfView: Текущие объекты в хипе
На сервере нет Visual Studio? - WinDbg
C:bin>procdump.exe -ma MemoryLeak
.loadby sos clr
!dumpheap -stat
!dumpheap -type System.String
!do 0336dc54
!dumparray /d 0336dc54
!do 0336dfb8
Пара слов о постраничной навигации
var persons = db.Person
.OrderBy(p => p.FirstName)
.ThenBy(p => p.LastName)
.ToList();
foreach(var person in persons.Take(100))
{
…
}
select FirstName, LastName
from Person.Person
order by FirstName, LastName
offset 0 rows
fetch next 10 rows only
«Ручное» управление памятью
• WeakReference
• GCSettings.LatencyMode
• LowLatency (Workstation GC only)
• SustainedLowLatency (Workstation and Server GC)
• Данный период должен быть как можно короче
• Постарайтесь не выделять большие объекты
• GC.Collect
• GC.TryStartNoGCRegion и GC.EndNoGCRegion
• Как насчет собственного сборщика мусора?
• Local GC Project (CoreCLR)
• https://github.com/dotnet/coreclr/tree/master/src/gc/sample.
GC настроили, а «тормоза»
остались
Проблемы с I/O, сетью и CPU.
Профилирование кода в MiniProfiler
Профилирование приложений в VS 2017
Асинхронные операции
Task Parallel Library
• Paraller.For и Parallel.ForEach
• Task.WhenAny, Task.WhenAll и Task.ContinueWith
• CancellationToken
• Palallel LINQ
Async/await «быстрее», но это не точно
• Код интенсивно использует I/O и сеть, а не CPU
• Вы хотите разрешить пользователям прерывать долгие операции
• Прирост производительности приложения превышает затраты на
переключение контекста
• Асинхронные операции НЕ ВЫПОЛНЯЮТСЯ быстрее, чем
синхронные
• Не стоит делать все приложение асинхронным!
Очереди сообщений и микросервисы
• MQ
• StackExchange.Redis
• Turbocharged.Beanstalk
• ZeroMQ
• RdKafka
• Service Bus
• NServiceBus
• MassTransit и MassTransit.RabbitMQ
• RestBus.AspNet и RestBus.RabbitMQ
Итоги
1. ROI
2. Выполните рекомендации Google PageSpeed Insights
3. Вертикальное масштабирование
4. Проверьте, нет ли проблем с базой данных
5. Профилирование памяти и настройка GC
6. Кэширование
7. Профилирование операций I/O, работы с сетью и CPU
8. MQ и микросервисы
Спасибо!
• https://verbitskiy.co/
• Twitter: @ilich_x86
• GitHub: https://github.com/ilich

More Related Content

Разгоняем ASP.NET Core / Илья Вербицкий (WebStoating s.r.o.)

Editor's Notes

  1. .NET Framework first shipped 15 years ago. .NET Standard is a specification of APIs that all .NET implementations must provide. It brings consistency to the .NET family and enables you to build libraries you can use from any .NET implementation. It replaces PCLs for building shared components. .NET Core is an implementation of the .NET Standard that’s optimized for building console applications, Web apps and cloud services using ASP.NET Core. Its SDK comes with a powerful tooling that in addition to Visual Studio development supports a full command line-based development workflow. You can learn more about them at aka.ms/netstandardfaq and aka.ms/netcore.
  2. The following characteristics best define .NET Core: Flexible deployment: Can be included in your app or installed side-by-side user- or machine-wide. Cross-platform: Runs on Windows, macOS and Linux; can be ported to other operating systems. The supported Operating Systems (OS), CPUs and application scenarios will grow over time, provided by Microsoft, other companies, and individuals. Command-line tools: All product scenarios can be exercised at the command-line. Compatible: .NET Core is compatible with .NET Framework, Xamarin and Mono, via the .NET Standard. Open source: The .NET Core platform is open source, using MIT and Apache 2 licenses. Documentation is licensed under CC-BY. .NET Core is a .NET Foundation project. Supported by Microsoft: .NET Core is supported by Microsoft, per .NET Core Support Workloads By itself, .NET Core includes a single application model -- console apps -- which is useful for tools, local services and text-based games. Additional application models have been built on top of .NET Core to extend its functionality, such as: ASP.NET Core Windows 10 Universal Windows Platform (UWP) Xamarin.Forms when targeting UWP
  3. Первый шаг (публичный сайт) – проанализировать скорость загрузки сайта, используя Google PageSpeed Insights.
  4. В Интранете можно использовать YSlow (http://yslow.org/).
  5. BuildBundlerMinifier: CSS, JS и HTML. <environment names="Development"> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.css" /> <link rel="stylesheet" href="~/css/site.css" /> </environment> <environment names="Staging,Production"> <link rel="stylesheet" href="https://ajax.aspnetcdn.com/ajax/bootstrap/3.3.7/css/bootstrap.min.css" asp-fallback-href="~/lib/bootstrap/dist/css/bootstrap.min.css" asp-fallback-test-class="sr-only" asp-fallback-test-property="position" asp-fallback-test-value="absolute" /> <link rel="stylesheet" href="~/css/site.min.css" asp-append-version="true" /> </environment> Visual Studio 2015 and 2017: Можно конвертировать bundleconfig.json в Gulp
  6. Paket is a dependency manager for .NET and mono projects, which is designed to work well with NuGet packages and also enables referencing files directly from Git repositories or any HTTP resource. It enables precise and predictable control over what packages the projects within your application reference.
  7. ImageProcessor - .NET Core support. ImageResizer – API + сервер для ASP.NET WebForms и MVC. Доступен по открытой и коммерческой лицензиям. Используется в nopCommerce. Используется в nopCommerce.
  8. You can create custom compression implementations with ICompressionProvider. The EncodingName represents the content encoding that this ICompressionProvider produces. The middleware uses this information to choose the provider based on the list specified in the Accept-Encoding header of the request. If you have an active IIS Dynamic Compression Module configured at the server level that you would like to disable for an app, you can do so with an addition to your web.config file. When a request is proxied by Nginx, the Accept-Encoding header is removed. This prevents the middleware from compressing the response.
  9. HTTPS Opaque upgrade used to enable WebSockets Unix sockets for high performance behind Nginx ASP.NET Core 2 – can use as stand-alone web-сервер. Maximum client connections There's a separate limit for connections that have been upgraded from HTTP or HTTPS to another protocol (for example, on a WebSockets request). After a connection is upgraded, it isn't counted against the MaxConcurrentConnections limit. The maximum number of connections is unlimited (null) by default. Maximum request body size The default maximum request body size is 30,000,000 bytes, which is approximately 28.6MB. The recommended way to override the limit in an ASP.NET Core MVC app is to use the RequestSizeLimit attribute on an action method. Minimum request body data rate Kestrel checks every second if data is coming in at the specified rate in bytes/second. If the rate drops below the minimum, the connection is timed out. The grace period is the amount of time that Kestrel gives the client to increase its send rate up to the minimum; the rate is not checked during that time. The grace period helps avoid dropping connections that are initially sending data at a slow rate due to TCP slow-start. The default minimum rate is 240 bytes/second, with a 5 second grace period.
  10. Вертикальное масштабирование нас спасет!
  11. v.3.2.0 – ASP.NET MVC only v.4.0.0-Alpha – ASP.NET Core protected void Application_Start() { MiniProfilerEF6.Initialize(); }
  12. The Cache Tag Helper provides the ability to dramatically improve the performance of your ASP.NET Core app by caching its content to the internal ASP.NET Core cache provider. The Razor View Engine sets the default expires-after to twenty minutes. The Distributed Cache Tag Helper provides the ability to dramatically improve the performance of your ASP.NET Core app by caching its content to a distributed cache source. The middleware determines when responses are cacheable, stores responses, and serves responses from cache.
  13. Workstation garbage collection can be concurrent or non-concurrent. Concurrent garbage collection enables managed threads to continue operations during a garbage collection. The collection occurs on the user thread that triggered the garbage collection and remains at the same priority. Because user threads typically run at normal priority, the garbage collector (which runs on a normal priority thread) must compete with other threads for CPU time. Server garbage collection, which is intended for server applications that need high throughput and scalability. Server garbage collection can be non-concurrent or background. The collection occurs on multiple dedicated threads that are running at THREAD_PRIORITY_HIGHEST priority level. A heap and a dedicated thread to perform garbage collection are provided for each CPU, and the heaps are collected at the same time. Each heap contains a small object heap and a large object heap, and all heaps can be accessed by user code. Objects on different heaps can refer to each other. Because multiple garbage collection threads work together, server garbage collection is faster than workstation garbage collection on the same size heap. Server garbage collection can be resource-intensive. For example, if you have 12 processes running on a computer that has 4 processors, there will be 48 dedicated garbage collection threads if they are all using server garbage collection. In a high memory load situation, if all the processes start doing garbage collection, the garbage collector will have 48 threads to schedule. gcServer = false (default) gcConcurrent = true (default) Note that ASP.NET and SQL Server enable server garbage collection automatically if your application is hosted inside one of these environments. If you are running hundreds of instances of an application, consider using workstation garbage collection with concurrent garbage collection disabled. This will result in less context switching, which can improve performance. In workstation or server garbage collection, you can enable concurrent garbage collection, which enables threads to run concurrently with a dedicated thread that performs the garbage collection for most of the duration of the collection. This option affects only garbage collections in generation 2; generations 0 and 1 are always non-concurrent because they finish very fast. Concurrent garbage collection enables interactive applications to be more responsive by minimizing pauses for a collection. Managed threads can continue to run most of the time while the concurrent garbage collection thread is running. This results in shorter pauses while a garbage collection is occurring. Your ability to allocate small objects on the heap during a concurrent garbage collection is limited by the objects left on the ephemeral segment when a concurrent garbage collection starts. As soon as you reach the end of the segment, you will have to wait for the concurrent garbage collection to finish while managed threads that have to make small object allocations are suspended. To improve performance when several processes are running, disable concurrent garbage collection. In background garbage collection, ephemeral generations (0 and 1) are collected as needed while the collection of generation 2 is in progress. There is no setting for background garbage collection; it is automatically enabled with concurrent garbage collection. Background garbage collection is a replacement for concurrent garbage collection. As with concurrent garbage collection, background garbage collection is performed on a dedicated thread and is applicable only to generation 2 collections. When background garbage collection is in progress and you have allocated enough objects in generation 0, the CLR performs a generation 0 or generation 1 foreground garbage collection. The dedicated background garbage collection thread checks at frequent safe points to determine whether there is a request for foreground garbage collection. If there is, the background collection suspends itself so that foreground garbage collection can occur. After the foreground garbage collection is completed, the dedicated background garbage collection thread and user threads resume. Background garbage collection removes allocation restrictions imposed by concurrent garbage collection, because ephemeral garbage collections can occur during background garbage collection. This means that background garbage collection can remove dead objects in ephemeral generations and can also expand the heap if needed during a generation 1 garbage collection. Starting with the .NET Framework 4.5, background server garbage collection is the default mode for server garbage collection. Background workstation garbage collection uses one dedicated background garbage collection thread, whereas background server garbage collection uses multiple threads, typically a dedicated thread for each logical processor. Unlike the workstation background garbage collection thread, these threads do not time out. In practice, there should rarely ever be a reason to disable background GC. If you want to prevent these background GC threads from ever taking CPU time from your application, but do not mind a potential increase in full, blocking GC latency or frequency, then you can turn this off.
  14. gcTrimCommitOnLowMemory This setting is recommended only for shared Web hosting scenarios. Because the garbage collector retains memory for future allocations, its committed space can be more than what is strictly needed. You can reduce this space to accommodate times when there is a heavy load on system memory. Reducing this committed space improves performance and expands the capacity to host more sites. When the gcTrimCommitOnLowMemory setting is enabled, the garbage collector evaluates the system memory load and enters a trimming mode when the load reaches 90%. It maintains the trimming mode until the load drops under 85%.
  15. 1 – анализ использования памяти 2 – самый большой объект 3 – как сильно объект увеличился в размере 4 – проблемный объект обнаружен 5 – проблемный участок кода JetBrains dotMemory Profiler
  16. Event Tracing for Windows (ETW) provides application programmers the ability to start and stop event tracing sessions, instrument an application to provide trace events, and consume trace events. Trace events contain an event header and provider-defined data that describes the current state of an application or operation. You can use the events to debug an application and perform capacity and performance analysis. PerfView - GCStats
  17. Кто и сколько раз обращался к LOH
  18. Take Heap Snapshot
  19. WinDbg
  20. Пример с купонами
  21. Weak References The garbage collector cannot collect an object in use by an application while the application's code can reach that object. The application is said to have a strong reference to the object. A weak reference permits the garbage collector to collect the object while still allowing the application to access the object. A weak reference is valid only during the indeterminate amount of time until the object is collected when no strong references exist. When you use a weak reference, the application can still obtain a strong reference to the object, which prevents it from being collected. However, there is always the risk that the garbage collector will get to the object first before a strong reference is reestablished. Weak references are useful for objects that use a lot of memory, but can be recreated easily if they are reclaimed by garbage collection. LatencyMode If you have periods of time that require critical performance, you can tell the GC not to perform expensive gen 2 collections. Both modes will greatly increase the size of the managed heap because compaction will not occur. If your process uses a lot of memory, you should avoid this feature. Right before entering this mode, it is a good idea to force a last full GC by calling GC.Collect( 2, GCCollectionMode.Forced). Once your code leaves this mode, do another one. You should never use this feature by default. It is designed for applications that must run without serious interruptions for a long time, but not 100% of the time. A good example is stock trading: during market hours, you do not want full garbage collections happening. When the market closes, you turn this mode off and perform full GCs until the market reopens. No GC You cannot nest calls to the TryStartNoGCRegion method, and you should only call the EndNoGCRegion method if the runtime is currently in no GC region latency mode. In other words, you should not call TryStartNoGCRegion multiple times (after the first method call, subsequent calls will not succeed), and you should not expect calls to EndNoGCRegion to succeed just because the first call to TryStartNoGCRegion succeeded. if (GCSettings.LatencyMode == GCLatencyMode.NoGCRegion) GC.EndNoGCRegion(); Local GC Project .NET Core 2.0
  22. 1 – MiniProfiler 2 – добавляем критическую секцию (string.Concat быстрее string.Format и StringBuilder) 3 – Информация о медленном запросе Проблемы с CPU и I/O
  23. 1 – общая аналитика 2 – информацию о конкретной функции JetBrains dotTrace Profiler
  24. The .NET Framework 4 introduced an asynchronous programming concept referred to as a Task and ASP.NET 4.5 supports Task. Tasks are represented by the Task type and related types in the System.Threading.Tasks namespace. The .NET Framework 4.5 builds on this asynchronous support with the await and async keywords that make working with Task objects much less complex than previous asynchronous approaches. The await keyword is syntactical shorthand for indicating that a piece of code should asynchronously wait on some other piece of code. The async keyword represents a hint that you can use to mark methods as task-based asynchronous methods. The combination of await, async, and the Task object makes it much easier for you to write asynchronous code in .NET 4.5. The new model for asynchronous methods is called the Task-based Asynchronous Pattern (TAP). This tutorial assumes you have some familiarity with asynchronous programing using await and async keywords and the Task namespace. On the web server, the .NET Framework maintains a pool of threads that are used to service ASP.NET requests. When a request arrives, a thread from the pool is dispatched to process that request. If the request is processed synchronously, the thread that processes the request is busy while the request is being processed, and that thread cannot service another request. This might not be a problem, because the thread pool can be made large enough to accommodate many busy threads. However, the number of threads in the thread pool is limited (the default maximum for .NET 4.5 is 5,000). In large applications with high concurrency of long-running requests, all available threads might be busy. This condition is known as thread starvation. When this condition is reached, the web server queues requests. If the request queue becomes full, the web server rejects requests with an HTTP 503 status (Server Too Busy). The CLR thread pool has limitations on new thread injections. If concurrency is bursty (that is, your web site can suddenly get a large number of requests) and all available request threads are busy because of backend calls with high latency, the limited thread injection rate can make your application respond very poorly. Additionally, each new thread added to the thread pool has overhead (such as 1 MB of stack memory). A web application using synchronous methods to service high latency calls where the thread pool grows to the .NET 4.5 default maximum of 5, 000 threads would consume approximately 5 GB more memory than an application able the service the same requests using asynchronous methods and only 50 threads. When you're doing asynchronous work, you're not always using a thread. For example, when you make an asynchronous web service request, ASP.NET will not be using any threads between the async method call and the await. Using the thread pool to service requests with high latency can lead to a large memory footprint and poor utilization of the server hardware. In web applications that see a large number of concurrent requests at start-up or has a bursty load (where concurrency increases suddenly), making web service calls asynchronous will increase the responsiveness of your application. An asynchronous request takes the same amount of time to process as a synchronous request. For example, if a request makes a web service call that requires two seconds to complete, the request takes two seconds whether it is performed synchronously or asynchronously. However, during an asynchronous call, a thread is not blocked from responding to other requests while it waits for the first request to complete. Therefore, asynchronous requests prevent request queuing and thread pool growth when there are many concurrent requests that invoke long-running operations.
  25. When the benefit of switching threads out weights the cost of the context switch. In general, you should make a method asynchronous if the synchronous method blocks the ASP.NET request thread while doing no work. By making the call asynchronous, the ASP.NET request thread is not blocked doing no work while it waits for the web service request to complete. Testing shows that the blocking operations are a bottleneck in site performance and that IIS can service more requests by using asynchronous methods for these blocking calls. Few applications require all methods to be asynchronous. Often, converting a few synchronous methods to asynchronous methods provides the best efficiency increase for the amount of work required.
  26. Turbocharged.Beanstalk – async libBeanstalk – sync