我们已经知道,对于一个服务(接口),有A、B、C三个要素,通过他们可以被客户端利用来调用,可是在服务端,它的结构是怎么样的?
ServiceHost
WCF的服务可以有很多寄存环境,可是,不管在哪种环境,服务模型在宿主初始化,操作调用和消息处理方面,都提供一致的运行时体验。ServiceHost类型在这过程中扮演着重要的角色。
简单的可以认为ServiceHost负责管理服务的通信信道的生存周期。
建立ServiceHost
一般使用public ServiceHost(Type serviceType, params Uri[] baseAddresses)构造方法建立ServicesHost。
参数:
Type serviceType --为实现了某些Contract的类的类型,为这个服务主机要host的服务。
params Uri[] baseAddresses --为任意数量的baseAddress。
Uri baseAddress = new Uri("http://localhost:8080/WCFService/Service");
//Instantiate new ServiceHost
myServiceHost = new ServiceHost(typeof(Service), baseAddress);
一个ServiceHost内只能驻留一个Service类,但是这个Service类可以实现多个Contract,每个Contract都能通过一个或多个(不同的bind)Endpoint向客户端暴露。
通过两种方法给ServiceHost添加Endpoint
ServiceHost.AddServiceEndpoint
AddServiceEndpoint方法有8种重载,ServiceHost提供了四种:
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, string address, Uri listenUri);
ServiceEndpoint AddServiceEndpoint(Type implementedContract, Binding binding, Uri address, Uri listenUri);
ServiceHost的父类ServiceHostBase也提供了四种:
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, string address, Uri listenUri);
ServiceEndpoint AddServiceEndpoint(string implementedContract, Binding binding, Uri address, Uri listenUri);
其中参数implementedContract为Contract的完全名称,即名称空间.类名。
myServiceHost.AddServiceEndpoint(typeof(WCFService.IService), new BasicHttpBinding(), "");
ServiceHost.Description.AddServiceEndpoint
ServiceDescription是一个Service在内存中的一个完整描述。包括:EndPoints和Behaviors。
ServiceEndpoint myServiceEndpoint = new ServiceEndpoint(ContractDescription.GetContract(typeof(WCFService.IService)), new BasicHttpBinding(), new EndpointAddress(baseAddress));
myServiceHost.Description.Endpoints.Add(myServiceEndpoint);
视需要给ServiceHost添加behavior
ServiceHost. Behaviors是一个 IServiceBehavior类型的对象集合。
IserviceBehavior提供了一个在整个服务范围内修改或则插入定制扩展的机制。
如果需要把服务通过WSDL对外暴露对服务的Metadata描述,就需要加一个ServiceMetadataBehavior类型的Behavior:
ServiceMetadataBehavior behavior = new ServiceMetadataBehavior();
behavior.HttpGetEnabled = true;
behavior.HttpGetUrl = new Uri("http://localhost:8001/");
myServiceHost.Description.Behaviors.Add(behavior); // myServiceHost是ServiceHost实例
一个较规范(正确的)ServiceHost描述应该是这样的:
EndPointListener侦听器包含了listening address,message filtering和duspatch。他们独赢ServiceEndpoint中的EndPointAddress、Contract、Binding。在EndpointListener中,还包含了一个Channel Stack,专门负责发送和接受消息。
WCF系列(一) -- 完全不使用配置文件构建和使用WCF服务
宿主
WCF典型的宿主包括以下四种:
l Self-Hosting 自托管宿主
l Managed Windows Services(Windows 服务宿主)
l Internet Information Services(IIS宿主)
l Windows Process Activation Service(WAS宿主)
1、自托管宿主利用
WCF提供的ServiceHost<T>提供的Open()和Close()方法,可以便于开发者在控制台应用程序,Windows应用程序乃至于ASP.NET应用程序中托管服务。不管自宿主的环境是何种应用程序,实质上托管服务的方式都是一致的。例如在控制台应用程序中:
using (ServiceHost host =
new ServiceHost(typeof(DocumentsExplorerService)))
{ host.Open();
Console.WriteLine("The Service had been launched.");
Console.Read();
}
由于ServiceHost实例是被创建在应用程序域中,因此我们必须保证宿主进程在调用服务期间不会被关闭,因此我们利用Console.Read()来阻塞进程,以使得控制台应用程序能够一直运行,直到认为地关闭应用程序。如果是Windows应用程序,则可以将创建ServiceHost实例的代码放在主窗体的相关代码中,保证服务宿主不会被关闭。相应地,我们需要配置应用程序的app.config配置文件:
<configuration>
<system.serviceModel>
<services>
<service name="DocumentsExplorerServiceImplementation.DocumentsExplorerService"
behaviorConfiguration="DocumentExplorerServiceBehavior">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8008/DocumentExplorerService"/> </baseAddresses>
</host>
<endpoint address=""
binding="basicHttpBinding" bindingConfiguration="DocumentExplorerServiceBinding" contract="DocumentsExplorerServiceContract.IDocumentsExplorerService"/>
<endpoint address="mex"
binding="mexHttpBinding" contract="IMetadataExchange"/> </service>
</services>
<bindings>
<basicHttpBinding>
<binding name="DocumentExplorerServiceBinding"
sendTimeout="00:10:00" transferMode="Streamed"
messageEncoding="Text" textEncoding="utf-8" maxReceivedMessageSize="9223372036854775807"> </binding>
</basicHttpBinding>
</bindings>
<behaviors>
<serviceBehaviors>
<behavior name="DocumentExplorerServiceBehavior">
<serviceMetadata
httpGetEnabled="true"/>
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
注意,配置文件中的服务名必须包含服务契约以及服务类的命名空间。此外,在配置文件中我通过<baseAddresses>标签为服务添加了基地址,因此在endpoint中,address为""。此时,调用服务的客户端配置文件也应与服务的配置保持一致:
注意,两个配置文件中的服务地址都是一样的,对于绑定的配置也基本一致。在通常的企业应用中,我们很少会采用自宿主方式托管服务,这是因为这种方式必须要在应用程序运行下,客户端才能够调用服务,且并不便于随时启动和停止服务。除了不具有易用性与易管理性之外,在可靠性、性能等诸多方面受到很多限制。但由于它简单、易于实现,因而往往用于开发期间的调试或演示环境。自托管宿主支持所有的绑定。
2、Windows Services宿主
Windows Services宿主则完全克服了自托管宿主的缺点,它便于管理者方便地启动或停止服务,且在服务出现故障之后,能够重新启动服务。我们还可以通过Service Control Manager(服务控制管理器),将服务设置为自动启动方式,省去了服务的管理工作。此外,Windows Services自身还提供了一定的安全性以及检测机制和日志机制。
Windows Services宿主的实现也非常简单。我们可以在Visual Studio中创建Windows Services项目。在创建项目之后,就可以创建一个继承了System.ServiceProcess.ServiceBase类的Windows服务类。Windows服务类继承了ServiceBase类的OnStart()和OnStop()方法,完成Windows服务的启动与停止。我们可以重写这两个方法,将ServiceHost的启动与关闭对应地放入这两个方法的实现中。例如我们创建的DocumentsExplorerWindowsService类:
namespace BruceZhang.WCF.DocumentsExplorer
{
public partial class DocumentsExplorerWindowsService : ServiceBase
{
private ServiceHost m_serviceHost = null;
public static void Main()
{
ServiceBase.Run(new DocumentsExplorerWindowsService());
}
public DocumentsExplorerWindowsService()
{
InitializeComponent();
ServiceName = "DocumentsExplorerService";
}
protected override void OnStart(string[] args)
{
if (m_serviceHost != null)
{
m_serviceHost.Close();
}
m_serviceHost = new ServiceHost(typeof(DocumentsExplorerService));
m_serviceHost.Open();
}
protected override void OnStop()
{
if (m_serviceHost != null)
{
m_serviceHost.Close();
m_serviceHost = null;
}
}
}
}
在Main函数中,我们通过ServiceBase.Run()静态方法创建Windows服务实例,并在Windows服务类的构造函数中,调用ServiceBase类的ServiceName属性指定服务名。在重写的OnStart()方法中,我们首先判断是否已经存在ServiceHost实例,如果不存在,则创建它。创建ServiceHost实例的方法与自托管宿主方式相同。
为了完成ServiceHost实例的创建,我们同样需要在项目中添加app.config配置文件,配置文件的内容与前完全一样。
如果在企业应用中要使用WCF技术,最佳的宿主方式我认为就是Windows Services,尤其是服务器的操作系统不是Vista的情况之下。它便于服务的管理,能够维持服务长时期的运行,同时它还支持所有的绑定,因而受到的限制最小。然而,这种方式唯一的缺点却是对宿主的部署相对比较复杂,必须通过.NET提供的Installutil.exe工具完成对服务宿主的安装(也可以通过安装包的自定义操作完成)。
若要完成对服务宿主的安装,我们还需要创建它的安装程序。我们可以自定义一个安装类,使其继承自System.Configuration.Install.Installer类。更简单的办法则是通过Windows服务提供的设计时支持,直接创建安装类。方法是在Windows服务例如DocumentsExplorerWindowsService的设计器视图下,通过单击右键,在快捷菜单中选择“Add Installer”,如图二所示:
图二 添加安装程序
创建的安装程序ExplorerServiceInstaller如下所示:
namespace BruceZhang.WCF.DocumentsExplorer
{
//Type services.msc to access the Service Control Manager(SCM) and browse the windows services
//Type installutil /u filename to uninstall the windows service
[RunInstaller(true)]
public partial class ExplorerServiceInstaller : Installer
{
private ServiceProcessInstaller m_process;
private ServiceInstaller m_service;
public ExplorerServiceInstaller()
{
InitializeComponent();
m_process = new ServiceProcessInstaller();
m_process.Account = ServiceAccount.LocalSystem;
m_service = new ServiceInstaller();
m_service.ServiceName = "DocumentsExplorerService";
Installers.Add(m_process);
Installers.Add(m_service);
}
}
}
在ExplorerServiceInstaller类中,ServiceAccount是一个枚举类型,可以设置为LocalService,LocalSystem,NetworkService以及User值。其中,LocalService的安全性最低,User值的安全性最高,需要有效的用户账号方才可以安装服务。
对于安装程序而言,也可以直接在设计器视图下设置它的属性。
安装程序直接建立在Windows服务的程序集中,编译之后会获得一个exe文件,例如DocumentsExplorer.exe。然后,我们通过在Visual Studio的Command Prompt模式下运行如下命令:
installutil DocumentsExplorer.exe
即可完成对服务宿主的安装。
打开服务控制管理器(可以在Command Prompt模式下输入Services.msc打开),可以看到名为DocumentsExplorer
图三 服务控制管理器
如果要卸载该服务宿主,可以通过installutil的/u开关卸载。
在企业应用中,我们往往会将该Windows服务设置为自动启动,可以简化管理员的工作。
关于如何使用ServiceBase
http://msdn.microsoft.com/en-us/zt39148a(zh-cn,VS.80).aspx
3、IIS宿主(说明,这里讲的IIS为IIS 6.0)
若要使用IIS宿主,需要为程序集中添加一个svc文件。我们可以通过为项目添加一个新项的方式添加svc文件:
4、WAS宿主
WAS是IIS 7.0的一部分,但也可以独立地安装与配置。WAS支持所有可用的WCF传输协议、端口与队列。
利用WAS托管服务与IIS宿主托管服务的方法并没有太大的区别,仍然需要创建svc文件,同时在IIS中需要在站点中创建应有程序指向托管应用程序,还可以设置访问服务的别名与应用程序池。
由于WAS诉诸支持所有的绑定,因此此时的服务绑定并不会受到宿主的限制。
联系客服