【问题标题】:Is smooth deployment possible with componentized ASP.NET MVC apps?组件化的 ASP.NET MVC 应用程序能否顺利部署?
【发布时间】:2011-05-30 02:29:29
【问题描述】:

这可能不是 MVC 特定的,它也可能适用于 ASP.NET WebForms,但到目前为止我们一直在 MVC2 上体验它。

每当我们开始使用 MSDeploy 进行远程部署时,我们都会收到一个简短的(5-6 秒)“服务器错误”页面来处理我们的请求,直到进行新的部署。这是错误文本:

“/”应用程序中的服务器错误。

无法加载文件或程序集 'Some.Assembly' 或其之一 依赖关系。进程不能 访问该文件,因为它正在 被另一个进程使用。 (例外 从 HRESULT: 0x80070020)

描述:未处理的异常 在执行过程中发生 当前的网络请求。请查看 堆栈跟踪以获取有关的更多信息 错误及其起源 代码。

异常详情: System.IO.FileLoadException:不能 加载文件或程序集“Some.Assembly” 或其依赖项之一。这 进程无法访问该文件,因为 它正被另一个进程使用。 (HRESULT 异常:0x80070020)

版本信息:Microsoft .NET 框架版本:4.0.30319; ASP.NET 版本:4.0.30319.1

这是错误页面中显示的堆栈跟踪:

[FileLoadException: Could not load file or assembly 'Some.Assembly' or one of its dependencies. The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)]
   System.Reflection.RuntimeAssembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +0
   System.Reflection.RuntimeAssembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, RuntimeAssembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection, Boolean suppressSecurityChecks) +39
   System.Reflection.RuntimeAssembly.InternalLoadAssemblyName(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection, Boolean suppressSecurityChecks) +132
   System.Reflection.RuntimeAssembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +144
   System.Reflection.Assembly.Load(String assemblyString) +28
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +46

[ConfigurationErrorsException: Could not load file or assembly 'Some.Assembly' or one of its dependencies. The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)]
   System.Web.Configuration.CompilationSection.LoadAssemblyHelper(String assemblyName, Boolean starDirective) +618
   System.Web.Configuration.CompilationSection.LoadAllAssembliesFromAppDomainBinDirectory() +209
   System.Web.Configuration.CompilationSection.LoadAssembly(AssemblyInfo ai) +130
   System.Web.Compilation.BuildManager.GetReferencedAssemblies(CompilationSection compConfig) +178
   System.Web.Compilation.BuildManager.GetPreStartInitMethodsFromReferencedAssemblies() +94
   System.Web.Compilation.BuildManager.CallPreStartInitMethods() +332
   System.Web.Hosting.HostingEnvironment.Initialize(ApplicationManager appManager, IApplicationHost appHost, IConfigMapPathFactory configMapPathFactory, HostingEnvironmentParameters hostingParameters, PolicyLevel policyLevel, Exception appDomainCreationException) +591

[HttpException (0x80004005): Could not load file or assembly 'Some.Assembly' or one of its dependencies. The process cannot access the file because it is being used by another process. (Exception from HRESULT: 0x80070020)]
   System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +8950644
   System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +97
   System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +256

导致该故障的程序集 (Some.Assembly) 不是实际的 Web 程序集,而是它的“参考”下的其他组件之一,用 .snk 签名。

5-6 秒后,网站启动,错误消失。

这当然不是我们想要的行为。我想知道我们在将组件连接在一起方面是否做错了什么。是否应该有另一种方法来确保顺利部署?或者这可能是 MVC2 本身的错误?

附: IIS 重叠旋转已启用,无论如何都是默认设置。

