组件化的 ASP.NET MVC 应用程序是否可以顺利部署?

发布于 2024-10-09 10:49:42 字数 3989 浏览 0 评论 0原文

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

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

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

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

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

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

版本信息:Microsoft .NET 框架版本:4.0.30319;网络平台 版本: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 本身的错误?

PS IIS 重叠旋转已启用,无论如何它都是默认值。

以下是组件:

  • A.dll
  • Some.Assembly.dll(<--- 这是失败的,取决于 A.dll)
  • B.dll(取决于 A.dll 和 Some.Assembly.dll)
  • Web.dll (这是 Web 应用程序,取决于上述所有内容)

所有依赖项都是使用常规程序集引用建立的,并启用了“复制本地”。所有 dll 都是作为单独项目的解决方案的一部分。

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

That's probably not MVC specific, it might as well applicable to ASP.NET WebForms, but we've been experiencing it on MVC2 so far.

Whenever we start remote deployment with MSDeploy we get a brief (5-6 seconds of) "server error" page for our requests until new deployment takes place. Here is the error text:

Server Error in '/' Application.

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)

Description: An unhandled exception
occurred during the execution of the
current web request. Please review the
stack trace for more information about
the error and where it originated in
the code.

Exception Details:
System.IO.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)

Version Information: Microsoft .NET
Framework Version:4.0.30319; ASP.NET
Version:4.0.30319.1

Here are the stack traces shown in the error page:

[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

The assembly which is causing that failure (Some.Assembly) is not the actual web assembly but one of other components under it's "References", signed with a .snk.

After 5-6 seconds, the site goes up and the error goes away.

That's certainly not the desired behavior. I wonder if we are doing something wrong in terms of wiring up the components together. Should there be another approach to ensure a smooth deployment? Or could this be a bug with MVC2 itself?

P.S. IIS overlapped rotation is enabled, it's the default anyways.

Here are the components:

  • A.dll
  • Some.Assembly.dll (<--- this is the failing one, depends on A.dll)
  • B.dll (depends on A.dll and Some.Assembly.dll)
  • Web.dll (this is the web application, depends on all of the above)

All the dependencies are established using regular assembly references with Copy Local enabled. All dll's are part of the solution as separate projects.

MSDeploy only deploys binaries, not the source.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一张白纸 2024-10-16 10:49:42

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

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

什么是“临时 ASP. NET Files”文件夹的用途? (我的回答)

同样,它们并不是为了提供一种机制来帮助您在上传新网站时保持整个现有代码库的运行。

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

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

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

整个站点将处于不一致的状态,直到部署最后一个文件。

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

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

App_Offline.htm - 斯科特·格思里< br>
App_Offline.htm 并解决“IE 友好错误”功能< /a>


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

如何:准备部署 Web 项目

复杂的替代方案,涉及有两个文件夹,例如:

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 中,并在您满意时切换到该版本。

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

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

Overlapping rotation only applies to pool recycling and is not intended to facilitate the type of deployment you're describing or require. When overlapping rotation is enabled, existing requests in a pool's "outgoing" worker process will be allowed to complete whilst new requests will be sent to the new worker process created. It's a mechanism to gracefully handover to a new worker process without pulling the rug from underneath existing requests. That's all it does.

Shadow copy folders are used for this purpose:

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

Again they are not intended to provide a mechanism to facilitate keeping a whole existing code base running whilst you upload a new site.

When you deploy an ASP.NET application the site will misbehave. Whilst you are copying your assemblies these files will be locked (probably exclusively) by whatever process is handling the upload (WebDAV or FTP).

This causes the exceptions you've observed and are most likely because the shadow copy mechanism can't read the new assemblies until they are written (and WebDAV or FTP removes the write locks).

Additionally, any pages that have dependencies on these assemblies may not (shadow) compile if expected method signatures have changed or been removed until the correct pages have been uploaded. Or if the pages/views upload first that depend on functionality in assemblies that haven't yet been deployed you will also get errors.

The whole site will be in an inconsistent state until the last file is deployed.

There is no built-in mechanism in IIS to ensure an "atomic" deployment, i.e. load all your stuff then switch over to running that. IIS will keep serving requests to the site and ASP.NET will still keep detecting file changes as you upload the application.

The only way to deploy an application without generating these errors this is to enable a special page called App_Offline.htm before deployment and then rename or remove after deployment:

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

You may also find this article useful:

How to: Prepare to Deploy a Web Project

There is a slightly more convoluted alternative which involves having two folders, for example:

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

The running site is pointing at d:\websites\site\www-A, meanwhile you deploy into d:\websites\site\www-B. When you're ready you switch the site to the d:\websites\site\www-B folder.

When you come to deploy the next built you deploy into d:\websites\site\www-A and switch to that when you're happy.

The drawback is that you need to be on your toes and remember which folder is which.

Also any user uploaded content would need to be synchronised between the two folders (although you could have a third folder mapped into to a virtual directory for stuff like that).

哆兒滾 2024-10-16 10:49:42

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

My deployment script stops the website and displays a "Please wait while the site is being updated..." page, which refreshes every 15 seconds. Since the deployment takes under a minute it seems to fix the problem.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文