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

之前我写了两篇有关在ASP.NET Core中运行异步任务的博文,本篇博文是对之前两篇博文中演示示例和实现方法的简短跟进。

你可以通过以下链接查看之前的博文。

启动任务的例子

在之前博客中,我收到的最常见的反馈是关于我在描述问题时使用的例子。在我最初的博客中,我列举了3种可能场景,在这3种场景中,你希望在ASP.NET Core应用启动时运行一些异步任务。

  • 检查强类型配置是否合法
  • 使用数据库或者API填充缓存
  • 运行数据库迁移

对于前两种场景,没有任何问题,但是对于数据库迁移,一些博友提出了一些疑问。其实在两篇博文中我一直都反复说明,数据库迁移作为启动任务不是一个很好的方案,这里我只是想用它作为一个说明如何在ASP.NET Core程序启动时运行异步任务的例子。现在来看,当时使用这个例子是非常失败的。

数据库迁移是一个糟糕的选择

那么为什么在ASP.NET Core应用启动时,运行数据库迁移任务会是一个问题呢?毕竟,在应用程序开始处理请求之前,你肯定要完成数据库迁移!

其实这里其实有3个问题:

  1. 数据库迁移是应该是单线程的
  2. 迁移数据库需要更多的权限
  3. 开发人员不太喜欢直接运行数据库迁移

下面我们依次说明一下。

数据库迁移应该是单线程的

扩展一个Web应用最常用的方式之一是进行横向扩展,启动多个运行实例并使用负载均衡分发请求

这种Web集群扩展的方案是非常有效的,特别是当当前应用是无状态的(请求被分发到各个应用程序中,如果一个应用程序崩溃,其他的Web应用实例依然可以处理请求)。

但是不幸的是,如果尝试将数据库迁移作为应用启动任务,你很可能就会遇到问题。如果有超过1个以上的实例同时启动,多个数据库迁移任务将同时运行。

虽然并不能保证你一定会遇到麻烦,但除非你非常小心地确保幂等更新和错误处理,否则你很可能会陷入困境。

你肯定不希望使用这种方法,因为它可能产生的数据库完整性问题。 这里一个更好的选择是先启动单个实例完成迁移操作。 这样数据库迁移任务变成一个单线程任务,自然也就避开最严重的危险。

这种方法比将数据库迁移作为启动任务运行更有意义,但它更安全,更容易实现。

当然,这不是唯一的选择。 如果你对启动任务迁移的想法有所了解,那么你可以使用分布式锁来确保只有一个应用程序实例来运行迁移脚本。 然而,这并没有解决第二个问题......

迁移通常需要更多的权限

通常来说,最佳实践是你必须限制你的应用程序,以便他们只有权访问和修改所需的资源。 如果报表应用只需要读取销售数据,那么它应该无法修改它们,或者更改数据库表结构! 为指定的连接字符串配置可操作的权限,可以防止在的的应用出现安全问题时产生大量影响。

如果你正在使用Web应用程序本身来运行数据库迁移,那么该Web应用程序自然需要数据库权限才能执行高风险活动,例如修改数据库表结构,更改权限或更新/删除数据。 你真的希望您的Web应用程序能够删除你的学生表吗?

同样,你可以使用一些特定的实现,例如,与正常的数据库访问相比,使用不同的连接字符串进行迁移。 但是,如果使用外部迁移过程,你根本无法锁定Web应用程序进程。

开发人员不习惯直接运行EF Core迁移

这是一个不那么明显的观点,但是很多人表示在生产环境中使用EF Core迁移工具可能不是一个好主意。

就个人而言,我已经有1年多没有在生产环境中使用EF Core迁移了,到目前为止迁移工具肯定已经有所改善。 话虽如此,我仍然看到一些问题:

就我自己而言,我经常使用DbUp和FluentMigrator,而不会使用EF Core迁移。我发现它们都运行良好。

