Dotnet Core IHttpClientFactory深度研究 (3)

最后,整个管道被包装在一个LifetimeTrackingHttpMessageHandler中。管道处理完成后,将与用于创建它的IServiceScope一起保存在一个新的ActiveHandlerTrackingEntry实例中,并给定HttpClientFactoryOptions中定义的生存期(默认为两分钟)。

该条目返回给调用者(CreateHandler()方法),添加到处理程序的ConcurrentDictionary<>中,添加到新的HttpClient实例中(在CreateClient()方法中),并返回给原始调用者。

在接下来的生存期(两分钟)内,每当您调用CreateClient()时,您将获得一个新的HttpClient实例,但是它具有与最初创建时相同的处理程序管道。

每个命名或类型化的HttpClient都有自己的消息处理程序管道。例如,名称为WangPlus的两个HttpClient实例将拥有相同的处理程序链,但名为api的HttpClient将拥有不同的处理程序链。

下一节,我们研究下计时器过期后的清理处理。

三、过期清理

以默认时间来说,两分钟后,存储在ActiveHandlerTrackingEntry中的计时器将过期,并触发StartExpiryTimer()的回调方法ExpiryTimer_Tick()。

ExpiryTimer_Tick负责从ConcurrentDictionary<>池中删除处理程序记录,并将其添加到过期处理程序队列中:

readonly ConcurrentQueue<ExpiredHandlerTrackingEntry> _expiredHandlers;

internal void ExpiryTimer_Tick(object state)
{
    var active = (ActiveHandlerTrackingEntry)state;

     _activeHandlers.TryRemove(active.Name, out Lazy<ActiveHandlerTrackingEntry> found);

    var expired = new ExpiredHandlerTrackingEntry(active);
    _expiredHandlers.Enqueue(expired);

    StartCleanupTimer();
}

当一个处理程序从_activeHandlers集合中删除后,当调用CreateClient()时,它将不再与新的HttpClient一起分发,但会保持在内存存,直到引用此处理程序的所有HttpClient实例全部被清除后,IHttpClientFactory才会最终释放这个处理程序管道。

IHttpClientFactory使用LifetimeTrackingHttpMessageHandler和ExpiredHandlerTrackingEntry来跟踪处理程序是否不再被引用。

看下面的代码:

internal class ExpiredHandlerTrackingEntry
{

    private readonly WeakReference _livenessTracker;

    public ExpiredHandlerTrackingEntry(ActiveHandlerTrackingEntry other)
    
{
        Name = other.Name;
        Scope = other.Scope;

        _livenessTracker = new WeakReference(other.Handler);
        InnerHandler = other.Handler.InnerHandler;
    }

    public bool CanDispose => !_livenessTracker.IsAlive;

    public HttpMessageHandler InnerHandler { get; }
    public string Name { get; }
    public IServiceScope Scope { get; }
}

根据这段代码,ExpiredHandlerTrackingEntry创建了对LifetimeTrackingHttpMessageHandler的弱引用。根据上一节所写的,LifetimeTrackingHttpMessageHandler是管道中的“最外层”处理程序,因此它是HttpClient直接引用的处理程序。

对LifetimeTrackingHttpMessageHandler使用WeakReference意味着对管道中最外层处理程序的直接引用只有在HttpClient中。一旦垃圾收集器收集了所有这些HttpClient,LifetimeTrackingHttpMessageHandler将没有引用,因此也将被释放。ExpiredHandlerTrackingEntry可以通过WeakReference.IsAlive检测到。

在将一个记录添加到_expiredHandlers队列之后,StartCleanupTimer()将启动一个计时器,该计时器将在10秒后触发。触发后调用CleanupTimer_Tick()方法,检查是否对处理程序的所有引用都已过期。如果是,处理程序和IServiceScope将被释放。如果没有,它们被添加回队列,清理计时器再次启动:

internal void CleanupTimer_Tick()
{
    StopCleanupTimer();

    int initialCount = _expiredHandlers.Count;
    for (int i = 0; i < initialCount; i++)
    {
        _expiredHandlers.TryDequeue(out ExpiredHandlerTrackingEntry entry);

        if (entry.CanDispose)
        {
            try
            {
                entry.InnerHandler.Dispose();
                entry.Scope?.Dispose();
            }
            catch (Exception ex)
            {
            }
        }
        else
        {
            _expiredHandlers.Enqueue(entry);
        }
    }

    if (_expiredHandlers.Count > 0)
    {
        StartCleanupTimer();
    }
}

为了看清代码的流程,这个代码我简单了。原始的代码中还有日志记录和线程锁相关的内容。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wppzpw.html