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接口的实现。)

Net6 DI源码分析Part5 在Kestrel内Di Scope生命周期是如何根据请求走的?的更多相关文章

  1. MyBatis源码分析(5)——内置DataSource实现

    @(MyBatis)[DataSource] MyBatis源码分析(5)--内置DataSource实现 MyBatis内置了两个DataSource的实现:UnpooledDataSource,该 ...

  2. vue 源码详解(二): 组件生命周期初始化、事件系统初始化

    vue 源码详解(二): 组件生命周期初始化.事件系统初始化 上一篇文章 生成 Vue 实例前的准备工作 讲解了实例化前的准备工作, 接下来我们继续看, 我们调用 new Vue() 的时候, 其内部 ...

  3. Net6 DI源码分析Part4 CallSiteFactory ServiceCallSite

    Net6 CallSiteFactory ServiceCallSite, CallSiteChain abstract class ServiceCallSite ServiceCallSite是个 ...

  4. 小白都能看懂的 Spring 源码揭秘之依赖注入(DI)源码分析

    目录 前言 依赖注入的入口方法 依赖注入流程分析 AbstractBeanFactory#getBean AbstractBeanFactory#doGetBean AbstractAutowireC ...

  5. Handle源码分析,深入群内了解风骚的Handle机制

    Hanlder的使用方式一: private static Handler mHandler = new Handler(){ public void handleMessage(android.os ...

  6. Springboot源码分析之代理对象内嵌调用

    摘要: 关于这个话题可能最多的是@Async和@Transactional一起混用,我先解释一下什么是代理对象内嵌调用,指的是一个代理方法调用了同类的另一个代理方法.首先在这儿我要声明事务直接的嵌套调 ...

  7. Spark Streaming源码解读之流数据不断接收全生命周期彻底研究和思考

    本期内容 : 数据接收架构设计模式 数据接收源码彻底研究 一.Spark Streaming数据接收设计模式   Spark Streaming接收数据也相似MVC架构: 1. Mode相当于Rece ...

  8. Net6 DI源码分析Part2 Engine,ServiceProvider

    ServiceProvider ServiceProvider是对IServiceProvider实现,它有一个internal的访问修饰符描述的构造,并需要两个参数IServiceCollectio ...

  9. Net6 DI源码分析Part1 ServiceCollection、ServiceDescriptor、ServiceLifetime、IServiceProvider

    ServiceCollection.ServiceDescriptor.ServiceLifetime.IServiceProvider Microsoft.Extensions.Dependency ...

随机推荐

  1. 5G的到来

    通信改变未来,从古至今信息的传输和获取从来就没有缺少过,之所以谁能取得胜利就是谁掌握的资源多,其中信息资源尤为重要,只要获取到更多的信息你就能提前做出应对策略.因此未来一定是信息的未来,作为信息传输的 ...

  2. 一个不常遇到的HbuilderX自动化测试运行问题

    晚上10点左右,刚好说想研究一下uniapp项目中怎么进行自动测试,于是跟着插件的官方教程开始配置测试环境,写好了一个简单的测试脚本,然后图形化操作IDE运行测试,却报错了一大片错误信息,如下所示: ...

  3. VS Code 如何设置大小写转换快捷键

    一般情况下,快捷键如下: 转换为大写:Ctrl+Shift+u 转换为小写:Ctrl+Shift+l 如果不行的话,需要单独进行设置,步骤如下: 1.点击[文件]-[首选项]-[键盘快捷方式]菜单: ...

  4. 强强联袂!腾讯云TDSQL与国双战略签约,锚定国产数据库巨大市场

    日前,腾讯云计算(北京)有限责任公司与北京国双科技有限公司签署了<国产数据库产品战略合作协议>,双方将在数据库技术方面展开深度合作,通过分布式交易型数据库的联合研发.产品服务体系建设.品牌 ...

  5. 为什么操作dom会消耗性能

    因为对DOM的修改为影响网页的用户界面,重绘页面是一项昂贵的操作.太多的JavaScript DOM操作会导致一系列的重绘操作,为了确保执行结果的准确性,所有的修改操作是按顺序同步执行的.我们称这个过 ...

  6. 编写Java程序,读取文本文档的内容,去除文本中包含的“广告”字样,把更改后的内容保存到一个新的文本文档中

    查看本章节 查看作业目录 需求说明: 读取文本文档的内容,去除文本中包含的"广告"字样,把更改后的内容保存到一个新的文本文档中 实现思路: 在main() 方法中,使用 new F ...

  7. Linux上天之路(十五)之文件查找

    主要内容 精确查找 模糊查找 1. 精确查找 find - search for files in a directory hierarchy 递归地在层次目录中处理文件 查找方式: 按文件属性查找 ...

  8. Hive的分析函数的使用

    原文: https://www.toutiao.com/i6769120000578945544/?group_id=6769120000578945544 我们先准备数据库.表和数据 开窗分析函数相 ...

  9. lvgl移植—Linux fbdev&evdev(基于LVGL v7)

    虽然lvgl官方提供了有关linux framebuffer操作的库函数,但是我决定自己试一下能否自己实现这部分操作 实际项目中应优先采用官方库函数,官方实现代码位于文件夹lv_drivers/dis ...

  10. Source Insight 4安装图文教程(附链接)

    Source Insight 4安装图文教程,附激活文件直链 下载链接:http://naturalporters.vicp.io/uploads/si4_kgen_unis.zip 直链没了,就用下 ...