简体   繁体   English

AppDomain,处理异常

[英]AppDomain, handling the exceptions

I am developing a large application which consists of many smaller plugins/applications.我正在开发一个由许多较小的插件/应用程序组成的大型应用程序。

They are not big enough to be a full process, but are too small to be run in a thread, under one process, along with that I want to have it based on a plugin-basis.它们不足以成为一个完整的进程,但太小而无法在一个线程中运行,在一个进程下,以及我希望基于插件的基础上拥有它。 If a newer version of that plugin is available it should be unloaded, updated and started again.如果该插件的更新版本可用,则应将其卸载、更新并重新启动。

During my search for a solution I can accross the magic word AppDomain, and I quote:在我寻找解决方案的过程中,我可以使用 AppDomain 这个神奇的词,我引用:

"Use application domains to isolate tasks that might bring down a process. If the state of the AppDomain that's executing a task becomes unstable, the AppDomain can be unloaded without affecting the process. This is important when a process must run for long periods without restarting. You can also use application domains to isolate tasks that should not share data." “使用应用程序域来隔离可能导致进程中断的任务。如果正在执行任务的 AppDomain 的 state 变得不稳定,则可以在不影响进程的情况下卸载 AppDomain。当进程必须长时间运行而不重新启动时,这一点很重要. 您还可以使用应用程序域来隔离不应共享数据的任务。”

Thus that is exactly what I want.因此,这正是我想要的。 However, I guess their 'State becomes unstable' is a different point of view than mine.但是,我猜他们的“状态变得不稳定”与我的观点不同。 I am thinking of a problem where one of the plugins throws an exception, for whatever reason.我正在考虑一个问题,其中一个插件出于某种原因引发异常。 I would like that be catched, e-mailed, unloaded and restart (if possible).我希望它被捕获、通过电子邮件发送、卸载并重新启动(如果可能的话)。

So I created an application that starts up, looks for all.dll's in its folder.所以我创建了一个启动的应用程序,在其文件夹中查找 all.dll。 Checks if the dll consists of a plugin.检查 dll 是否包含插件。 Creates a new AppDomain for that plugin, and once everything is loaded it will start each plugin.为该插件创建一个新的 AppDomain,一旦加载了所有内容,它将启动每个插件。 (Where each plugin can consist of multiple threads, co-existing happily next to ech other). (每个插件可以由多个线程组成,彼此愉快地共存)。

So I also added a time-out in there, that fires after 5seconds to throw a new Exception();所以我还在那里添加了一个超时,它会在 5 秒后触发以抛出一个新的 Exception(); Added a UnhandledException event on the AppDomain to handle it.在 AppDomain 上添加了一个 UnhandledException 事件来处理它。 But, it catched it, and after cathing, still 'crashed' the whole process including all the extra child-AppDomains.但是,它捕捉到了它,并且在捕捉之后,仍然“崩溃”了整个过程,包括所有额外的子 AppDomains。

But it clearly states in the quote 'to isolate tasks that "might" bring down a process'.但它在引用中明确指出“隔离“可能”导致流程中断的任务。 So am I missing something vital?所以我错过了一些重要的东西吗? Is my view on the quote wrong?我对报价的看法是错误的吗?

Since .NET 2.0 unhandled exceptions crash the process .由于 .NET 2.0未处理的异常使进程崩溃 From AppDomain.UnhandledException event documentation:来自AppDomain.UnhandledException事件文档:

This event provides notification of uncaught exceptions.此事件提供未捕获异常的通知。 It allows the application to log information about the exception before the system default handler reports the exception to the user and terminates the application .它允许应用程序在系统默认处理程序向用户报告异常并终止应用程序之前记录有关异常的信息。

The same goes for AppDomain.FirstChanceException : AppDomain.FirstChanceException也是如此:

This event is only a notification .这个事件只是一个通知 Handling this event does not handle the exception or affect subsequent exception handling in any way.处理此事件不会以任何方式处理异常或影响后续异常处理。

You need to think about how you will handle exceptions just like you will do it in normal app.您需要考虑如何处理异常,就像在普通应用程序中一样。 Just using AppDomains will not help.仅使用 AppDomains 将无济于事。 If the exception has not been handled within given AppDomain it will get rethrown in calling AppDomain until it either get handled or crashes the process.如果在给定的 AppDomain 中没有处理异常,它将在调用 AppDomain 时重新抛出,直到它得到处理或使进程崩溃。 It is perfectly fine to handle some exceptions and don't let them crash your process.处理一些异常并且不要让它们使您的进程崩溃是非常好的。

