写在前面
Microsoft.Extensions.ObjectPool 是 ASP.NET Core 基础结构的一部分,当对象的初始化成本较高,并且可能被频繁使用时,才适合采用对象池技术;被ObjectPool管理的对象不会进入垃圾回收,使用时通过由实例对象实现的Get()方法,从对象池中借出对象,用完之后调用Return(T obj)方法,将对象还回去。也可以在Return(T obj)方法中设置判断条件,仅允许特定的对象进入对象池。
通过NuGet 获取 Microsoft.Extensions.ObjectPool 类库
代码实现
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.ObjectPool;
using System.Security.Cryptography;
using System.Text;var builder = WebApplication.CreateBuilder(args);// DefaultPooledObjectPolicy: 默认的策略,继承抽象类PooledObjectPolicy
builder.Services.TryAddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>();builder.Services.TryAddSingleton<ObjectPool<ReusableBuffer>>(serviceProvider =>
{var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();var policy = new DefaultPooledObjectPolicy<ReusableBuffer>();return provider.Create(policy);
});builder.Services.TryAddSingleton<ObjectPool<Person>>(serviceProvider => {var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();var policy = new PersonPoolPolicy();return provider.Create(policy);
});builder.Services.TryAddSingleton<ObjectPool<StringBuilder>>(serviceProvider =>
{var provider = serviceProvider.GetRequiredService<ObjectPoolProvider>();var policy = new StringBuilderPooledObjectPolicy();return provider.Create(policy);
});var app = builder.Build();// config middleware
app.UseMiddleware<BirthdayMiddleware>();app.MapGet("/", () => "Hello rjcql!");// return the SHA256 hash of a word
http://localhost:5128/hash/xxxx
app.MapGet("/hash/{word}", (string word, ObjectPool<ReusableBuffer> bufferPool) =>
{var buffer = bufferPool.Get();try{// Set the buffer data to the ASCII values of a wordfor (var i = 0; i < word.Length; i++){buffer.Data[i] = (byte)word[i];}Span<byte> hash = stackalloc byte[32];SHA256.HashData(buffer.Data.AsSpan(0, word.Length), hash);return "Hash: " + Convert.ToHexString(hash);}finally{// Data is automatically reset because this type implemented IResettablebufferPool.Return(buffer);}
});app.MapGet("/create/{name}", (string name, ObjectPool<Person> personPool) =>
{var person = personPool.Get();try{person.Id = Guid.NewGuid().ToString("N");var lastName = person.Name; // 这个是上一个对象的值person.Name = name;return $"{person.Id}:{person.Name}, {lastName}";}finally{// 根据条件回收personPool.Return(person);}
});app.Run();public class ReusableBuffer : IResettable
{public byte[] Data { get; } = new byte[1024 * 1024]; // 1 MBpublic bool TryReset(){Array.Clear(Data);return true;}
}public class Person
{public string Id { get; set; }public string Name { get; set; }
}public class PersonPoolPolicy : PooledObjectPolicy<Person>
{public override Person Create(){return new Person { Id = "", Name = "rjcql" };}public override bool Return(Person p){if (p.Name != "rjcql"){// 不允许其他名称的对象放入对象池return false;}return true;}
}/// <summary>
/// 创建中间件
/// </summary>
public class BirthdayMiddleware
{private readonly RequestDelegate _next;public BirthdayMiddleware(RequestDelegate next){_next = next;}public async Task InvokeAsync(HttpContext context, ObjectPool<StringBuilder> builderPool){var stringBuilder = builderPool.Get();try{stringBuilder.Append("Hi");// 在中间干点啥别的// await context.Response.WriteAsync(stringBuilder.ToString());await _next.Invoke(context);}finally // 即使出错也要保证归还对象{builderPool.Return(stringBuilder);}}
}
调用示例
因为设置了回收条件,所以只有名字为rjcql的对象才会被回收,所以每次调用都先把rjcql对象取出。