如何在ASP.NET Core程序启动时运行异步任务(1)

原文:Running async tasks on app startup in ASP.NET Core (Part 1)
作者:Andrew Lock
译者:Lamond Lu

640?wx_fmt=jpeg

背景

我们做项目的时候,有时候希望自己的ASP.NET Core应用在启动前执行一些初始化逻辑。例如,你希望验证配置是否合法,填充缓存数据,或者运行数据库迁移脚本。在本篇博客中,我将介绍几种可选的方案,并且通过展示一些简单的方法和扩展点来说明我想要解决的问题。

开始我将先描述一下ASP.NET Core内置的解决方案,使用IStartupFilter来运行同步任务。然后我将描述几种可选的执行异步任务的方案。你可以(但是可能不应该这样做)使用IStartupFilter或者IApplicationLifetime事件来执行异步任务。你也可以使用IHostService接口来运行一次性任务且不会阻塞ASP.NET Core应用启动。最后唯一合理的方案是在program.cs文件中手动运行任务。在下一篇博客中,我会展示一个可以简化这个流程的推荐方案。

为什么我们需要在程序启动时运行异步任务?

在程序启动,开始监听请求之前,运行一些初始化代码是非常普遍的。对于一个ASP.NET Core应用程序,启动前有许多任务需要运行,例如:

  • 确定当前的托管环境

  • 从appsetting.json文件和环境变量中读取配置

  • 配置依赖注入容器

  • 构建依赖注入容器

  • 配置中间件管道

以上几步都四发生在应用程序引导时。然而有些一次性任务需要在WebHost启动,监听请求前运行。例如

  • 检查强类型配置是否合法

  • 使用数据库或者API填充缓存

  • 运行数据库迁移脚本(这通常不是一个很好的方案,但是对于一些应用来说够用了)

有些时候,一些任务并不是非要在程序启动,监听请求前运行。这里我们以填充缓存为例,如果它是设计的比较好的话,在程序启动前是否填充缓存数据是无关紧要的。但是,相对的,你肯定也希望在应用程序开始监听请求之前,迁移你的数据库!

其实ASP.NET Core框架自己也需要运行一些一次性初始化任务。这个最好的例子就是数据保护,它常用来做数据加密,这个模块必须要在应用启动前初始化。为了实现初始化,它们使用了IStartupFilter

使用IStartupFilter来运行同步任务

在之前的博客中,我已经介绍过IStartupFilter, 它是一个自定义ASP.NET Core应用的强力接口。

  • 探索ASP.NET Core中的IStartupFilter

  • 如何为ASP.NET Core的强类型配置对象添加验证

如果你是第一次接触Filter, 我建议你去我之前的博客,这里我只会提供一个简短的总结。

IStartupFilter会在配置中间件管道的进程中被执行(通常在Startup.Configure()中完成)。它们允许你通过插入额外的中间件,分叉或执行任何其他操作来自定义应用程序实际创建的中间件管道。例如下面代码展示的AutoRequestServiceStartupFilter

640?wx_fmt=png

这非常有用,但它与ASP.NET Core应用程序启动时运行一次性任务有什么关系呢?

IStartupFilter的主要功能是为开发人员提供了一个钩子(hook), 这个钩子触发的时机是在在应用程序配置完成并配置依赖注入容器之后,应用程序启动之前。这意味着,你可以在实现IStartupFilter的类中使用依赖注入,这样你就可以在这里完成许多希望在应用程序启用前需要运行的任务。以ASP.NET Core内置的DataProtectionStartupFilter为例,它会在程序启用前初始化整个数据保护模块。

IStartupFilter提供的另外一个重要功能就是,它允许你通过向依赖注入容器注册服务来添加要执行的任务。这意味着如果你自己编写了一个Library, 你可以在应用程序启动时注册一个任务,而不需要应用程序显式调用它。

问题是IStartupFilter基本上是同步的。Configure方法的返回值不是Task,因此我们只能使用同步方式执行异步任务,这显然不是好的实现方案。 我稍后会讨论这个,但现在让我们先跳过它。

为什么不用健康检查?

ASP.NET Core 2.2中加入了一个新的健康检查功能,它通过暴露一个HTTP节点,让你可以查询当前应用的健康状态。当应用部署之后,像Kubernetes这样的编排引擎或HAProxy和NGINX等反向代理可以查询此HTTP节点以检查你应用是否已准备好开始接收请求。

