在作为高级软件工程师开发高规模.NET应用的十多年中,我亲历了众多性能挑战。有些经验来自深夜的生产事故,有些来自艰难的优化冲刺。以下是团队和我通过惨痛教训总结的十大最具影响力的性能优化实践。
某订单处理微服务在高负载下频繁超时,根源竟是不必要的async/await使用。虽然async/await强大,但每个await都会创建状态机,带来内存分配和上下文切换开销。
// 初始错误实现
public async Task<OrderResult> ProcessOrder(Order order)
{
var customer = await _customerRepository.GetCustomerAsync(order.CustomerId); // 异步
var validation = await ValidateOrderAsync(order); // 不必要的异步
var price = CalculatePrice(order); // 同步操作
return await SaveOrderAsync(order, price); // 异步
}
// 优化后版本
public async Task<OrderResult> ProcessOrder(Order order)
{
var customerTask = _customerRepository.GetCustomerAsync(order.CustomerId);
var validation = ValidateOrder(order); // 改为同步(CPU密集型)
var price = CalculatePrice(order);
var customer = await customerTask; // 仅在需要时await
return await SaveOrderAsync(order, price);
}
关键优化点:
仪表盘加载耗时20+秒,罪魁祸首是延迟加载引发的N+1查询。看似无害的代码可能触发数百次数据库往返。
// 问题代码
public async Task<List<OrderSummary>> GetOrderSummaries()
{
var orders = await _context.Orders.ToListAsync();
foreach (var order in orders)
{
// 每次循环触发独立查询!
var items = order.OrderItems.Count();
var customer = order.Customer.Name;
}
}
// 优化方案
public async Task<List<OrderSummary>> GetOrderSummaries()
{
return await _context.Orders
.Include(o => o.OrderItems)
.Include(o => o.Customer)
.Select(o => new OrderSummary
{
OrderId = o.Id,
ItemCount = o.OrderItems.Count,
CustomerName = o.Customer.Name
})
.ToListAsync();
}
优化成效:
Include()
预先加载关联实体某长运行服务的持续内存泄漏,竟源于未正确注销的事件处理程序。这在消息处理或长期订阅场景中尤为危险。
public class NotificationService : IDisposable
{
private readonly IMessageBus _messageBus;
private bool _disposed;
private readonly List<WeakReference> _references = new();
public NotificationService(IMessageBus messageBus)
{
_messageBus = messageBus;
// 忘记取消订阅!
_messageBus.OnMessage += HandleMessage;
}
// 修复后的Dispose实现
public void Dispose()
{
if (!_disposed)
{
_messageBus.OnMessage -= HandleMessage;
_disposed = true;
_references.Clear();
GC.SuppressFinalize(this);
}
}
}
改进要点:
日志处理服务在峰值时内存飙升,根源是循环中的字符串拼接——每次迭代创建新字符串对象,引发GC压力。
// 内存杀手
public string ProcessLogs(List<LogEntry> entries)
{
string result = "";
foreach (var entry in entries)
{
result += $"{entry.Timestamp}: {entry.Message}\n"; // 每次创建新字符串
}
return result;
}
// 优化方案
public string ProcessLogs(List<LogEntry> entries)
{
var sb = new StringBuilder(entries.Count * 85); // 预分配容量
foreach (var entry in entries)
{
sb.AppendFormat("{0}: {1}\n", entry.Timestamp, entry.Message);
}
return sb.ToString();
}
优化效果:
AppendFormat
代替插值过度缓存与不当过期策略反而降低性能,导致内存压力和脏数据问题。
// 原始错误缓存
public class ProductService
{
public async Task<Product> GetProduct(int id)
{
return await _cache.GetOrCreateAsync($"product_{id}", async entry =>
{
entry.SlidingExpiration = TimeSpan.FromHours(24); // 过长!
return await _repository.GetProductAsync(id);
});
}
}
// 智能缓存策略
public class ProductService
{
public async Task<Product> GetProduct(int id)
{
if (!_cache.TryGetValue(cacheKey, out Product product))
{
product = await _repository.GetProductAsync(id);
var options = new MemoryCacheEntryOptions()
.SetSlidingExpiration(TimeSpan.FromMinutes(10))
.SetAbsoluteExpiration(TimeSpan.FromHours(1))
.SetSize(1)
.AddExpirationToken(_productUpdateNotifier.Token);
_cache.Set(cacheKey, product, options);
}
return product;
}
}
优化策略:
频繁创建HttpClient实例导致端口耗尽,这在负载下会引发DNS问题和连接失败。
// 错误用法
public class ExternalService
{
private HttpClient CreateClient()
{
return new HttpClient(); // 频繁创建/销毁
}
}
// 正确使用HttpClientFactory
public class ExternalService
{
private readonly IHttpClientFactory _clientFactory;
private HttpClient GetClient() => _clientFactory.CreateClient("ExternalAPI");
}
// Startup配置
services.AddHttpClient("ExternalAPI", client =>
{
client.BaseAddress = new Uri("https://api.external.com");
client.Timeout = TimeSpan.FromSeconds(30);
})
.AddTransientHttpErrorPolicy(builder => builder
.WaitAndRetryAsync(3, retryAttempt =>
TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))));
改进点:
消息处理系统偶发挂起,根源是未正确管理TaskCompletionSource。这类问题在特定时序下难以复现。
// 问题实现
public class MessageProcessor
{
private readonly Dictionary<string, TaskCompletionSource<Response>> _pendingRequests = new();
public async Task<Response> SendAndWaitAsync(string messageId, Message message)
{
var tcs = new TaskCompletionSource<Response>();
_pendingRequests[messageId] = tcs;
await SendMessageAsync(message);
return await tcs.Task; // 可能永久挂起!
}
}
// 修复版本
public class MessageProcessor
{
public async Task<Response> SendAndWaitAsync(string messageId, Message message)
{
var tcs = new TaskCompletionSource<Response>(TaskCreationOptions.RunContinuationsAsynchronously);
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
cts.Token.Register(() =>
{
if (_pendingRequests.Remove(messageId, out var pendingTcs))
{
pendingTcs.TrySetCanceled();
}
});
try
{
await SendMessageAsync(message);
return await tcs.Task;
}
finally
{
_pendingRequests.Remove(messageId);
}
}
}
关键修复:
大对象序列化效率低下导致API响应缓慢,在复杂对象集合场景尤为明显。
// 原始低效实现
public class LargeResponse
{
public List<ComplexObject> Items { get; set; }
public Dictionary<string, string> Metadata { get; set; }
}
// 优化后版本
public class LargeResponse
{
[JsonIgnore]
public List<ComplexObject> FullItems { get; set; }
[JsonPropertyName("items")]
public List<SimpleDto> Items => FullItems.Select(i => new SimpleDto
{
Id = i.Id,
Name = i.Name,
Summary = i.GetSummary(),
LastModified = i.LastModified
}).ToList();
}
优化手段:
高负载时API因GC暂停数秒,根源是临时对象过量创建。
// 问题代码
public class DataProcessor
{
public async Task ProcessLargeDataSet(IEnumerable<DataItem> items)
{
var processedItems = items
.Select(item => new ProcessedItem(item)) // 创建大量临时对象
.ToList();
await SaveItems(processedItems);
}
}
// 优化方案:对象池+流处理
public class DataProcessor
{
private readonly ObjectPool<ProcessedItem> _itemPool;
public async Task ProcessLargeDataSet(IEnumerable<DataItem> items)
{
await foreach (var item in items.ConfigureAwait(false))
{
var processedItem = _itemPool.Get();
try
{
processedItem.Update(item);
await SaveItem(processedItem);
}
finally
{
_itemPool.Return(processedItem);
}
}
}
}
优化效果:
微服务在峰值期耗尽数据库连接,导致级联故障。
// 原始配置
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// 优化配置
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"),
sqlOptions =>
{
sqlOptions.MinPoolSize(10);
sqlOptions.MaxPoolSize(100);
sqlOptions.EnableRetryOnFailure(3, TimeSpan.FromSeconds(30), null);
sqlOptions.ConnectRetryCount(5);
}));
services.AddHealthChecks()
.AddDbContextCheck<AppDbContext>();
改进点:
这些优化经验均来自真实生产环境,核心启示包括:
性能优化是持续旅程。今日有效的策略,可能在应用扩展时需重新调整。始终测量、分析,并从错误中学习。