Net6 DI原始碼分析Part5 在Kestrel內Di Scope生命週期是如何根據請求走的?

一身大膘發表於2022-02-10

Net6 DI原始碼分析Part5 在Kestrel內Di Scope生命週期是如何根據請求走的?

在asp.net core中的DI生命週期有一個Scoped是根據請求走的,也就是說在處理一次請求時,Scope生命週期所提供的服務是同一個例項。它是用IServiceScope是實現的。但是我們要知道何時構建的IServiceScope以及IServiceScope何時被銷燬掉
先說結論IServiceScope是根據當前的RequestServicesFeature內作為_scope成員存在的,只要知道RequestServicesFeature何時建立,何時銷燬就瞭解整http request DI的生命週期

netcore中DI生命週期。

在net core 預設的DI中你直接build 出來的servicepProvider 用它去獲取的例項物件Scoped & Singleton 是具有相同生命週期的。
如果需要有Scoped生命週期的例項,你需要通過serviceProvider建立一個IServiceScope例項,然後通過該例項獲取到的服務例項會跟著IServiceScope一同銷燬從而達到Scoped生命週期。

DI生命週期案例 1
ServiceCollection serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<MySingletonClass>();
serviceCollection.AddScoped<MyScopedClass>();
serviceCollection.AddTransient<MyTransientClass >();
var servicepProvider = serviceCollection.BuildServiceProvider();
using (IServiceScope scope = servicepProvider.CreateScope(), scope2 = servicepProvider.CreateScope())
{
    var scopeObj1 = scope.ServiceProvider.GetService<MyScopedClass>();
    var scopeObj2 = scope2.ServiceProvider.GetService<MyScopedClass>();
    Console.WriteLine(object.ReferenceEquals(scopeObj1, scopeObj2)); //false
}
//Scoped Constructor
//Scoped Constructor
//False
//Scoped Dispose
//Scoped Dispose
Console.ReadLine();
RequestServicesFeature

為什麼說http request DI 的生命週期是根據RequestServicesFeature來的。通過DI生命週期案例瞭解到需要建立一個scope生命週期,要有一個IServiceScope例項。
那麼在http feature中RequestServicesFeature做了對該介面的封裝。也用於提供RequestSerivces服務。

  1. IServiceScope的建立: HttpContext獲取RequstServiceProivder是根據RequestServicesFeature.RequestServices。在RequestServices屬性的get方法內建立_scope。並返回給該scope對應的ServiceProvide供後續使用。
  2. IServiceScope的銷燬:在RequestServicesFeature.Dispose 方法內又呼叫了_scope屬性(IServiceScope)的同名方法從而進行銷燬由此提供的所有service。那麼只要銷燬了當前請求的RequestServicesFeature 例項就銷燬了。當前http requst 的scope生命週期的所有服務。
RequestServicesFeature 是何時被銷燬的。
  1. HttpProtocol.ProcessRequests 方法內呼叫await FireOnCompleted();
  2. FireOnCompleted內部會迴圈執行 Stack<KeyValuePair<Func<object, Task>, object>>? _onCompleted;堆疊委託
  3. requestServiceFeature.Dispose在此刻被呼叫,其內部Dispose了IServiceScope。自此ServiceScope生命週期結束。
RequestServicesFeature 簡化程式碼
public class RequestServicesFeature : IServiceProvidersFeature, IDisposable, IAsyncDisposable
{
    private IServiceScope? _scope;
    private readonly HttpContext _context;
 
    public RequestServicesFeature(HttpContext context, IServiceScopeFactory? scopeFactory)
    {
        _context = context;
        _scopeFactory = scopeFactory;
    }
 
    public IServiceProvider RequestServices
    {
        get
        {
            _context.Response.RegisterForDisposeAsync(this);
            _scope = _scopeFactory.CreateScope();
            _requestServices = _scope.ServiceProvider;
            return _requestServices!;
        }

    }
 
    /// <inheritdoc />
    public ValueTask DisposeAsync()
    {
        switch (_scope)
        {
            case IAsyncDisposable asyncDisposable:
                var vt = asyncDisposable.DisposeAsync();
                if (!vt.IsCompletedSuccessfully)
                {
                    return Awaited(this, vt);
                }
                vt.GetAwaiter().GetResult();
                break;
            case IDisposable disposable:
                disposable.Dispose();
                break;
        }
    }
}
HttpProtocol

該類是處理http協議的 ProcessRequests作為重要方法之一,用來使用我們構建好的IHttpApplication 處理request
httpontext的建立,以及我們編排好的http中介軟體管道都是在這裡被執行的。同時呼叫FireOnCompleted, RequestServicesFeature就是在這裡被銷燬的。

private async Task ProcessRequests<TContext>(IHttpApplication<TContext> application) where TContext : notnull
{
    while (_keepAlive)
    {
        var context = application.CreateContext(this);
        // Run the application code for this request
        await application.ProcessRequestAsync(context);
        if (_onCompleted?.Count > 0)
        {
            await FireOnCompleted();
        }
        application.DisposeContext(context, _applicationException);
    }
}

protected Task FireOnCompleted()
    {
        var onCompleted = _onCompleted;
        if (onCompleted?.Count > 0)
        {
            return ProcessEvents(this, onCompleted);
        }
 
        return Task.CompletedTask;
 
        static async Task ProcessEvents(HttpProtocol protocol, Stack<KeyValuePair<Func<object, Task>, object>> events)
        {
            while (events.TryPop(out var entry))
            {
                try
                {
                    await entry.Key.Invoke(entry.Value);
                }
                catch (Exception ex)
                {
                    protocol.Log.ApplicationError(protocol.ConnectionId, protocol.TraceIdentifier, ex);
                }
            }
        }
    }
RequestServicesFeature 是如何註冊到HttpProtocol _onCompleted;堆疊委託中的

HttpProtocol委託堆疊中 RequestServicesFeature 是什麼時候被註冊進去的呢?

  1. RequestServicesFeature.RequestServices屬性的Get方法呼叫了方法內有一句這樣的程式碼 _context.Response.RegisterForDisposeAsync(this);
  2. HttpResponse的RegisterForDisposeAsync呼叫了抽象方法abstract void OnCompleted。實現該方法的是在完成的DefaultHttpResponse
  3. DefaultHttpResponse的OnCompleted把委託註冊到HttpProtocol.OnCompleted方法註冊到_onCompleted中,實際程式碼體現為 HttpResponseFeature.OnCompleted(callback, state);這裡HttpResponseFeature.就是就是HttpProtocol,(HttpProtoccol是個IFeatureCollection介面的實現。)

相關文章