你可以使用健康检查功能来确保你的应用程序不会开始处理请求,直到所有必需的一次性初始化任务完成为止。然而,这有一些缺点:

  • WebHost和Kestrel本身将在执行一次性初始化任务之前启动,虽然他们不会收到可能存在问题的“真实”请求(仅健康检查请求)。

  • 这种方式会引入了额外的复杂度,除了添加运行一次性任务的代码之外,还需要添加运行状况检查以测试任务是否完成,并同步任务的状态。

  • 应用程序的启动会有延迟,因为需要等待所有任务完成,所以不太可能减少启动时间。

  • 如果任务失败,应用程序不会终止,而且健康检查也永远不会通过。这可能是可以接受的,但是我个人更喜欢让应用程序立刻终止。

  • 使用健康检查,并不能知道一次性任务运行的怎么样,你只能了解到任务是否完成。

在我看来,健康检查并不适合一次性任务的场景,他们可能对我描述的一些例子很有用,但我不认为它适用于所有情况。我真的希望能在WebHost启动之前,运行一些一次性任务。

运行异步任务

我已经花了很长的篇幅来讨论了所有不能完成我的目标的所有方法,那么哪些才是可行的方案!在这一节中,我将描述几种运行异步任务的方案(即方法返回Task, 并且需要等待的),其中有一些较好的方案,也有一些需要规避的方案。

这里为了更清楚的描述这些方案,我选用数据库迁移作为例子。在EF Core中,你可以在运行时调用myDbContext.Database.MigrateAsync()来迁移数据库,其中myDbContext是当前应用程序的数据库上下文实例。

EF还提供了一个同步的数据库迁移方法Database.Migrate(),但是这里我们不需要使用它。

使用IStartupFilter

我之前描述过如何使用IStartupFilter在应用程序启动时运行同步任务。 不过,这里为了异步方法,我们使用了GetAwaiter()GetResult()阻塞了线程, 将异步方法变成了一个同步方法。

警告:这是一种非常不好的异步实践方式

640?wx_fmt=png


这段代码可能不会引起任何问题,它会在应用程序启动且未开始监听请求时运行,所以不太可能出现死锁。但是坦率的说,我会尽可能不用这种方式。

使用IApplicationLifetime 事件

我之前还没有讨论过和这个事件相关的内容,但是当你的应用程序启动和关闭前,你可以使用IApplicationLifetime接口接收到通知。这里我不会详细介绍它,因为使用它来实现我们的目的会有一些问题。

IApplicationLifetime使用CancellationTokens来注册回调,这意味着你只能同步执行回调。 这实际上意味着无论你做什么,你都会遇到同步异步模式。
ApplicationStarted事件仅在WebHost启动后触发,因此任务在应用程序开始接受请求后运行。
鉴于他们没有解决
IStartupFilter使用同步方式处理异步任务的问题,也没有阻止应用启动,所以我只是将它列出来仅供参考。

使用IHostedService运行异步事件

IHostService允许在ASP.NET Core应用程序生命周期内,以后台程序的方式执行长时间运行的任务。它有许多不同的用途,你可以使用它在计数器上运行定期任务,或者监听RabbitMQ消息。在ASP.NET Core 3.0中, Web Host也可能是使用IHostService构建的。

IHostService本质上是异步的,他提供了StartAsyncStopAsync方法。这对我们来说非常的有用,它再在是使用同步方式处理异步任务了。使用IHostService,我们的数据库迁移任务可以变成一个托管服务。

640?wx_fmt=png


不幸的是,IHostedService并不是我们希望的灵丹妙药。 它允许我们编写真正的异步代码,但它有几个问题:

  • IHostService的典型实现期望StartAsync方法能够相对快速返回。对于后台任务来说,它希望你能够以异步分当时启动服务,但是大多数任务都是在启动代码之外。迁移数据库的任务会阻止其他IHostService启动(这里我不太理解作者的意思,只是按字面意思翻译,后续会更新这里)。

  • 第二个问题是最大的问题,你的应用程序会在IHostService运行数据库迁移之前开始接受请求,这显然不是我们想要的。

Program.cs中手动运行任务

到现在为止,我们都没有提供一种完善的解决方案,他们或者是使用同步方式处理异步任务,或者是不能阻止程序启动。

现在让我们停止尝试使用框架机制,手动来完成工作。

ASP.NET Core模板中使用的默认Program.csMain函数的一个语句中构建并运行IWebHost

640?wx_fmt=png

这里你可能会发现在Build()方法之后, Run()方法之前,你可以添加一些自定义的代码,再加上C# 7.1中允许使用异步方式运行Main方法,所以这里我们有了一个合理的方案。

640?wx_fmt=png


