ASP.NET Core 性能最佳實踐(一)


這篇文章的主要內容來源於.NET文檔,此處翻譯前4條內容,其他內容會陸續貼出來

  1. 積極使用緩存
  2. 明確”熱代碼路徑”
  3. 避免使用阻塞調用
  4. 返回值使用IEnumerable<T> 還是 IAsyncEnumerable<T>?

 

積極使用緩存

詳情請查看:ASP.NET Core 中的響應緩存.

asp.net core 中的幾類緩存:

1.響應緩存(輸出緩存) ResponseCache

響應緩存本質上是控制響應數據Header頭中的Cache-Control,從而設置瀏覽器對Http請求的緩存。

有兩種實現方式:

1.使用響應緩存特性:ResponseCacheAttribute

  下面的示例表示響應緩存60秒

[ResponseCache(Duration = 60)]

  下面的示例表示不使用響應緩存,即響應Header頭為:Cache-Control:no-store,no-cache

[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]

 

2.使用響應緩存中間件 (可全局設定緩存策略)

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddResponseCaching();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
app.UseResponseCaching(); app.Use(async (context, next) => { context.Response.GetTypedHeaders().CacheControl = new Microsoft.Net.Http.Headers.CacheControlHeaderValue() { Public = true, MaxAge = TimeSpan.FromSeconds(10) }; context.Response.Headers[Microsoft.Net.Http.Headers.HeaderNames.Vary] = new string[] { "Accept-Encoding" }; await next(); }); }

注意:如果跨域中間件和響應緩存中間件共同使用的話,那么跨域中間件要在前面。

使用響應緩存設置緩存策略,不僅可以告知客戶端緩存響應數據,也可以明確告知客戶端不要緩存數據(Cache-Control:no-store,no-cache)。

 

2.內存緩存 IMemoryCache

在Startup中注入內存緩存依賴

public class Startup
{
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMemoryCache(option =>
            {
                option.CompactionPercentage = 0.1;
                option.SizeLimit = 1000;
            });
        }
}

MemoryCacheOptions的解釋:

SizeLimit:緩存的最大大小。

CompactionPercentage:表示超出緩存最大大小時,需要壓縮的緩存比例。

ExpirationScanFrequence:表示掃描過期項的時間間隔。(注意:只有在訪問緩存之后才會觸發)

使用內存緩存

public class HomeController : Controller
{
        private readonly IMemoryCache _cache;

        public HomeController(IMemoryCache cache)
        {
            _cache = cache;
        }

        public IActionResult Index()
        {
            _cache.TryGetValue("Test", out object value);
            if (value == null)
            {
                value = "Hello";
                _cache.Set("Test", value);
            }
            this.ViewBag.Test = value;

            return View();
        }
}

注意事項:緩存到達失效時間,並不會被立即處理,因為並沒有對緩存失效進行判斷的定時器。

第一種情況:當緩存再次被訪問( (GetSetRemove))時,會觸發失效掃描。

第二種情況:使用CancellationTokenSource,當CancellationTokenSource失效時,自動觸發緩存的失效。

 

3.分布式緩存 IDistributedCache

.NET Core中實現了IDistributedCache接口的四個分布式緩存:

1.分布式內存緩存

內存緩存,本質上並不是分布式緩存,主要用於在開發,測試階段使用。

services.AddDistributedMemoryCache();

2.分布式SQLServer緩存

使用SQLServer數據庫作為數據存儲,用以提供kv存儲的分布式服務

services.AddDistributedSqlServerCache(options =>
{
    options.ConnectionString = _config["DistCache_ConnectionString"];
    options.SchemaName = "dbo";
    options.TableName = "TestCache";
});

3.分布式緩存Redis

首先安裝程序包:Microsoft.Extensions.Caching.StackExchangeRedis

public void ConfigureServices(IServiceCollection services)
{
    services.AddStackExchangeRedisCache(options =>
    {
        options.Configuration = "localhost:6379";
    });
}

 

4.分布式緩存NCache

NCache是一個為.NET應該程序開發的,非常快,非常穩定的,開放源代碼的分布式緩存。(GitHub上的解釋)

 

4.對象池 ObjectPool

 

明確應用的”熱代碼路徑”

”熱代碼路徑”的定義為訪問頻繁並且耗時較長的代碼。”熱代碼”會限制應用對水平擴展,並且對性能影響很明細,這個問題在文檔的后續部分也會多次提及。

 我認為叫“關鍵路徑”更貼切,這一點的本質是找出系統中的性能瓶頸,確定對性能影響最大的代碼,然后加以解決。

 

避免使用阻塞調用

ASP.NET Core 程序應該被設計成同時處理多個請求。異步API使用一個小線程池可以處理上千個並發請求,而不會阻塞。這樣請求線程可以去處理其他請求,而不是等待一個長時同步任務完成。

ASP.NET Core 程序的一個常見的性能問題是,阻塞了本該異步執行的調用。很多同步調用會導致線程池飢餓,增大相應時間。

不要像下面這樣做:

  • 通過 Task.Wait 或 Task.Result 阻塞異步執行。
  • 在常用代碼上使用鎖(lock)。ASP.NET Core 程序被設計為並行架構時執行效率最高。
  • 自己定義一個 Task.Run ,然后去await 。由於ASP.NET Core 程序已經是在通用線程池上運行了,調用 Task.Run 是沒有必要的,

應該這樣做:

  • 異步調用“熱代碼”。
  • 在訪問數據,I/O,長時操作的時候,如果有異步API就盡量使用異步API。不要使用Task.Run將同步API異步執行。
  • 使Controller或Razor Page中的Action異步化。將整個調用棧異步化,以便使用 async/await。

性能分析器 PerfView,可以用來查找頻繁加入線程池的線程。

Microsoft-Windows-DotNETRuntime/ThreadPoolWorkerThread/Start 表明一個線程加入了線程池。

 

返回IEnumerable<T> 還是 IAsyncEnumerable<T>

如果Action Result返回IEnumerable<T>,那么序列化器以同步的方式處理集合迭代,這樣阻塞調用可能會導致線程池飢餓,為避免同步迭代,可在返回迭代之前調用 ToListAsync()。

從ASP.NET Core 3.0開始,IAsyncEnumerable<T>作為異步枚舉,可取代IEnumerable<T>。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM