Original (原创) by Teddy’s Knowledge Base
Content (目录)
(1) WCF Configuration Centralization (WCF配置集中管理)
(2) WCF Automatic Deployment (WCF自动化部署)
(3) WCF Automatic Service Locating (WCF自动化服务定位)
(4) WCF Database Paging & Sorting (WCF数据库分页和排序)
(5) WCF Based ASP.NET DataSouce Control (基于WCF的数据源控件)
(6) 1 + 2 + 3 + 4 + 5 = ?
摘要
本文提供一种简化在多服务器和服务器群上自动化部署WCF服务的方案。
正文
什么是自动化部署?
自动化部署指自动化地部署一个应用程序到它的运行环境。一般,通过创建批处理脚本来代替手动执行部署过程。对于WCF来说,最好的部署环境是IIS,尤其是包含了WAS的IIS7。最好的IIS上的部署工具是微软即将发布的Web Deployment Tool工具。可以访问其官方网站获得更多详情。
如果使用批处理脚本来自动化部署,那么自动化部署的复杂度就依赖于脚本的复杂度。一个批处理脚本可能包含许多任务,诸如:文件复制、DLL注册、IIS设置、Windows服务设置,甚至更复杂的依赖于其他资源和服务的任务。即使我们用像Web Deployment Tool这样功能强大的部署工具,它也只是能帮你更容易的创建部署脚本,并不会减小实际需要执行的任务的复杂程度。所以说,想要真正简化部署的过程,降低部署成本,我们更应该寻找能够减少或者转移走部署过程中的各种依赖的方法。
配置集中管理对自动化部署的好处
前一章说到了配置集中管理,可能的配置包括服务的元数据、数据库连接字串、应用设置、策略等等。实际上,你很容易就能想象,绝大多数部署过程中可能有的依赖,都和这些配置有关。换句话说,配置管理越集中化,自动化部署也就能变得更简单也更轻巧。
例如。从Dev、QA、Staging到Live环境:
- 消费和提供服务的元数据因为不同的端点地址、安全和日志策略可能不同。
- 数据库服务器可能不同,所以,数据库连接字串也可能不同。
- 任何关联整合的外部资源,诸如:URI,链接,缓存服务器地址等等都可能不同。
如果我们保证大多数配置都能被集中管理,那么在最理想的情况下(实际上,实践中,绝大多数情况下都能达到最理想情况),我们甚至能使得在任意环境部署的文件完全一致,不需要修改。
IIS对简化自动化部署的好处
大多数读者可能已经在IIS里至少部署过最简单的WCF演示程序了,感觉有任何难的吗?想必最可能让人觉得有一点复杂的是元数据(服务行为、端点)的配置。前面讨论过了,如果我们能从应用程序配置文件中移除这个复杂度,你是不是会说他已经足够简单了呢?这是因为IIS还给我们提供了另三个好处:
- 自动地址绑定。
如果一个WCF服务部署在IIS里,就不需要显式指定服务发布的地址了,因为地址可以通过部署的文件夹和网站的地址和端口计算出来。
- 自动端口共享。
网站的端口自动地被所有部署在网站中的服务共享。更进一步,在IIS7中,因为IIS7支持诸如TCP这样的更多的接口,部署在IIS7中的服务甚至可以自动共享相同的TCP端口。我们甚至可以在IIS7里让多个不同类型的端点绑定(WSHttp, NetTCP, NetPipedName)指向单一的一个完全相同的服务地址(不包含地址的前缀如’http:’部分)。
- 自动的服务宿主管理。
在其他的WCF服务宿主环境,诸如命令行程序或Windows服务程序中,必须要写额外的代码来管理ServiceHost类的实例的生命周期;而在IIS中,我么们可以简单的写一个.svc文件,IIS通过.NET Framework提供的ServiceHostFactory类可以自动为你管理ServiceHost实例的生命周期。尽管.NET Framework提供的ServiceHostFact的默认实现只能从应用程序配置文件读取WCF服务的元数据配置,我们可以写一个自定义的ServiceHostFactory类,从其他地方,例如我们提到过的集中化的配置中心读取元数据配置。下面的代码是一个从集中化的Store读取元数据配置的自定义的ServiceHostFactory实现片断:
1 public class WcfServiceHostFactory : ServiceHostFactory
2 {
3 //…
4
5 protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
6 {
7 var serviceContracts = WcfServiceHelper.GetServiceContracts(serviceType);
8
9 var host = new ServiceHost(serviceType, BuildBaseAddresses(serviceContracts));
10 foreach (var serviceContract in serviceContracts)
11 {
12 var endpoints = EndpointStore.GetServerEndpoints(serviceContract);
13 if (endpoints.Count == 0)
14 return host;
15
16 foreach (var endpoint in endpoints)
17 {
18 var address = WcfServiceHelper.BuildAddress(endpoint);
19 if (address == default(Uri)) continue;
20
21 var binding = WcfServiceHelper.BuildBinding(serviceContract, endpoint);
22
23 if (binding == null) continue;
24
25 if (!IsBehaviorConfigured<ServiceMetadataBehavior>(host))
26 {
27 var smb = new ServiceMetadataBehavior();
28 host.Description.Behaviors.Add(smb);
29 }
30
31 if (endpoint.MexBindingEnabled)
32 {
33 host.AddServiceEndpoint(typeof (IMetadataExchange), new CustomBinding(binding), "mex");
34 }
35
36 if (!IsBehaviorConfigured<ServiceThrottlingBehavior>(host))
37 {
38 var serviceThrottle = new ServiceThrottlingBehavior();
39 if (endpoint.MaxConcurrentCalls.HasValue)
40 serviceThrottle.MaxConcurrentCalls = endpoint.MaxConcurrentCalls.Value;
41 if (endpoint.MaxConcurrentInstances.HasValue)
42 serviceThrottle.MaxConcurrentInstances = endpoint.MaxConcurrentInstances.Value;
43 if (endpoint.MaxConcurrentSessions.HasValue)
44 serviceThrottle.MaxConcurrentSessions = endpoint.MaxConcurrentSessions.Value;
45 host.Description.Behaviors.Add(serviceThrottle);
46 }
47
48 if (!IsBehaviorConfigured<ServiceDebugBehavior>(host) && endpoint.IncludeExceptionDetailInFaults.HasValue && endpoint.IncludeExceptionDetailInFaults.Value)
49 {
50 var serviceDebug = new ServiceDebugBehavior
51 {
52 IncludeExceptionDetailInFaults =
53 endpoint.IncludeExceptionDetailInFaults.Value
54 };
55 host.Description.Behaviors.Add(serviceDebug);
56 }
57
58 host.AddServiceEndpoint(serviceContract, binding, address);
59 }
60 }
61
62 return host;
63 }
64
65 }
提示
- 一般,在.svc文件中指定的Type是服务的实现类的类型,ServiceHostFactory的CreateHostHost方法会将这个类型作为参数传给ServiceHost类的构造函数。但是,也可以只在.svc文件里指定服务的接口类型,然后通过使用诸如Unity和Castle这样的IoC容器,在你的自定义ServiceHostFactory的实现中,先从服务接口类型获取服务的实现类型,然后再传给ServiceHost的构造函数。
- 在自定义的ServiceHostFactory实现中,要小心捕获创建ServiceHost的过程中的错误并记录日志,因为任何在ServiceHost的创建过程中抛出的的异常的详细信息都不会反映给服务的客户端。客户端永远不会知道是为什么,只会收到一个连接被拒绝的消息。不过我们还是可以像调试其他Web应用程序一样,用VS2008的远程调试工具来调试自定义的ServiceHostFactory的。
参考
(1) SOA Design Pattern Catalog: http://www.soapatterns.org/
//我是结尾符,待续…