这个方案有以下优点:

  • 我们使用的是真正的异步,而不是使用同步方式处理异步任务

  • 我们可以使用异步方式运行任务

  • 只有当我们的异步任务都完成之后,WebHost才会启动

  • 在这个时间点,依赖注入容易已经构建完成,我们可以使用它来创建服务

但是这种方法也存在一些问题:

  • 即使依赖注入容器构建完成,但是中间件管道却还没有完成构建。只有当你调用Run()或者RunAsync()方法之后,中间件管道才开始构建。当构建中间件管道时,IStartupFilter才会被执行,然后程序启动。如果你的异步任务需要在以上任何步骤中配置,那你就不走运了。

  • 我们失去了通过向依赖注入容器添加服务来自动运行任务的能力。 我们只能手动运行任务。

如果这些问题都不是问题,那么我认为这个最终选项提供了解决问题的最佳方案。 在我的下一篇文章中,我将展示一些方法,我们可以在这个例子的基础上构建,以使某些内容更容易使用。

总结

在这篇文章中,我讨论了在ASP.NET Core应用程序启动时执行异步运行任务的必要性。 我描述了这样做的一些问题和挑战。 对于同步任务,IStartupFilter为ASP.NET Core应用程序启动过程提供了一个有用的钩子,但是需要使用同步方式运行异步任务,这通常是一个坏主意。 我描述了运行异步任务的一些可能的选项,我发现其中最好的是在Program.cs中“手动”运行任务。 在下一篇文章中,我将介绍一些代码,使这个模式更容易使用。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/317383.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

扫描线讲解

参考文章: 线段树扫描线(有关扫描线的理解) 线段树扫描线(基本原理) 扫描线 第二个文章里面的图很生动: 我总结一下就是:将所给图形的横坐标全部记录,纵坐标记录为扫描线 然后对与…

梁迪:我为MVP骄傲,《微软最有价值专家奖励计划介绍》附专题视频

题记:有些事情,比 MVP 更加不朽,浩气长空,日月星汉,我们为 MVP 和那些心目中的“MVP”感到骄傲。微软 MVP 是一种追求,不必要去强求,但 MVP 必定是俱乐部发展的根基础。火车跑得快全凭车头带&am…

P3295 [SCOI2016]萌萌哒(DP+倍增)

P3295 [SCOI2016]萌萌哒 description solution 强制部分区间相同,很容易就想到了并查集,直接暴力并查集合并是O(n2)O(n^2)O(n2)的 只需要考虑那一个数据结构将其转化成O(nlog⁡n)O(n\log n)O(nlogn)的 树之类的就不考虑了,一段一段的区间…

【恭贺新春】2019年春节放假

2019年放假通知致全体微友: 2019年2月5日(正月初一)至2月8日(正月初四)春节放假,共4天,小编停止更新公众号信息。敬请相互转告。值此新春佳节到来之际,“dotNet跨平台”给大家拜个…

C - Insertion Sort Gym - 101955C

C - Insertion Sort Gym - 101955C 题意: t组数据,每组数据给你n,k,q,让你求存在多少合法的1~n排列 合法要求: 对排列的前k项进行排序,使得整个序列中最长的递增子序列长度为n-1 题解&#x…

华为云.NET Core支持情况调查

各大公有云都提供了开发者开发的SDK,今天我们来看看华为云对.NET Core的支持情况怎么样? .NET SDK地址 https://developer.huaweicloud.com/sdk#.NET华为云的.NET SDK相比其他语言少的可伶,而且这几个SDK还不支持.NET Core。SDK的支持实在太差…

[2021-07-19 内测NOIP] 操作(状压DP),异或(字典树),等级(线段树),矩阵(DP)

[2021-07-19 内测] NOIP操作descriptionsolutioncode异或descriptionsolutioncode等级descriptionsolutioncode矩阵descriptionsolutioncode操作 description 有n堆石子,每堆石子都有一定的数量,第i堆石子的数量用Ai表示。 任意两堆石子均可合并&…

.NET和Java之争

这几天连续有多篇文章诋毁.NET,这类文章我十几年前就看得多了,只不过十几年前是C和C之争,C和Java之争。我从来不理这类文章,因为这类口水战并没有什么实际意义。然而接连收到多位粉丝私聊说,主席,你应该写点…

线性代数一之矩阵转向量随机化求解——神奇的矩阵(BZOJ)+向量内积

向量随机化神奇的矩阵descriptionsolutioncode[NOI2013]向量内积descriptionsolutioncode矩阵既可以看成是一张数位表,也可以看成是若干个行向量或者若干个列向量的向量表神奇的矩阵 description solution 暴力做A∗BA*BA∗B会达到n3n^3n3的复杂度,难…