因此,如果数据库迁移任务不适合应用启动任务示例,那么哪些任务才是比较适合的呢?

比较适合作为启动任务的一些例子

虽然在之前的博文中我已经反复提到了一些例子,但我还是将在下面再次描述它们。这里其他博友还给我一些有趣的补充。

  • 检查强类型配置是否有效。ASP.NET Core 2.2引入了配置验证,但它只在首次访问IOptions 类时执行此操作。 正如我在之前文章中所描述的那样,你可能希望在应用启动时进行验证,以确保你的环境和配置有效。
  • 填充缓存。 你的应用程序可能需要来自文件系统或远程服务的数据,它只需要加载一次,但加载需要耗费相当多的资源,所以在应用程序启动之前加载此数据可减少请求延迟。
  • 预连接到数据库和/或外部服务。 以类似的方式,你可以通过连接到数据库或其他外部服务来填充数据库连接池。 这些通常是相对昂贵的操作,因此是很好的用例。
  • 预编译加载应用中所有的单例服务。我认为这个一个非常有趣的想法,通过预加载依赖注入容器中注册的单例服务,你可以减少请求的响应时间。

使用健康检查来完成启动任务

我完全同意有关数据库迁移的反馈。 当这不是一个好主意时,将它们用作启动任务的示例有点误导,特别是因为我个人不使用我所描述的方法!

然而,很多人都同意我所描述的另外一种方法 - 在启动Kestrel服务器和处理请求之前运行启动任务。

这里Damian Hickey针对这个方案提出了一个问题,他建议尽快启动Kestrel服务器。 他建议在所有启动任务完成后,使用运行健康检查向负载均衡器发出信号,表明应用程序已准备好开始接收请求。 与此同时,所有非健康检查流量(如果负载均衡器正在执行此任务,则不应该有任何流量)将收到503服务不可用。

这种方法的主要好处是它可以避免网络超时。 一般来说,应用程序最好能尽快的针对请求返回错误代码,而不是根本不响应请求,并导致客户端超时。 这减少了客户端所需的资源数量。 通过较早启动Kestrel服务器,应用程序可以更早地开始响应请求,即使响应是“未就绪”响应。

这实际上与我在第一篇文章中描述的方法非常相似,但是我没有选用它了,因为它太复杂了,而且没有达到我设定的目标。 从技术上来说,在那篇文章中,我只是关注在Kestrel服务器启动之前运行任务的方法,健康检查方法不能完成这个功能。

然而,Damian提出的问题让我再次思考。 在我下一篇博客中,我将描述如何使用ASP.NET Core 2.2的健康检查功能向负载均衡器发送信号,表明应用程序已经完成了所有启动任务。

总结

在这篇文章中,我分享了我之前关于在ASP.NET Core应用程序启动时运行异步任务的一些反馈。 这里最大的问题是我选择使用EF Core数据库迁移作为启动任务的示例。 数据库迁移不适合在应用程序启动时运行,因为它们通常需要由单个进程运行,并且需要比更多的数据库权限。

我提供了一些比较适合作为的启动任务的场景,并且描述了Damian给出的建议 - 尽快启动Kestrel服务器,并使用运行状况检查来指示任务何时完成。 我将在下一篇文章中描述如何实现这一功能。

