EF Core 6中的DbContextPool
起因
前几天通过GitHub看到EF Core 修改DbContextPool默认大小,有原来的128改为1024.便打开DbContextPool源码学习了一番.DbContextPool源码
public class DbContextPool<TContext> : IDbContextPool<TContext>, IDisposable, IAsyncDisposable
where TContext : DbContext
{
//默认大小由原来的128,改为1024
public const int DefaultPoolSize = 1024;
//队列
private readonly ConcurrentQueue<IDbContextPoolable> _pool = new();
//缓存创建DbContext对象(或者集成DbContext对象的实例)委托
//该委托 相当于new DbContext(),性能比反射高很多
private readonly Func<DbContext> _activator;
private int _maxSize;
private int _count;
//
public DbContextPool(DbContextOptions<TContext> options)
{
_maxSize = options.FindExtension<CoreOptionsExtension>()?.MaxPoolSize ?? DefaultPoolSize;
if (_maxSize <= 0)
{
throw new ArgumentOutOfRangeException(nameof(CoreOptionsExtension.MaxPoolSize), CoreStrings.InvalidPoolSize);
}
options.Freeze();
_activator = CreateActivator(options);
}
//1.获取DbContext对象或继承DbContext对象构造方法和参数
//2.通过表达式树创建并返回一个委托
private static Func<DbContext> CreateActivator(DbContextOptions<TContext> options)
{
var constructors = typeof(TContext).GetTypeInfo().DeclaredConstructors.Where(c => !c.IsStatic && c.IsPublic).ToArray();
if (constructors.Length == 1)
{
var parameters = constructors[0].GetParameters();
if (parameters.Length == 1
&& (parameters[0].ParameterType == typeof(DbContextOptions)
|| parameters[0].ParameterType == typeof(DbContextOptions<TContext>)))
{
return Expression.Lambda<Func<TContext>>(Expression.New(constructors[0], Expression.Constant(options))).Compile();
}
}
throw new InvalidOperationException(CoreStrings.PoolingContextCtorError(typeof(TContext).ShortDisplayName()));
}
//1. 如果DbContext池有DbContext对象,从队列中获取
//2. 如果队里中,没有DbContext,则由委托创建DbContext
public virtual IDbContextPoolable Rent()
{
if (_pool.TryDequeue(out var context))
{
Interlocked.Decrement(ref _count);
Check.DebugAssert(_count >= 0, $"_count is {_count}");
return context;
}
context = _activator();
context.SnapshotConfiguration();
return context;
}
// 1.当DbContext对象使用完毕,队列中的DbContext数量没有超过默认值1024,则DbContext放入队列中
// 2.如果队列中超过1024这个默认值,则DbContext进行释放
public virtual void Return(IDbContextPoolable context)
{
if (Interlocked.Increment(ref _count) <= _maxSize)
{
context.ResetState();
_pool.Enqueue(context);
}
else
{
PooledReturn(context);
}
}
// 归还DbContext的异步实现
public virtual async ValueTask ReturnAsync(IDbContextPoolable context, CancellationToken cancellationToken = default)
{
if (Interlocked.Increment(ref _count) <= _maxSize)
{
await context.ResetStateAsync(cancellationToken).ConfigureAwait(false);
_pool.Enqueue(context);
}
else
{
PooledReturn(context);
}
}
private void PooledReturn(IDbContextPoolable context)
{
Interlocked.Decrement(ref _count);
Check.DebugAssert(_maxSize == 0 || _pool.Count <= _maxSize, $"_maxSize is {_maxSize}");
context.ClearLease();
context.Dispose();
}
public virtual void Dispose()
{
_maxSize = 0;
while (_pool.TryDequeue(out var context))
{
context.ClearLease();
context.Dispose();
}
}
public virtual async ValueTask DisposeAsync()
{
_maxSize = 0;
while (_pool.TryDequeue(out var context))
{
context.ClearLease();
await context.DisposeAsync().ConfigureAwait(false);
}
}
}
还是学到了不少东西,比如使用表达式树创建委托对象,在缓存委托对象,而不是通过反射的方式创建(性能不佳).还有就是使用原子操作判断队列的数量,是否达到最大值,使用ConncurentQueue队列而不是使用Queue等.
秋风
2021-05-27