以下是组件:

  • A.dll
  • Some.Assembly.dll(
  • B.dll(取决于 A.dll 和 Some.Assembly.dll)
  • Web.dll(这是 Web 应用程序,取决于以上所有内容)

所有依赖项都是使用启用Copy Local 的常规程序集引用建立的。所有 dll 都是作为单独项目的解决方案的一部分。

MSDeploy 仅部署二进制文件,而不是源代码。

【问题讨论】:

  • 展示您如何使网站组成部分会很有帮助。
  • 所以你不在运行时加载组件?

标签: asp.net asp.net-mvc iis-7


【解决方案1】:

重叠轮换仅适用于池回收,并非旨在促进您描述或要求的部署类型。启用重叠轮换后,池的“传出”工作进程中的现有请求将被允许完成,而新请求将被发送到创建的新工作进程。这是一种优雅地移交给新工作进程的机制,而无需从现有请求下方拉扯地毯。就是这样。

影子复制文件夹用于此目的:

What is the “Temporary ASP.NET Files” folder for? (My Answer)

同样,它们并非旨在提供一种机制,以便在您上传新站点时保持整个现有代码库运行。

当您部署 ASP.NET 应用程序时,站点出现异常。在您复制程序集时,这些文件将被处理上传的任何进程(WebDAV 或 FTP)锁定(可能完全锁定)。

这会导致您观察到的异常,并且很可能是因为卷影复制机制在写入新程序集之前无法读取它们(并且 WebDAV 或 FTP 删除了写锁定)。

此外,如果预期的方法签名已更改或被删除,则在上传正确的页面之前,任何依赖于这些程序集的页面都可能无法(影子)编译。或者,如果首先上传的页面/视图依赖于尚未部署的程序集中的功能,您也会收到错误。

在部署最后一个文件之前,整个站点将处于不一致状态。

IIS 中没有内置机制来确保“原子”部署,即加载所有内容然后切换到运行它。 IIS 将继续为站点提供请求,而 ASP.NET 仍将在您上传应用程序时继续检测文件更改。

在不产生这些错误的情况下部署应用程序的唯一方法是在部署之前启用一个名为 App_Offline.htm 的特殊页面,然后在部署之后重命名或删除:

App_Offline.htm - Scott Guthrie
App_Offline.htm and working around the "IE Friendly Errors" feature

您可能还会发现这篇文章很有用:

How to: Prepare to Deploy a Web Project

还有一个稍微复杂的替代方案,它涉及到两个文件夹,例如:

d:\websites\site\www-A d:\websites\site\www-B

运行站点指向d:\websites\site\www-A,同时你部署到d:\websites\site\www-B。准备好后,将站点切换到 d:\websites\site\www-B 文件夹。

当您开始部署下一个构建时,您将部署到 d:\websites\site\www-A 并在您满意时切换到该位置。

缺点是您需要保持警惕并记住哪个文件夹是哪个。

此外,任何用户上传的内容都需要在两个文件夹之间同步(尽管您可以将第三个文件夹映射到虚拟目录中以存储类似的内容)。

【讨论】:

  • 如此详尽且写得很好的答案。仍然不是我想要的理想解决方案,但绝对超出我对 SO 的预期:) 谢谢。
  • @ssg - 不知道你是否读过 Rob Conery 的博客。他对 ASP.NET 部署故事一针见血:wekeroad.com/post/4373719917/… 和跟进wekeroad.com/post/4456244952/…
  • 确实很不错的文章。我会记住 capistrano,但我们决定采用 a/b 交换解决方案。它还允许我们在闲暇时毫不费力地拥有回滚功能。这绝对是微软的必修课。
  • 2 个文件夹的诀窍是:(1) 将当前 Web 应用程序 (www-live) 复制到 www-temp (2) 将 vdir 指向 www-temp (3) 更新 www-live ( 4) 将 vdir 指向 www-live // 这样你就不必记住哪个是哪个,但是你有 2 个编译而不是 1
【解决方案2】:

我的部署脚本会停止网站并显示“请稍候,正在更新网站...”页面,该页面每 15 秒刷新一次。由于部署需要不到一分钟的时间,它似乎可以解决问题。

【讨论】:

  • 感谢大卫,但我不赞成这样做。 IIS 具有执行平滑部署的功能,称为“重叠旋转”。结合 .NET 的“影子副本”等功能,我发现这不太可能。我会继续寻找答案。
  • 我个人认为,任何因维护而无法忍受一分钟停机的企业,其问题远比技术问题严重。也许他们应该审查他们的优先事项。
  • 为了清楚起见,我认为这个解决方案是最好的。这很简单,它是原生的(使用 app_offline),它是无风险的(用户在更新站点时无法访问该站点,因此他们无法生成或查看损坏的数据、不一致的状态等),并且成本为零。任何不能等待一分钟进行维护的网站用户都不值得您花费时间和精力。
  • 这有点浮夸? 60 秒的停机时间很长,但如果您需要正常运行时间,您可以使用负载均衡器,一次从访问者那里优雅地耗尽一个 IIS,直到可以清楚地更新...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-21
  • 1970-01-01
相关资源
最近更新 更多