AppDomain is a logical container for assemblies and memory (not for threads). AppDomain 是程序集和 memory(不适用于线程)的逻辑容器。 Isolation for AppDomain implies: AppDomain 的隔离意味着:

  • Objects created in domain A can not be accessed directly by domain B (without marshaling).在域 A 中创建的对象不能由域 B 直接访问(无需编组)。 This allows for domain A to be unloaded without affecting anything in domain B. These objects will get automatically deleted when 'owning' domain gets unloaded.这允许在不影响域 B 中的任何内容的情况下卸载域 A。当“拥有”域被卸载时,这些对象将被自动删除。

  • Assemblies can be automatically unloaded with AppDomain.可以使用 AppDomain 自动卸载程序集。 This the only way you can unload managed dll from process.这是您可以从进程中卸载托管 dll 的唯一方法。 This is useful for DLL hot-swapping.这对于 DLL 热插拔很有用。

  • AppDomain security permissions and configuration can be isolated from other AppDomains. AppDomain 的安全权限和配置可以与其他 AppDomain 隔离。 This can be helpful when you load untrusted third party code.当您加载不受信任的第三方代码时,这会很有帮助。 It also lets you override how assemblies will be loaded (version binding, shadow copying etc).它还允许您覆盖程序集的加载方式(版本绑定、卷影复制等)。

Most common reasons for using AppDomain is when you run untrusted third party code.使用 AppDomain 的最常见原因是当您运行不受信任的第三方代码时。 Or you have unmanaged code and want to host CLR or need dll hot swapping.或者您有非托管代码并且想要托管 CLR 或需要 dll 热交换。 I think that in CLR hosting scenario you can save your process from crashing when thirdparty code throws unhandled exception.我认为在CLR 托管方案中,当第三方代码引发未处理的异常时,您可以避免进程崩溃。

Also instead of rolling your own infrastructure you might want to look at System.Addin or MEF .此外,您可能希望查看System.Addin 或 MEF ,而不是滚动您自己的基础架构。

There are two problems with an unhandled exception.未处理的异常有两个问题。 AppDomain solves only one of them. AppDomain 只解决其中一个问题。 You're trying to deal with the other one.你正试图对付另一个。

Good news first.先说好消息。 When you handle an exception, you have to restore the program state as though the exception never happened.当您处理异常时,您必须恢复程序 state,就好像该异常从未发生过一样。 Everything has to be rewound to the before-the-code-ran state.一切都必须回到代码运行前的 state。 You normally have a bunch of catch and finally clauses that undo the state mutations performed by code.您通常有一堆 catch 和 finally 子句来撤消代码执行的 state 突变。 Nothing very simple of course.当然没有什么很简单的。 But entirely impossible if the exception is unhandled.但如果异常未处理,则完全不可能。 You have no idea exactly what got mutated and how to restore it.你不知道究竟是什么发生了变异以及如何恢复它。 AppDomain handles this very difficult problem with aplomb. AppDomain 沉着应对这个非常棘手的问题。 You unload it and whatever state was left is just gone.你卸载它,剩下的 state 就消失了。 No more garbage collected heap, no more loader heap (statics).没有更多的垃圾收集堆,没有更多的加载器堆(静态)。 The whole enchilada gets reset to whatever the state was before you create the AppDomain.在您创建 AppDomain之前,整个 enchilada 将重置为 state 的任何值。

That's great.那太棒了。 But there's another problem that's pretty hard to deal with as well.但是还有另一个问题也很难处理。 Your program was asked to perform a job.您的程序被要求执行一项工作。 The thread set off to do that job.线程开始做这项工作。 But it suffered a heart attack.但它心脏病发作了。 Big problem number one: the thread is dead.大问题一:线程死了。 That's pretty bad news if your program had only one thread to begin with.如果您的程序开始时只有一个线程,那将是一个非常糟糕的消息。 There's no thread left, the program terminates.没有线程了,程序终止。 Nice that the AppDomain unloaded first, but it really doesn't make any difference, it would have got unloaded anyway.很高兴 AppDomain 首先卸载,但它真的没有任何区别,无论如何它都会被卸载。

