首先,不推荐在ASP.NET后台中,启动Long-Running的任务。因为无论是用的Task还是ThreadPool.QueueUserWorkItem,ASP.NET不会知道它们在后台运行,这会产生一些问题,比如:
当修改web.config的时候,会触发Appdomain被回收(尽管此时IIS web服务器进程w3wp.exe仍然活着),IIS本身也会每29小时回收应用程序池,这都会导致后台线程被终止,从而引发异常。
当ASP.NET要回收AppDomain,它会让已经存在的请求处理完再回收,ASP.NET和IIS服务器也知道这些请求正在运行,所以等它们完成。问题是ASP.NET不知道任何后台线程比如一个计时器或者其他,它只知道和request相关的操作。
事实上,在后台长时间的运行某些任务实在不是web server该做的事情,通常都可以用其他的方式来避免这样做,比如:
用console application和windows任务管理器,或者使用Windows服务等。
但是,如果确定要这样做,那么在ASP.NET中也有些办法保证后台任务能够安全的退出。
方法一,使用IRegisteredObject接口。
通过IRegisteredObject接口,并且调用HostingEnvironment.RegisterObject方法在ASP.NET中注册它。
当Appdomain要被回收的时候,会调用已注册对象中的IRegisteredObject中的Stop方法。
public interface IRegisteredObject{ void Stop( bool immediate);} |
已注册对象没有被取消注册(即没有调用HostingEnvironment.UnregisterObject方法来取消注册),那么Stop方法会被调用两次,第一次调用的时候,immediate参数为false,此时如果已注册对象已经停止了,那么应该调用 HostingEnvironment.UnregisterObject方法来取消注册。如果还不取消注册,那么30秒后,该方法会被再次执行(这是最后的机会),不同的是此时传入的immediate参数为true,此时注册对象必须先调用 UnregisterObject 方法然后返回;否则应用程序管理器将移除该对象的注册。
使用IRegisteredObject接口并不是用来处理后台Long-Running任务的,但是这个功能可以让你安全的退出后台任务。
举个例子,如下:
public class JobHost:IRegisteredObject private readonly object _lock= new object (); private bool _shuttingDown; public JobHost() HostingEnvironment.RegisterObject( this ); public void Stop( bool immediate) _shuttingDown= true ; HostingEnvironment.UnregisterObject( this ); public void DoWork(Actionwork) if (_shuttingDown) |
如上面的代码,ASP.NET要回收Appdomain时,Stop 方法会被调用,这个方法会获得一个锁,在DoWork方法也获得同样的所,这样的话,DoWork在运行的时候,Stop方法不得不等待。
方法二,使用HostingEnvironment.QueueBackgroundWorkItem
HostingEnvironment.QueueBackgroundWorkItem 方法在.NET4.5.2中引入,QueueBackgroundWorkItem (QBWI) 通过ASP.NET运行时,注册后台任务。这样的话,由于ASP.NET知道有后台任务,他就不会立即回收Appdomain,当然,这不意味着后台任务可以做任何事情。当ASP.NET需要做回收的时候,它可以通过一个CancellationToken通知后台任务,并且等待30s让工作完成。如果30秒内没问出,那么这个工作也会消失了。关于QueueBackgroundWorkItem的更多细节,可以通过
方法二,使用HangFire
HangFire是一个开源的类库,提供简单的方法在ASP.NET中执行后台Long-Running任务。这个类库需要一些额外的存储上的支持,SQLServer,Redis或者MSMQ。HangFire的资料在http://hangfire.io/
参考资料
http://blog.stephencleary.com/2014/06/fire-and-forget-on-asp-net.html
本文出自 "一只博客" 博客,请务必保留此出处http://cnn237111.blog.51cto.com/2359144/1549947
阅读目录:
-
1.背景介绍
-
2.SOA架构下的显示端架构腐化
-
3.有效使用防腐层来隔离碎片服务导致显示端逻辑腐烂
-
4.剥离服务调用的技术组件让其依赖接口
-
5.将服务的DTO与显示端的ViewModel之间的转换放入防腐层
-
5.1.转换逻辑过程化,直接写在防腐层的方法中
-
5.2.转换逻辑对象化,建立起封装、重用结构,防止进一步腐化
-
-
6.防腐层的两种依赖倒置设计方法
-
6.1.事件驱动(防腐层监听显示逻辑事件)
-
6.2.依赖注入接口
-
-
7.总结
随着现在的企业应用架构都在向着SOA方向转变,目的就是将一个庞大的业务系统按照业务进行划分,不管从公司的管理上、产品的开发上,这一系列流程来看,都是正确的。SOA确实带来了解决现在大型企业级应用系统快速膨胀的解决办法。
但是本文要说的是,我们都将目光转向到了后端,也就是服务端,而将精力和时间都重点投在了后端服务的架构设计上,渐渐的忽视了显示端的架构设计。然而显示端的逻辑也越来越复杂,显示端轻薄的架构其实已经浮现出难以应付后端服务接口快速膨胀的危险,服务接口都是按照指数级增加,基本上每一个新的业务需求都是提供新的接口,这没有问题。按照服务的设计原则,服务接口就应该有着明确的作用,而不是按照代码的思维来考虑接口的设计。
但是由此带来的问题就是组合这些接口的显示端的结构是否和这种变化是一致的,是否做好了这种变化带来显示端逻辑复杂的准备。
根据我自己的亲身体会,我发现显示端的架构设计不被重视,这里的重视不是老板是否重视,而是我们开发人员没有重视,当然这里排除时间问题。我观察过很多用户接口项目架构,结构及其简单,没有封装、没有重用,看不到任何的设计原则。这样就会导致这些代码很难随着业务的快速推动由服务接口带来的冲击,这里还有一个最大的问题就是,作为程序员的我们是否有快速重构的意识,我很喜欢这条程序员职业素质。它可以让我们敏捷的、快速的跟上由业务的发展带来的项目结构的变化。
迭代重构对项目有着微妙的作用,重构不能够过早也不能够过迟,要刚好在需要的时候重构。对于重构我的经验就是,当你面对新功能写起来比较蹩脚的时候时,这是一个重构信号,此时应该是最优的重构时间。重构不是专门的去准备时间,而是穿插在你写代码的过程中,它是你编码的一部分。所以我觉得TDD被人接受的理由也在于此。
2.SOA架构下的显示端架构腐化
显示端的架构腐化我个人觉得有两个问题导致,第一个,原本显示端的结构在传统系统架构中可以工作的很好,但是现在的整体架构变了,所以需要及时作出调整。第二,显示端的架构未能及时的重构,未能将显示端结构进行进一步分离,将显示逻辑独立可测试。
这样随着SOA接口的不断增加,显示端直接将调用服务的方法嵌入到显示逻辑中,如,ASP.NET Mvc、ASP.NET Webapi的控制器中,包括两个层面之间的DTO转换。
按照DDD的上下文设计方法,在用户显示端也是可以有选择的创建面向显示的领域模型,此模型主要处理领域在即将到达服务端之后的前期处理。毕竟一个领域实体有着多个方面的职责,如果能在显示端建立起轻量级的领域模型,对显示逻辑的重构将大有好处,当然前提是你有着复杂的领域逻辑。(我之前的上一家公司(美国知名的电子商务平台),他们的显示端有着复杂的领域逻辑,就光一个显示端就复杂的让人吃惊,如果能在此基础上引入领域模型显示端上下文,将对复杂的逻辑处理很有好好处,当然这只是我未经验证的猜测而已,仅供参考。)
对显示端领域模型处理有兴趣的可以参考本人写的有关这方面的两篇文章:
.NET应用架构设计―面向查询的领域驱动设计实践(调整传统三层架构,外加维护型的业务开关)
.NET应用架构设计―面向查询服务的参数化查询设计(分解业务点,单独配置各自的数据查询契约)
原本干净的显示逻辑多了很多无关的服务调用细节,还有很多转换逻辑,判断逻辑,而这些东西原本不属于这个地方,让他们放在合适的地方对显示逻辑的重构、重用很有帮助。
如果不将其移出显示逻辑中,那么随着服务接口的不断增加和扩展,将直接导致你修改显示逻辑代码,如果你的显示逻辑代码是MVC、Webapi共用的逻辑,那么情况就更加复杂了,最后显示逻辑里面将被ViewModel与Service Dto之间的转换占领,你很难找到有价值的逻辑了。
3.有效使用防腐层来隔离碎片服务导致显示端逻辑腐烂
解决这些问题的方法就是引入防腐层,尽管防腐层的初衷是为了解决系统集成时的领域模型之间的转换,但是我觉得现在的系统架构和集成有着很多相似之处,我们可以适当的借鉴这些好的设计方法来解决相似的问题。
引入防腐层之后,将原本不该出现在显示逻辑中的代码全部搬到防腐层中来,在防腐层中建立起OO机制,让这些OO对象能够和显示逻辑一起搭配使用。
图1:
将用户层分层三个子层,UiLayer,Show Logic Layer,Anticorrosive Layer,最后一个是服务的接口组,所有的服务接口调用均需要从防腐层走。
我们需要将Show Logic Layer中的服务调用,类型转换代码迁移到Anticorrsoive Layer中,在这里可以对象化转换逻辑也可以不对象化,具体可以看下项目是否需要。如果业务确实比较复杂的时候,那么我们为了封装、重用就需要进行对象化。
4.剥离服务调用的技术组件让其依赖接口
首先要做的就是将逻辑代码中的服务对象重构成面向接口的,然后让其动态的依赖注入到逻辑类型中。在ASP.NETWEBAPI中,我们基本上将显示逻辑都写在这里面,我也将使用此方式来演示本章例子,但是如果你的MVC项目和WEBAPI项目共用显示逻辑就需要将其提出来形成独立的项目(Show Logic Layer)。
using OrderManager.Port.Models; using System.Collections.Generic; using System.Web.Http; namespace O
订阅:
博文评论 (Atom)
|
没有评论:
发表评论