ASP.NET Core性能优化全流程实践:从代码到部署的完整指南

发布时间:2026/9/1 22:02:45
ASP.NET Core性能优化全流程实践:从代码到部署的完整指南 在实际的 ASP.NET Core 项目开发中性能问题往往不是单一因素导致的而是由一系列微小的配置不当、代码习惯或架构选择累积而成。当应用面临高并发、大数据量或复杂业务逻辑时性能瓶颈会突然显现导致响应延迟、吞吐量下降甚至服务不可用。本文将以一个典型的 ASP.NET Core Web API 项目为例系统性地梳理从开发到部署全流程的性能优化实践。我们将从环境配置、代码编写、中间件使用、数据库操作、缓存策略、诊断工具等多个维度入手构建一套可落地、可验证的性能优化方案。无论你是正在处理现有项目的性能瓶颈还是希望在新项目中建立高性能的起点本文提供的思路和具体操作都能为你提供清晰的指引。1. 理解 ASP.NET Core 性能优化的核心维度性能优化不是简单地开启某个“高性能模式”而是一个系统工程。在开始动手之前我们需要建立一个清晰的认知框架知道应该从哪些方面去审视和优化我们的应用。1.1 性能指标与优化目标优化前必须先明确目标。对于 Web 应用核心性能指标通常包括响应时间从客户端发起请求到收到完整响应所花费的时间。这是用户体验最直接的感受。吞吐量单位时间内系统能够成功处理的请求数量如 RPS - Requests Per Second。资源利用率CPU、内存、磁盘 I/O、网络带宽的占用情况。优化目标是在保证吞吐量和响应时间的前提下降低资源消耗。并发用户数系统在可接受的响应时间内能够同时服务的用户数量。不同的业务场景侧重点不同。例如一个实时交易系统对响应时间极其敏感而一个报表导出服务可能更关注吞吐量和内存使用。1.2 性能瓶颈的常见来源在 ASP.NET Core 应用中瓶颈通常出现在以下几个层面应用程序代码低效的算法、不当的循环、频繁的字符串拼接、未释放的资源如数据库连接、文件句柄、同步阻塞异步调用等。框架与中间件配置未启用响应压缩、未配置合理的缓存策略、中间件管道顺序不当、日志级别过高且未异步等。数据访问层N1 查询问题、缺少索引、大表全表扫描、未使用参数化查询导致 SQL 注入风险及计划缓存污染、事务范围过大等。外部服务与集成同步调用外部 HTTP 服务、未设置超时和重试、序列化/反序列化开销大。基础设施与部署服务器资源配置不足、未启用 Kestrel 优化选项、未使用反向代理如 Nginx、IIS进行静态文件服务和负载均衡、容器配置不当等。优化工作应遵循“测量 - 分析 - 优化 - 验证”的循环。盲目优化往往事倍功半。2. 环境准备与基准测试建立在优化之前我们必须有一个稳定的基准和合适的测试工具否则无法量化优化效果。2.1 创建基准测试项目首先创建一个用于测试的 ASP.NET Core Web API 项目。这里我们使用 .NET 8其长期支持版本是当前生产环境的稳健选择其理念与输入材料中提到的 .NET 9 预览版一脉相承但更稳定。dotnet new webapi -n PerformanceDemo -f net8.0 cd PerformanceDemo2.2 引入性能测试与诊断工具工欲善其事必先利其器。我们需要以下工具基准测试使用 BenchmarkDotNet 对特定方法或算法进行微观基准测试。负载测试使用 Apache JMeter 、 k6 或 Vegeta 模拟多用户并发测试 API 端点。应用性能监控 (APM)使用 Application Insights 、 OpenTelemetry 或 MiniProfiler 进行运行时诊断。修改PerformanceDemo.csproj添加 BenchmarkDotNet 和 MiniProfiler 的包引用Project SdkMicrosoft.NET.Sdk.Web PropertyGroup TargetFrameworknet8.0/TargetFramework Nullableenable/Nullable ImplicitUsingsenable/ImplicitUsings /PropertyGroup ItemGroup PackageReference IncludeBenchmarkDotNet Version0.13.12 / PackageReference IncludeMiniProfiler.AspNetCore Version4.3.8 / !-- 其他包引用 -- /ItemGroup /Project2.3 建立一个“性能不佳”的示例端点为了演示优化过程我们在Controllers/WeatherForecastController.cs中添加一个存在典型问题的端点using Microsoft.AspNetCore.Mvc; using System.Diagnostics; namespace PerformanceDemo.Controllers; [ApiController] [Route([controller])] public class WeatherForecastController : ControllerBase { // ... 原有代码 ... [HttpGet(slow)] public IActionResult GetSlow() { // 模拟低效CPU计算 var sum 0L; for (int i 0; i 1_000_000; i) { sum i; } // 模拟同步阻塞反模式 Task.Delay(100).Wait(); // 错误同步等待异步操作 // 低效字符串操作 var result ; for (int i 0; i 1000; i) { result i.ToString(); // 错误在循环中拼接字符串 } return Ok(new { sum, message This endpoint has several performance issues. }); } }这个端点包含了循环内字符串拼接、同步阻塞异步调用等常见问题。我们将以此为基础进行优化。3. 应用程序代码层优化代码层面的优化是收益最直接也往往是最容易被忽视的环节。3.1 使用正确的数据结构与算法这是优化的根本。对于查找操作频繁的场景使用HashSetT或DictionaryTKey, TValue而非ListT。对于需要频繁在头部/尾部插入删除的场景考虑LinkedListT。3.2 避免常见的性能陷阱1. 字符串操作优化字符串在 .NET 中是不可变的。每次拼接都会创建新的字符串对象。对于频繁的拼接操作务必使用StringBuilder。// 优化前低效 string result ; for (int i 0; i 1000; i) result i; // 优化后高效 var sb new StringBuilder(); for (int i 0; i 1000; i) sb.Append(i); string result sb.ToString();2. 集合初始化与预分配如果知道集合的大致大小在初始化时指定容量可以避免多次内存分配和复制。// 优化前 var list new Listint(); for (int i 0; i 10000; i) list.Add(i); // 优化后 var list new Listint(10000); // 预分配容量 for (int i 0; i 10000; i) list.Add(i);3. 异步编程的正确姿势始终遵循“Async All the Way”原则。避免使用.Result、.Wait()或GetAwaiter().GetResult()在异步方法上阻塞这会导致线程池线程浪费极易引发死锁和性能下降。// 优化前错误 public IActionResult GetData() { var data _service.GetDataAsync().Result; // 同步阻塞 return Ok(data); } // 优化后正确 public async TaskIActionResult GetDataAsync() { var data await _service.GetDataAsync(); // 异步等待 return Ok(data); }确保你的控制器、服务层、数据访问层的方法签名都正确使用async/await。3.3 优化我们的示例端点根据以上原则重构GetSlow端点[HttpGet(optimized)] public async TaskIActionResult GetOptimizedAsync() { // 使用更高效的算法或并行计算如果适用 // 这里仅作演示实际计算可能不同 var sum 0L; // 简单的循环计算如果计算量巨大可考虑 Parallel.For for (int i 0; i 1_000_000; i) { sum i; } // 正确使用异步等待 await Task.Delay(100); // 模拟异步I/O操作 // 使用 StringBuilder 优化字符串拼接 var sb new StringBuilder(4000); // 预估容量 for (int i 0; i 1000; i) { sb.Append(i); } var result sb.ToString(); return Ok(new { sum, message This endpoint has been optimized., data result }); }使用 BenchmarkDotNet 创建一个控制台项目来对比这两个端点核心逻辑的性能差异这里仅对比字符串拼接部分作为示例using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Running; using System.Text; namespace StringConcatBenchmark; [MemoryDiagnoser] // 同时分析内存分配 public class StringBenchmark { [Benchmark] public string ConcatenateWithPlusOperator() { string result ; for (int i 0; i 1000; i) result i; return result; } [Benchmark] public string ConcatenateWithStringBuilder() { var sb new StringBuilder(4000); for (int i 0; i 1000; i) sb.Append(i); return sb.ToString(); } } public class Program { public static void Main(string[] args) { var summary BenchmarkRunner.RunStringBenchmark(); } }运行此基准测试你会看到StringBuilder版本在时间和内存分配上都有数量级的优势。4. 框架与中间件配置优化ASP.NET Core 框架本身提供了许多可配置的选项来提升性能。4.1 启用响应压缩对于文本类型的响应JSON, HTML, CSS, JS压缩可以显著减少网络传输大小。在Program.cs中启用var builder WebApplication.CreateBuilder(args); // 添加响应压缩服务 builder.Services.AddResponseCompression(options { options.EnableForHttps true; // 也为 HTTPS 启用 options.Providers.AddBrotliCompressionProvider(); options.Providers.AddGzipCompressionProvider(); }); // 配置压缩提供程序选项可选 builder.Services.ConfigureBrotliCompressionProviderOptions(options { options.Level CompressionLevel.Fastest; }); builder.Services.ConfigureGzipCompressionProviderOptions(options { options.Level CompressionLevel.SmallestSize; }); var app builder.Build(); // 在 UseRouting 之后UseEndpoints 之前使用中间件 app.UseResponseCompression(); // ... 其他中间件配置 app.Run();注意静态文件中间件默认可能不压缩对于频繁访问的静态资源考虑使用 CDN 或反向代理如 Nginx进行压缩和缓存。4.2 配置合理的 JSON 序列化选项System.Text.Json是高性能的默认序列化器。可以通过配置进一步优化builder.Services.AddControllers() .AddJsonOptions(options { // 使用不区分大小写的属性名称匹配根据需求 options.JsonSerializerOptions.PropertyNameCaseInsensitive true; // 使用驼峰命名法与前端 JavaScript 惯例一致 options.JsonSerializerOptions.PropertyNamingPolicy JsonNamingPolicy.CamelCase; // 忽略循环引用根据业务逻辑决定 // options.JsonSerializerOptions.ReferenceHandler ReferenceHandler.IgnoreCycles; // 使用源代码生成器以获得最佳性能.NET 6 // options.JsonSerializerOptions.AddContextYourJsonSerializerContext(); });对于性能要求极高的场景可以考虑使用源代码生成器Source Generator来避免反射开销。4.3 优化中间件管道顺序中间件的执行顺序影响性能。应将最可能短路请求如静态文件服务、健康检查或最轻量的中间件放在前面将复杂的、耗时的中间件如认证、授权放在后面。var app builder.Build(); // 异常处理应最早以便捕获后续中间件的异常 if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler(/Error); app.UseHsts(); } app.UseHttpsRedirection(); // 静态文件服务应较早避免进入 MVC 路由 app.UseStaticFiles(); // 响应压缩应在产生响应体的中间件之前 app.UseResponseCompression(); app.UseRouting(); // 认证、授权等较重的中间件放在路由之后 app.UseAuthentication(); app.UseAuthorization(); app.MapControllers(); app.Run();4.4 使用IHttpClientFactory管理 HTTP 客户端避免直接使用new HttpClient()它不会回收底层套接字可能导致端口耗尽。始终使用IHttpClientFactory它能管理连接池和生命周期。// 在 Program.cs 中注册 builder.Services.AddHttpClient(ExternalApi, client { client.BaseAddress new Uri(https://api.example.com/); client.DefaultRequestHeaders.Add(Accept, application/json); // 设置合理的超时时间 client.Timeout TimeSpan.FromSeconds(30); }); // 在服务中使用 public class MyService { private readonly IHttpClientFactory _httpClientFactory; public MyService(IHttpClientFactory httpClientFactory) _httpClientFactory httpClientFactory; public async TaskSomeModel GetExternalDataAsync() { var client _httpClientFactory.CreateClient(ExternalApi); var response await client.GetAsync(/data); response.EnsureSuccessStatusCode(); return await response.Content.ReadFromJsonAsyncSomeModel(); } }5. 数据访问与数据库优化数据库通常是 Web 应用的性能瓶颈所在。优化数据访问能带来巨大收益。5.1 使用异步数据库操作确保所有数据库调用使用 Entity Framework Core 或 Dapper都是异步的。// Entity Framework Core 示例 public class ProductService { private readonly AppDbContext _context; public ProductService(AppDbContext context) _context context; // 同步错误 public ListProduct GetProductsSync() _context.Products.ToList(); // 异步正确 public async TaskListProduct GetProductsAsync() await _context.Products.ToListAsync(); }5.2 解决 N1 查询问题这是 ORM 中最常见的性能问题。使用Include或投影Select来一次性加载所需数据。// 问题N1 查询 var orders await _context.Orders.ToListAsync(); foreach (var order in orders) // 第一次查询获取所有订单 { // 对每个订单发起一次查询获取客户信息 var customer await _context.Customers.FindAsync(order.CustomerId); } // 解决方案1使用 Include 预先加载 var ordersWithCustomers await _context.Orders .Include(o o.Customer) // 一次性加载关联的 Customer 数据 .ToListAsync(); // 解决方案2使用投影Select仅获取所需字段效率更高 var orderSummaries await _context.Orders .Select(o new OrderSummary { OrderId o.Id, OrderDate o.OrderDate, CustomerName o.Customer.Name // 在查询中关联数据库执行 JOIN }) .ToListAsync();5.3 使用高效的查询语句只选择需要的列避免SELECT *。合理使用索引为WHERE、JOIN、ORDER BY子句中的列创建索引。但索引不是越多越好写操作会变慢。分页查询对于大量数据务必使用分页Skip/Take或Keyset Pagination。// 分页查询示例 public async TaskPagedResultProduct GetProductsPagedAsync(int pageIndex, int pageSize) { var query _context.Products.AsNoTracking(); // AsNoTracking 用于只读查询提升性能 var totalCount await query.CountAsync(); var items await query .OrderBy(p p.Id) .Skip((pageIndex - 1) * pageSize) .Take(pageSize) .ToListAsync(); return new PagedResultProduct(items, pageIndex, pageSize, totalCount); }5.4 实施缓存策略缓存是提升性能的利器尤其是对于变化不频繁的“热数据”。1. 内存缓存IMemoryCache适用于单实例部署存储简单、少量的数据。builder.Services.AddMemoryCache(); public class CatalogService { private readonly IMemoryCache _cache; private readonly AppDbContext _context; private readonly TimeSpan _cacheDuration TimeSpan.FromMinutes(5); public async TaskListCategory GetCategoriesAsync() { // 尝试从缓存获取 if (!_cache.TryGetValue(AllCategories, out ListCategory categories)) { // 缓存中没有从数据库获取 categories await _context.Categories.ToListAsync(); // 存入缓存设置过期时间 var cacheEntryOptions new MemoryCacheEntryOptions() .SetSlidingExpiration(_cacheDuration); _cache.Set(AllCategories, categories, cacheEntryOptions); } return categories; } }2. 分布式缓存IDistributedCache适用于多实例部署或需要跨进程共享缓存的场景如使用 Redis。// 安装包Microsoft.Extensions.Caching.StackExchangeRedis builder.Services.AddStackExchangeRedisCache(options { options.Configuration builder.Configuration.GetConnectionString(Redis); options.InstanceName PerformanceDemo_; }); public class CatalogService { private readonly IDistributedCache _cache; private readonly AppDbContext _context; public async TaskListCategory GetCategoriesAsync() { var cacheKey AllCategories; var cachedData await _cache.GetStringAsync(cacheKey); if (cachedData ! null) { return JsonSerializer.DeserializeListCategory(cachedData); } var categories await _context.Categories.ToListAsync(); var serializedData JsonSerializer.Serialize(categories); await _cache.SetStringAsync(cacheKey, serializedData, new DistributedCacheEntryOptions { SlidingExpiration TimeSpan.FromMinutes(5) }); return categories; } }6. 部署与基础设施优化应用最终运行在服务器上服务器和网络配置对性能有决定性影响。6.1 Kestrel 服务器配置Kestrel 是 ASP.NET Core 的内置 Web 服务器。在appsettings.json或Program.cs中可以进行优化配置{ Kestrel: { Limits: { MaxConcurrentConnections: 100, // 根据服务器资源调整 MaxConcurrentUpgradedConnections: 100, // WebSocket 连接限制 MaxRequestBodySize: 52428800, // 50MB限制请求体大小防攻击 MinRequestBodyDataRate: { BytesPerSecond: 240, GracePeriod: 00:00:05 }, MinResponseDataRate: { BytesPerSecond: 240, GracePeriod: 00:00:05 } }, Endpoints: { Http: { Url: http://localhost:5000 }, Https: { Url: https://localhost:5001 } } } }对于高并发场景可能需要调整线程池设置但这通常是最后的手段且需要谨慎// 在 Program.cs 的 Main 或 CreateHostBuilder 最开始处 ThreadPool.SetMinThreads(100, 100); // 设置最小工作线程和IO线程数6.2 使用反向代理在生产环境中通常将 Kestrel 放在 Nginx 或 IIS 等反向代理之后。反向代理可以提供静态文件服务更高效。SSL 终止减轻 Kestrel 的加密负担。负载均衡将请求分发到多个应用实例。缓冲保护应用免受慢客户端攻击。Nginx 配置示例 (/etc/nginx/sites-available/yourdomain)server { listen 80; server_name yourdomain.com; return 301 https://$server_name$request_uri; } server { listen 443 ssl http2; server_name yourdomain.com; ssl_certificate /path/to/your/cert.pem; ssl_certificate_key /path/to/your/key.pem; location / { proxy_pass http://localhost:5000; # Kestrel 监听地址 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection keep-alive; proxy_set_header Host $host; proxy_cache_bypass $http_upgrade; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto $scheme; # 缓冲设置提升对慢客户端的处理能力 proxy_buffering on; proxy_buffer_size 4k; proxy_buffers 8 4k; proxy_busy_buffers_size 8k; } # 静态文件由 Nginx 直接处理性能更好 location ~* \.(css|js|png|jpg|jpeg|gif|ico|svg)$ { root /var/www/yourdomain/wwwroot; expires 1y; add_header Cache-Control public, immutable; } }6.3 容器化优化如果使用 Docker 部署注意镜像大小和运行时配置。# 使用多阶段构建减小镜像体积 FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build WORKDIR /src COPY [PerformanceDemo.csproj, .] RUN dotnet restore PerformanceDemo.csproj COPY . . RUN dotnet publish PerformanceDemo.csproj -c Release -o /app/publish FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS runtime WORKDIR /app EXPOSE 80 EXPOSE 443 # 使用非 root 用户运行 RUN adduser --disabled-password --gecos appuser chown -R appuser /app USER appuser COPY --frombuild /app/publish . ENTRYPOINT [dotnet, PerformanceDemo.dll]在docker run或编排文件如 docker-compose.yml中可以设置环境变量来优化 .NET 运行时services: webapp: image: your-image environment: - ASPNETCORE_ENVIRONMENTProduction - DOTNET_SYSTEM_GLOBALIZATION_INVARIANTtrue # 如果不需要全球化可设为 true 以提升启动速度 - COMPlus_ReadyToRun1 # 启用 ReadyToRun (AOT编译的一部分) - COMPlus_TieredCompilation1 # 启用分层编译 deploy: resources: limits: cpus: 2 # 限制 CPU memory: 1G # 限制内存7. 性能诊断与监控优化离不开持续的监控和诊断。我们需要知道应用在真实负载下的表现。7.1 使用 MiniProfiler 进行轻量级诊断MiniProfiler 可以直观地显示每个请求的耗时精确到每个 SQL 查询、中间件执行时间。在Program.cs中配置using StackExchange.Profiling; builder.Services.AddMiniProfiler(options { options.RouteBasePath /profiler; // 访问路径 options.ColorScheme StackExchange.Profiling.ColorScheme.Auto; options.PopupShowTimeWithChildren true; // 跟踪 SQL 查询如果使用 EF Core options.TrackConnectionOpenClose true; }).AddEntityFramework(); // 添加对 EF Core 的支持 var app builder.Build(); // ... 其他中间件 app.UseMiniProfiler();访问/profiler即可看到性能分析结果。7.2 结构化日志与集中式日志收集使用Serilog或NLog替代默认的控制台日志并输出到文件或日志服务如 Elasticsearch Kibana, Seq。// 安装包Serilog.AspNetCore builder.Host.UseSerilog((context, config) { config.ReadFrom.Configuration(context.Configuration) .Enrich.FromLogContext() .WriteTo.Console(outputTemplate: [{Timestamp:HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}) .WriteTo.File(logs/app-.txt, rollingInterval: RollingInterval.Day); });在日志中记录关键性能指标如请求处理时间、数据库查询时间。7.3 健康检查与指标端点ASP.NET Core 内置了健康检查可以快速了解应用状态。builder.Services.AddHealthChecks() .AddDbContextCheckAppDbContext() // 检查数据库连接 .AddRedis(builder.Configuration.GetConnectionString(Redis)) // 检查 Redis .AddUrlGroup(new Uri(https://api.example.com/health), External API); // 检查外部服务 var app builder.Build(); app.MapHealthChecks(/health);还可以使用AppMetrics或OpenTelemetry暴露 Prometheus 格式的指标与 Grafana 等监控系统集成。8. 常见性能问题排查清单当遇到性能问题时可以按以下清单进行排查问题现象可能原因检查方式处理建议CPU 使用率持续过高1. 存在死循环或低效算法。2. 大量同步阻塞调用。3. 序列化/反序列化开销大。4. 日志级别过高如 Debug。1. 使用性能分析器如 dotnet-trace, Visual Studio Profiler查看热点函数。2. 检查代码中是否有.Result、.Wait()。3. 检查 JSON/XML 序列化的数据量。1. 优化算法使用更高效的数据结构。2. 将同步方法改为异步。3. 减少不必要的数据传输使用投影。4. 生产环境将日志级别设为 Warning 或 Error。内存使用量不断增长内存泄漏1. 静态集合或缓存无限增长。2. 未及时释放非托管资源文件、网络连接。3. 事件订阅未取消。4. 大对象堆LOH碎片化。1. 使用内存分析工具如 dotnet-counters, dotnet-dump。2. 检查代码中的静态Dictionary、List。3. 检查IDisposable对象的using语句或Dispose调用。1. 为缓存设置过期策略或大小限制。2. 确保IDisposable对象被正确释放。3. 及时取消事件订阅。4. 考虑使用ArrayPoolT或对象池。数据库响应慢1. N1 查询。2. 缺少索引。3. 锁竞争。4. 查询未使用参数化导致计划缓存污染。1. 使用 MiniProfiler 或 EF Core 日志查看生成的 SQL。2. 分析数据库慢查询日志。3. 使用EXPLAIN或执行计划分析工具。1. 使用Include或投影解决 N1。2. 为高频查询条件添加索引。3. 优化事务隔离级别和范围。4. 始终使用参数化查询。应用启动缓慢1. 首次请求的 JIT 编译。2. 依赖注入容器注册项过多、复杂。3. 启动时同步执行大量初始化逻辑。1. 使用ReadyToRun(R2R) 编译。2. 检查Program.cs和Startup.cs中的初始化代码。3. 使用IHostedService进行后台初始化。1. 发布时使用-p:PublishReadyToRuntrue。2. 延迟初始化非核心服务。3. 将耗时的启动任务异步化或移到后台服务。特定端点响应慢1. 该端点逻辑复杂。2. 调用了慢速的外部服务。3. 序列化了大对象。1. 使用 MiniProfiler 定位该端点的耗时环节。2. 检查该端点的外部依赖。3. 检查返回的数据量。1. 优化端点业务逻辑考虑异步并行处理。2. 为外部调用设置超时和重试并考虑缓存结果。3. 使用分页或仅返回必要字段。9. 生产环境性能优化最佳实践将优化措施固化到开发流程和运维规范中。性能测试左移在开发阶段就引入基准测试和集成测试对关键路径进行性能验证。代码审查关注性能在代码审查中将性能反模式如同步阻塞、N1查询、循环内字符串拼接作为审查重点。建立性能基线在应用上线前使用负载测试工具建立性能基线如平均响应时间、P95/P99延迟、最大RPS。后续任何重大变更都应与基线对比。实施渐进式发布与监控使用蓝绿部署或金丝雀发布先让一小部分流量进入新版本密切监控性能指标CPU、内存、错误率、延迟确认无异常后再全量发布。配置告警对核心性能指标如接口P99延迟 1秒错误率 0.1%设置告警以便在用户感知前发现问题。定期进行容量规划与压测随着业务增长定期进行压力测试评估系统容量瓶颈提前进行扩容或优化。性能优化是一个持续的过程而非一劳永逸的任务。它要求开发者不仅关注功能的实现更要具备全局视角从代码细节、架构设计、基础设施等多个层面进行系统性思考。从今天起将本文提到的检查点融入你的日常开发和运维习惯你的 ASP.NET Core 应用将变得更加健壮和高效。