第十三讲 服务寄宿
代码
https://yunpan.cn/cPns5DkGnRGNs 密码:3913
不是特殊的Demo,我们不再贴实例Demo的图片了,直接去网盘找相应的项目看
上节课我们介绍了 自我寄宿 的知识点 以及注意事项,那这节课我们就说说 通过操作系统现有的进程激活方式为WCF服务提供宿主 的 寄宿方式
WCF的服务寄宿,究其本质,就是创建或激活一个进程,为WCF服务构建一个运行环境。IIS本身可以看做一个自动化的进程激活工具。
以ASP.NET应用为例:我们通过HTTP协议请求寄宿于目的WEB服务器(IIS)的某个ASP.NET资源,比如aspx页面或asmx服务等。IIS会将该请求转发给相应的工作进程,如果该进程不存在,则激活或创建一个新的工作进程来处理该请求。同理,也可以采用相似的进程激活机制来处理基于WCF的服务寄宿。
借助于IIS寄宿WCF服务是最常见的服务寄宿方式,能够适应真正的企业级服务部署的要求。基于IIS的WCF服务寄宿使用于各种典型的Windows平台和不同的IIS版本
下面列出了比较典型的操作系统和IIS版本的组合:
WindowsXP(SP2)+IIS5.1
Windows2003+IIS6.0
Windows 7+IIS7.0
Windows Server 2008+IIS7.0
Windows 8+IIS8.0
Windows 10+IIS8.5
ASP.NET 本身并不依赖于IIS,它提供一个独立的处理WEB 请求的管道,我们完全可以将ASP. NET运行时寄宿于一个Window Form应用。但是绝大部分基于ASP.NET 应用部署于IIS下,IIS和ASP.NET协同工作处理WEB请求。我们以一个ASP.NET 应用为例,客户端通过浏览器对某一个.aspx页面发起请求,当该请求抵达目的服务器,IIS是第一道屏障。WEB请求经过IIS 管道的处理,被转发 给ASP.NET管道进行进一步的处理。
IIS其实就是一个中转站,当ASP.NET 的请求来了,就将这个请求交给处理ASP.NET管道,然后进行下一步处理。
IIS5.X 与ASP.NET:
我们先来看看IIS5.x是如何处理基于ASP.NET资源请求的。IIS5.x运行在InetInfo.exe中,该进程中一个最重要的服务就是名为World Wide Web Publishing Service(简称W3SVC)的Windows Service。W3SVC的主要功能包括HTTP请求的监听、工作进程的管理及配置管理等。
我们先看一下 这个 进程 ( 这里我的操作系统因为是 Win10 所以 我就 按照Win10 的界面截图了,与XP 大差不差 )
[ 13-01 ]
当检测到某个HTTP Request时,先根据扩展名判断请求是否是静态资源(比如.html,.img,.txt,.xml),如果是则直接将文件内容以Http Response的形式返回。如果是动态资源(比如.aspx,asp,php),则通过扩展名从IIS 的脚本映射 找到相应的ISAPI 的 DLL 文件来处理
ISAPI是Internet服务器API的缩写,是一套本地的Win32API,具有较高的执行性能,是 IIS 和其他动态WEB应用或平台之间的纽带。比如ASP ISAPI 桥接IIS与ASP,而ASP.NET ISAPI 连接着IIS与ASP.NET。ISAPI定义在一个DLL中,ASP.net ISAPI对应的DLL为Aspnet_isapi.dll,也就是说 请求的是一个ASP.NET 请求,那么IIS 就将这个请求转交给 了 Aspnet_isapi.dll 这个文件处理解析。
[ 13-02 ]
ISAPI支持ISAPI扩展(ISAPI Extension)和ISAPI筛选(ISAPI Filter),前者是真正处理HTTP请求的接口,后者则可以在HTTP请求真正被处理之前查看、修改、转发或拒绝请求。
如果我们请求的是一个基于ASP.NET的资源类型,比如:.aspx Web Page,.asmx web service或.svc WCF等,Aspnet_isapi.dll 会被加载, ASP.NET ISAPI扩展会创建ASP.NET的工作进程(如果该进程尚未启动),对于IIS5.X来说,该工作进程为aspnet.exe。IIS 进程与工作进程之间通过命名管道进程通信,以获得最好的性能。也就是说 IIS的进程与aspnet.exe的进程通过管道进行通信。
在工作进程初始化过程中.net运行时被加载,从而构建了一个托管的环境。对于某个WEB应用的初次请求,CLR 会为期创建一个AppDomain。在此AppDomian中,HTTP运行时被加载并用以创建相应的应用。寄宿于IIS5.X的所有WEB应用都运行在同一个进程的不同AppDomain中。
通过我们的介绍,我们可以看出IIS5.x至少存在着如下两个方面的不足:
1:ISAPI DLL 被加载到InetInfo.exe进程中,它和工作进程之间是一种典型的跨进程通信方式,尽管采用性能 最好的命名管道,但是仍然会带来性能的瓶颈。
2:所有ASP.NET应用,运行在相同进程中的不同的应用程序域中,基于应用程序域的隔离级别不能从根本上解决一个应用程序对另一个程序的影响,在更多的时候,我们需要不同的WEB应用 运行在不同的进程中。
IIS6.0 中,为了解决第一个问题,ISAPI.dll被直接加载到工作进程中。为了解决第二个问题,引入了应用程序池的机制。我们可以为一个或多个WEB应用池的WEB应用提供基于进程的隔离级别。IIS6.0 的工作进程名称为w3wp.exe。
当然,除了上面两点改进之外,IIS6.0还有其他一些值得称道的地方,其中最重要的一点就是创建了一个新的HTTP监听器:HTTP协议栈(HTTP.SYS).HTTP.SYS运行在Windows的内核模式下,作为驱动程序而存在。它是Windows2003的TCP/IP网络子系统的一部份,从结构上,它属于TCP之上的一个网络驱动程序。严格来说,HTTP.SYS已经不属于IIS范畴了,所以HTTP.SYS的配置信息并不保存在IIS的元数据库中,而是定义在注册表中.
HTTP.SYS能够带来如下的好处
持续监听:由于HTTP.SYS是一个网络驱动程序,始终处于运行状态,对于用户的HTTP请求,能够及时作出反应。更好的稳定性:HTTP.SYS运行在操作系统内核模式下,并不执行任何用户代码,所以本身不会受到WEB应用,工作进程和IIS进程的影响。
内核模式下数据缓存:如果某个资源被频繁请求,HTTP.SYS会把响应的内容进行缓存,缓存的内容可以直接影响后续请求。由于这是基于内核模式的缓存,不存在内核模式和用户模式的切换,相应速度得到极大的改进。
IIS7.0与ASP.NET:
IIS7.0 在请求的监听和分发机制上又进行了革新性的改进,主要体现在对Windows 进程激活服务(WAS)的引入,将原来(IIS6.0)W3SVC承载的部分功能分流给了WAS。具体来说,
通过上面的介绍我们知道对于IIS6.0来说,W3SV主要承载着3大功能。
1:HTTP请求接受:接受HTTP.SYS监听到的HTTP请求。
2:配置管理:从元数据库中加载配置信息对相关组件进行配置。
3:进程管理:创建,回收, 监控工作进程.
在IIS7.0 中后两组功能被移入WAS中,接收 HTTP请求的任务仍然落在W3SVC头上。
WAS的引入为IIS7.0 一项前所未有的特性:同时处理HTTP和非HTTP请求。
在WAS中,通过一个重要的接口:监听器适配接口抽象出不同协议监听器监听到的请求。
至于IIS下的监听器,除了基于网络驱动的 HTTP.SYS提供HTTP请求监听功能外,WCF提供了3种类型的监听器:TCP监听器,命名管道,和MSMQ监听器,分别提供了基于TCP,命名管道和MSMQ传输协议的监听功能。
与此3种监听器相对的是3种监听器适配器,它们提供监听器与监听适配器接口之间的适配。 从这个意义上讲,IIS7.0 中的W3SVC更多地为HTTP.SYS起着监听适配器的功能.
ASP.NET 集成
从上面对IIS5.X和IIS6.0的介绍中,我们不难发现这一点,IIS与ASP.NET是两个相互独立的管道,在各自管辖范围内,它们各自具有自己的一套机制对HTTP请求进行处理。两个管道通过ISAPI实现"连通",IIS是第一道屏障,当对HTTP请求进行必要的前期处理时,通过ISAPI将请求分发给ASP.NET管道。当ASP.NET在自身管道范围内完成对HTTP请求的处理时,处理后的结果再返回到IIS,IIS对其进行后期处理,最终生成HTTP响应(HTTP Response)。从另一个角度讲,IIS进行在非托管的环境中,而ASP.NET管道则是托管的,从这个意义上讲,ISAPI还是连接非托管环境和托管环境的纽带。
ASP.NET管道:
HttpApplication:
HttpApplication是整个ASP.NET基础架构的核心,它负责处理分发给它的HTTP请求。由于一个HttpApplication对象在某个时刻只能处理一个请求,只有完成对某个请求的处理后,HttpApplication才能用于后续的请求的处理。 ( Global.asax 文件继承自HttpApplication 类,我们可以在里面做相应的 全局应用程序 的操作 )所以ASP.NET采用对象池的机制来创建或获取HttpApplication对象。具体来讲,当第一个请求抵达的时候,ASP.NET会一次创建多个HttpApplication对象,并将其置于池中,选择其中
一个对象来处理请求。当处理完毕,HttpApplication不会被回收,而是释放到池中。对于后续的请求,空闲的HttpApplication对象会从池中取出,如果池中所有的HttpApplication对象都处于繁忙的状态,ASP.NET会创建新的HttpApplication对象。对于一个ASP.NET应用来说,HttpApplication派生global.asax文件,我们可以通过创建global.asax文件对HttpApplication的请求处理进行定制。global.asax采用一种很直接的方式实现了这样的功能。
HttpModule:
ASP.NET 为创建各种.NET web应用提供了强大的平台,它拥有一个具有高度可扩展性的引擎,并且能够处理对于不同资源类型的请求。那么,是什么成就了ASP.NET的高可扩展性呢?HttpModule功不可没。
从功能上讲,HttpModule之于ASP.NET,就好比ISAPI Filter之于IIS一样。IIS将接收到的请求分发给相应的ISAPI Extension之前,注册的ISAPI Filter会先截获该请求。ISAPI Filter可以获取甚至修改请求的内容,完成一些额外的功能。与之相似地,当请求转入ASP.NET管道时,最终负责处理该请求的是与请求资源类型相匹配的HttpHandler对象。但是在Handler正式工作之前,ASP.NET会先加载并初始化所有配置的HttpModule对象.HttpModule在初始化的过程中,会将一些功能注册到HttpApplication相应的事件
中,那么在HttpApplication整个请求处理生命周期中的某个阶段,相应的事件会被触发,通过HttpModule注册的事件处理程序也得以执行.
HttpHandler:
如果说HttpModule相当于IIS的ISAPI Filter的话,我们可以说HttpHandler则相当于IIS的ISAPI Extension,HttpHandler在ASP.NET中扮演请求的最终处理者的角色。对于不同资源类型的请求,ASP.NET会加载不同的Handler来处理,也就是说.aspx page与.asmx web service对应的Handler是不同的。
所以如果是ASP.NET 的请求 顺序应该是:IIS——ISAPI——ISAPI Filter——ISAPI Extension——Aspnet_isapi.dll——HttpApplication——HttpModule——HttpHandler
到这里我们创建了一个 关于 IIS 寄宿方式的 WCF 的 一个小DEMO( 1.IIS寄宿 )。 在网盘上。
这个项目很简单,所以就不再 过多叙述,因为之前 已经都讲过了。
前面我们回顾了 一下关于 IIS 以及 ASP.NET 的 一些知识,接下来就 开始讲解WCF 在 其中的不同。
WCF与ASP.NET 并行模式:
对于基于IIS服务寄宿,System.ServiceModel.Activation.HttpModule将基于.svc的请求劫持并分发给WCF的服务模型,从而结束了请求在ASP.NET管道的旅程。除了ASP.NET提供的一些少量底层服务,比如动态编译等,绝大部分ASP.NET对传统的ASP.NET资源的请求处理机制将不会应用在基于WCF Service的请求处理流程中。从这个意义上讲,我们可以说WCF Service的运行模式和ASP.NET运行时采用的是一种并行的模式。
这种并行模式 是一种默认的一种模式。之前的 一个小 DEMO 就是 并行模式。
你可以完全把一个映射到IIS虚拟目录的 ASP.NET WebSite 同时作为 .asmx(webService) 和 .svc(WCFService) 的宿主。在这种情况下,.aspx(ASP.NET Page)、 .asmx(WebService) 和 .svc(WCFService) 运行在同一个AppDomain中。但是HttpRuntime 对 .aspx(ASP.NET Page) 和 .asmx(WebService) 的处理机制并不会应用于对 .svc(WCFService) 请求。我们把WCF Service这种寄宿模型称为ASP.NET并行模式.
为什么WCF 要采用这种于ASP.NET并行的模式,而不像Web Service一样采用与ASP.NET完全兼容呢?
这主要是因为WCF和 .asmx(WebService) 有本质的区别:
Web Service总是采用IIS寄宿,并使用HTTP作为传输,而WCF则具有不同的寄宿方式,对于传输协议的选择也没有限制。在默认的情况下,不论采用何种寄宿方式,WCF本身的行为应该保持一致。所以,让WCF服务的行为独立于寄宿的环境与传输协议,是采用并行模式的主要原因.
但是通过设置,我们可以将 WCF 与 ASP.NET 设置为 兼容模式:
虽然在默认的情况下,IIS的寄宿采用ASP.NET 并行的模式。但是在一个WEB应用中,尤其是一些AJAX的WEB应用,却明确地需要以一种ASP.NET兼容模式处理WCFService请求。比如,在WCFService的操作中,需要获取ASP.NET应用的SessionState等。
WCF 对此提供了支持,实现起来也很简单,对于编程来说,仅仅需要在Service类型(WCF的服务契约实现的Service)加上一个特殊的AspNetCompatibilityRequirementsAttribute特性
并将RequirementsMode属性指定为AspNetCompatibilityRequirementsMode.Allowed
除此之外,WCF的配置需要做一些修改,我们需要将<serviceHostingEnvironment/>配置节的aspNetCompatibilityEnabled属性设为true.
看代码:
1 //设置WCF与ASP.NET保持兼容模式 2 [AspNetCompatibilityRequirements(RequirementsMode=AspNetCompatibilityRequirementsMode.Allowed)] 3 public class Service1 : IService1 4 { 5 6 public void Add(double x) 7 { 8 throw new NotImplementedException(); 9 } 10 11 public void Subtract(double x) 12 { 13 throw new NotImplementedException(); 14 } 15 16 public void Multiply(double x) 17 { 18 throw new NotImplementedException(); 19 } 20 21 public void Divide(double x) 22 { 23 throw new NotImplementedException(); 24 } 25 26 public double GetResult() 27 { 28 throw new NotImplementedException(); 29 } 30 31 }
并且 配置文件也要改:
1 <system.serviceModel> 2 3 <serviceHostingEnvironment aspNetCompatibilityEnabled="true"/> 4 5 </system.serviceModel>
这样子,在ASP.NET 兼容模式下,ASP.NET 将会采用与处理 .aspx , .asmx一样的方式来处理基于.svc 的请求,对于WCFService请求的处理将会贯穿HttpApplication请求处理的整个生命周期。原本实现在HttpModule中对WCFService的请求处理逻辑将实现在一个HttpHandler中。
好了,WCF服务 与 ASP.NET 兼容模式 说完了,但是要真正做到贯穿整个请求声明周期还需要客户端的配合,我们接着看。
本节课的另一个Demo ( 2.兼容模式 ) 利用ASP.NET兼容模式创建支持会话(Session)的WCF服务。
我们的WCF服务是 根据客户端的 Session 中的值来进行计算。
我们先看 Client 客户层 项目中的 App.config 配置文件:
1 <?xml version="1.0" encoding="utf-8" ?> 2 <configuration> 3 <system.serviceModel> 4 <client> 5 <endpoint address="http://localhost/AspServices/Service1.svc" binding="wsHttpBinding" contract="Contract.IService1" name="CalculatorService" bindingConfiguration="CookieAllowableBinding"></endpoint> 6 </client> 7 <bindings> 8 <wsHttpBinding> 9 <binding name="CookieAllowableBinding" allowCookies="true"></binding> 10 </wsHttpBinding> 11 </bindings> 12 </system.serviceModel> 13 </configuration>
有一个 allowCookies="true" 的配置。
当我们把这个配置去掉也就是把 bindings 节点删掉,并且 将 endpoint 节点的 bindingConfiguration="CookieAllowableBinding" 设置也删掉
来运行试一试,很遗憾 所有结果都为0。因为客户端就没有传递Cookie中的Session,对于HTTP的请求,每次请求都为一个新的请求。
然后我们再把之前删除的节点加上,再来试一试。
结果很令人满意。这是为什么呢?
那得从Session的实现机制说起。众所周知,HTTP是无状态的传输协议,对于服务端来说,它收到的每个HTTP请求都是全新的请求。ASP.NET会话(Session)的实现很简单,就是让每次HTTP请求携带Session的识别信息(Session ID),那么服务就可以根据此信息判断请求来自哪个客户端了。关于Session识别信息的保存,Asp.Net有两种方式:Cookie和URL,前者将其放到Cookie中,每次HTTP请求将会携带该Cookie的值,后者则将其作为请求URL的一部分。一般情况下采用基于Cookie的实现机制,如果Cookie禁用则采用后者.那么对于ASP.NET兼容模式下的WCF也一样,要想让服务能够识别会话,就需要让每个服务调用的HTTP请求携带Session的识别信息,我们可以通过传递Cookie的方式来解决这个问题。对于WCF来说,Cookie传递能够通过Binding来控制,对于WsHttpBinding来说,默认情况下并不允许Cookie的传递。我们可以通过WsHttpBinding的AllowCookies来控制是否允许传递Cookie,该属性可以通过配置进行设置.