潘淳:国士无双《微软技术俱乐部(苏州)成立大会暨微软技术交流会介绍》附专题视频...

题记:凡我赶不上的,我就在未来等他,随时等待捕捉那个趋势的的到来。2019年立一个FLAG,加入一个成长性组织,一个能让我学习使我成长的平台。2019年你和我都将亲历这个组织的到来,一个属于苏州程序员自己的大…

ASP.NET Core中借助CSRedis实现安全高效的分布式锁

引言最近回头看了看开发的.NET Core 2.1项目的复盘总结,其中在多处用到Redis实现的分布式锁,虽然在OnResultExecuting方法中做了防止死锁的处理,但在某些场景下还是会发生死锁的问题,下面我只展示部分代码:问题&#x…

L Machining Disc Rotors

L Machining Disc Rotors 题意: 圆心为(0,0)半径为R的圆,现在被被n个互不相交的圆切割(圆心和半径会给出),保证这n个彼此之间不会交叉,保证n个圆中不会有某个包含整个大圆的情况。问切割后大圆剩余部分的直径(即两点…

线性代数二之矩阵加速DP——数学作业,Arc of Dream

矩阵加速数学作业descriptionsolutioncodeArc of Dreamdescriptionsolutioncode数学作业 description solution dpdpdp状态转移方程,dpidpi−1∗10lenii(modM)dp_{i}dp_{i-1}*10^{len_i}i\pmod Mdpi​dpi−1​∗10leni​i(modM) nnn巨大,分段矩阵加速 …

Shadow Properties之美(二)【Microsoft Entity Framework Core随笔】

接着上一篇Shadow Properties之美(一),我们来继续举一个有点啰嗦的栗子。先看简单需求:某HR系统,需要记录员工资料。需要记录的资料有:员工号(规则:分公司所在城市拼音首字母&#x…

线性代数三之状压DP的矩阵加速——Quad Tiling,Bus公交线路

状压与矩阵加速的藕断丝连Quad Tilingdescriptionsolutioncode[Hnoi2010]Bus 公交线路descriptionsolutioncodeQuad Tiling description solution 设dpi,S:dp_{i,S}:dpi,S​: iii列的状态为SSS的方案数&#xff0c;最后答案为dpn,(1<<4)−1dp_{n,(1<<4)-1}dpn,(…

如何在ASP.NET Core程序启动时运行异步任务(2)

原文&#xff1a;Running async tasks on app startup in ASP.NET Core (Part 2)作者&#xff1a;Andrew Lock译者&#xff1a;Lamond Lu在我的上一篇博客中&#xff0c;我介绍了如何在ASP.NET Core应用程序启动时运行一些一次性异步任务。本篇博客将继续讨论上一篇的内容&…

线性代数四之动态DP(广义矩阵加速)——Can you answer these queries III,保卫王国

动态DP——广义矩阵加速SP1716 GSS3 - Can you answer these queries IIIdescriptionsolutioncode[NOIP2018 提高组] 保卫王国descriptionsolutioncode动态DP能矩阵加速要满足外层操作符对内层操作符具有分配率加法对于乘法就具有分配率(ab)*ca*cb*c SP1716 GSS3 - Can you a…

.Net Core跨平台应用研究-HelloArm(串口篇)

引言为了验证采用dotnet core技术开发的物联网设备数据采集接入服务应用是否能在高性价比的linux嵌入式平台运行&#xff0c;针对dotnet core应用程序进行嵌入式linux环境的发布部署运行验证研究。硬件环境硬件系统经过对比筛选&#xff0c;选用了友善之臂出品的NanoPC-T3 Plus…

[数论系列一]C Looooops,跳跳棋,The Luckiest number,CF906D Power Tower,Minimal Power of Prime,仪仗队,LCMSUM

文章目录C Looooopsdescriptionsolutioncode跳跳棋descriptionsolutioncodeThe Luckiest numberdescriptionsolutioncodeCF906D Power TowerdescriptionsolutioncodeMinimal Power of Primedescriptionsolutioncode[SDOI2008]仪仗队descriptionsolutioncodeLCMSUMdescriptionso…

.NET Core使用微软AI认知服务识别文字语言

点击上方蓝字关注“汪宇杰博客”识别一段文字的语言有多种途径&#xff0c;在这个以AI为热点的时代&#xff0c;我们也可以给自己的应用强行加上AI&#xff0c;然后就能加上“智慧”的名字“自主研发成功”后去吹牛逼。今天我带大家来看看如何使用微软智慧云Azure提供的AI认知服…