如何在ASP.NET Core程序启动时运行异步任务(3)的更多相关文章

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

    原文:Running async tasks on app startup in ASP.NET Core (Part 2) 作者:Andrew Lock 译者:Lamond Lu 在我的上一篇博客中 ...

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

    原文:Running async tasks on app startup in ASP.NET Core (Part 1) 作者:Andrew Lock 译者:Lamond Lu 背景 当我们做项目 ...

  3. 在 ASP.NET Core 程序启动前运行你的代码

    一.前言 在进行 Web 项目开发的过程中,可能会存在一些需要经常访问的静态数据,针对这种在程序运行过程中可能几乎不会发生变化的数据,我们可以尝试在程序运行前写入到缓存中,这样在系统后续使用时就可以直 ...

  4. 探索ASP.Net Core 3.0系列四:在ASP.NET Core 3.0的应用中启动时运行异步任务

    前言:在本文中,我将介绍ASP.NET Core 3.0 WebHost的微小更改如何使使用IHostedService在应用程序启动时更轻松地运行异步任务. 翻译 :Andrew Lock   ht ...

  5. ASP.NET Core 的启动和运行机制

    目录 ASP .NET Core 的运行机制 ASP .NET Core 的启动 ASP .NET Core 的管道和中间件 参考 ASP .NET Core 的运行机制 Web Server: AS ...

  6. 如何在ASP.NET Core中使用JSON Patch

    原文: JSON Patch With ASP.NET Core 作者:.NET Core Tutorials 译文:如何在ASP.NET Core中使用JSON Patch 地址:https://w ...

  7. 如何在ASP.NET Core中自定义Azure Storage File Provider

    文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...

  8. Asp.Net Core 程序部署到Linux(centos)生产环境(二):docker部署

    运行环境 照例,先亮环境:软件的话我这里假设你已经批准好了.net core 运行环境,未配置可以看我的这篇[linux(centos)搭建.net core 运行环境] 腾讯云 centos:7.2 ...

  9. Asp.Net Core 程序部署到Linux(centos)生产环境(一):普通部署

    运行环境 照例,先亮底 centos:7.2 cpu:1核 2G内存 1M带宽 辅助工具:xshell xftp 搭建.net core运行环境 .net core 的运行环境我单独写了一篇,请看我的 ...

随机推荐

  1. storyboard中的三种传值

    三种传值:属性传值 block传值 以及 代理传值 (这里我用前面的页面和后面的)来表示两个控制器:LoginViewController和RegisterViewController 建立两个控制器 ...

  2. sublime 关闭自动更新

    第一步: 点击菜单栏“Preferences”=> "Settings-User" 进入个人参数设置页面: 第二步: 在大括号内插入如下代码:"update_che ...

  3. cocos2dx JAVA,C++互相调用函数

    C++调用JAVA 例子 #if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID) #include "platform/android/jni/Jni ...

  4. Floyd算法应用-医院选址问题

    1)问题描述 n个村庄之间的交通图可以用有向网图来表示,图中边<vi, vj>上的权值表示从村庄i到村庄j的道路长度.现在要从这n个村庄中选择一个村庄新建一所医院,问这所医院应建在哪个村庄 ...

  5. setInterval setTimeout 详解

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...

  6. 201621123037 《Java学习设计》 第五周学习总结

    Week05-继承.多态.抽象类与接口 1. 本周学习总结 1.1 写出你认为本周学习中比较重要的知识点关键词 关键词:接口."has-a".多态.comparable.Compa ...

  7. 某DP题目4

    题意 有两个栈分别有n和m个数,每次从任意栈中取出一个数,令k为不同输出序列的总数,其中第i种输出序列的产生方式有ai个,求Σai2. n <= 500 分析 此题是关于ai2转换.咋一看此题好 ...

  8. ASP.NET总结

    ASP.NET已经学习完.学牛腩的时候面对一些控件和方法会用,但对当中的原理还不懂.学习这部分的内容时, 从头到尾都有一种相识的感觉,把之前一些不懂得地方也理解了,每个知识都有相应的样例练习,学起来还 ...

  9. EditText监听方法,实时的判断输入多少字符

    最近在写一个小项目,其中有一点用到了显示EditText中输入了多少个字符,像微博中显示剩余多少字符的功能.在EditText提供了一个方法addTextChangedListener实现对输入文本的 ...

  10. 前端(css引入的3中方式)

    一.css引入的三种方式 行间式 在标签头部的style属性内 属性值满足的是css语法 属性值用key:value形式赋值,value具有单位 属性值之间用;隔开 外联式(企业开发中使用这种方式) ...