Big problem too: it was really rather important that this job got done.大问题也是:完成这项工作非常重要。 It didn't.它没有。 That matters, the job was, say, to balance the corporate profit and loss statement.重要的是,比如说,这项工作是平衡公司损益表。 That didn't get done, somebody is going to have to take care of that because not balancing the statement is going to get a lot of people very upset.那没有完成,有人将不得不照顾它,因为不平衡声明会让很多人非常沮丧。

How do you solve that?你怎么解决这个问题?

There are only a few selected scenarios where that's acceptable.只有少数选定的场景是可以接受的。 Server scenarios.服务器场景。 Somebody asks it to do something, the server reports back "couldn't do it, please contact the system administrator".有人要求它做某事,服务器报告“不能做,请联系系统管理员”。 The way ASP.NET and SQL Server work. ASP.NET 和 SQL 服务器的工作方式。 They use AppDomains to keep the server stable.他们使用 AppDomains 来保持服务器稳定。 And have system administrators to deal with the problems.并有系统管理员来处理问题。 You'll have to create that kind of support system to make AppDomains work for you.您必须创建这种支持系统才能使 AppDomains 为您工作。

Just adding some extra info on the subject for anybody that considers (been there myself) using application domains mainly to guarantee the stability of an application:只需为考虑(我自己去过那里)主要使用应用程序域来保证应用程序稳定性的任何人添加一些关于该主题的额外信息:

A few years ago, the System.AddIn team published a very interesting blog entry.几年前, System.AddIn团队发表了一篇非常有趣的博客文章。 Using AppDomain Isolation to Detect Add-In Failures . 使用 AppDomain 隔离检测加载项故障

It explains that only out-of-process add-ins can guarantee the stability of the host.它解释了只有进程外的加载项才能保证主机的稳定性。 More specifically:进一步来说:

Starting with the CLR v2.0 unhandled exceptions on child threads will now cause the entire process to be torn down and thus it is impossible for a host to completely recover from this.从 CLR v2.0 开始,子线程上的未处理异常现在将导致整个进程被拆除,因此主机不可能完全从中恢复。

So what they suggest is to subscribe to the AppDomain.UnhandledException and before your application crashes, store somewhere (a log, a database etc.) information about who caused this exception.因此,他们建议订阅AppDomain.UnhandledException并在您的应用程序崩溃之前,将有关谁导致此异常的信息存储在某处(日志、数据库等)。 Then the next time your application starts use this information to protect your application.然后在您的应用程序下次启动时使用此信息来保护您的应用程序。 Perhaps you don't load the add-in or you inform the user and let her/him decide.也许您不加载加载项,或者您通知用户并让她/他决定。 (Microsoft Office applications followed this approach and disabled the plugins that crashed the host. You then had to re-enable them yourself.) (Microsoft Office 应用程序遵循这种方法并禁用了导致主机崩溃的插件。然后您必须自己重新启用它们。)

They also published another blog entry that shows how to do this even in scenarios where the host is running in another host (IIS, WAS etc.).他们还发布了另一个博客条目,展示了即使在主机在另一台主机(IIS、WAS 等)中运行的情况下如何执行此操作。 More on Logging UnhandledExeptions from Managed Add-Ins . 有关从托管加载项记录 UnhandledExeptions 的更多信息

Although both these articles are centered around System.AddIn , they contain useful information for anyone trying to increase the stability of their plugin-aware application.尽管这两篇文章都以System.AddIn为中心,但它们包含对任何试图提高其插件感知应用程序稳定性的人有用的信息。

AppDomain is more oft used for being able to unload assemblies (like your suggesting) and for controlling startup parameters like .NET access levels, configurations, etc. If you really want 'isolation' then the best bet is always a worker process; AppDomain 更常用于卸载程序集(如您的建议)和控制启动参数,如 .NET 访问级别、配置等。如果您真的想要“隔离”,那么最好的选择始终是工作进程; however, it's a lot more work.然而,这还有很多工作要做。

I do a fair amount of this in several projects.我在几个项目中做了很多这样的事情。 Just to give a broad-stroke picture, we use Google ProtoBuffers ( Jon Skeet's port ) over a managed Windows LRPC Library for most of the communications.只是为了给出一个大致的画面,我们使用Google ProtoBuffersJon Skeet 的端口)在一个托管的 Windows LRPC 库上进行大多数通信。 For worker-process management we rely heavily on named events, I recently published an inter-process event library here just for this purpose.对于工作进程管理,我们严重依赖命名事件,我最近在这里发布了一个进程间事件库,就是为了这个目的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM