在这一步中我们将实现为宿主应用程序(TutorialHost)添加主业务服务功能,通过宿主应用程序来托管我们需要的业务服务。并且通过学习来掌握如果通过配置服务体系架构来更好的管理应用程序中的业务体系架构。
第六步:创建一个主业务服务
在这个步骤中我们将添加一个主业务服务并将它托管在宿主应用程序中,需要的项目包括:
Service contract project
Service data contract project
Service implementation project
这些项目将被添加到我们之前创建的TutorialHost解决方案中。主业务服务包含了你希望托管的任何将要实现的(或者已经被实现的)业务逻辑。添加这些项目以后,我们需要把这些服务关联到Tutorial.Host项目的宿主启动逻辑中去(以下将涉及这些内容)。
1.在TutorialHost解决方案中添加TuturialDataContract、TutorialServiceContract和TutorialServiceImplementation项目,这些项目在[stocktrader setup dir]\configuration\tutorial\目录中可以找到。添加完后你的解决方案中应该包含6个项目,而且可以一起通过编译。
2.在我们的TutorialServiceImplementation项目中我们现在需要修改启动逻辑来初始化一个新的主业务虚拟宿主。
3.首先我们要为TutorialHost项目添加需要的项目引用,选择TutorialServiceImplementation和TutorialServiceContract项目。
4.接着在TuturialHost项目中打开program.cs文件。
5.你需要在文件中添加Using Tutorial.ServiceImplementation语句。
6.你需要在宿主启动逻辑中添加如下两行代码来实现服务的托管,如下用加粗显示:
Application.SetCompatibleTextRenderingDefault(false);
//The key call to create our list of runtime hosts to be initialized. See tutorial for parameters expected.
List<ServiceHostInfo> startupList = new List<ServiceHostInfo>();
ServiceHostInfo tutorialHost = new ServiceHostInfo("Tutorial Service Host", false, null, new object[] { }, new TutorialService());
startupList.Add(tutorialHost);
//This starts the Windows program main form, which is a class that inherits from a base class with all the Windows logic/display layout pre-provided.
Application.Run(new ServiceHostConsole(
new Tutorial.Settings.Settings(),
new ConfigurationService(),
new ConfigService.ServiceNodeCommunicationImplementation.NodeCommunication(),
null,
new ConfigurationActions(),
startupList,
null,
new object[] { },
null)
);
7.完成上述步骤后你现在可以编译和通过使用F5来运行Tutorial.Host.exe了。
第七步:添加一个虚拟主机和主业务服务终结点
在这个步骤中我们将为托管的业务服务配置一个虚拟主机和一个终结点。这个步骤中我们将利用自托管的TutorialService服务来实现,当然这个过程也同样适合于托管在IIS中的服务;需要注意的是托管在IIS宿主中的服务在每次添加时需要创建一个.svc服务入口访问文件添加到你网站中去。你可以参考示例提供的node.svc或者config.svc文件;这个.svc文件内部总是使用相同的factory类处理,你仅仅需要做的是,修改服务的名称为你提供的虚拟主机的名称。
1.保证Tuturial.Host.exe先启动,在浏览器中输入http://localhost/configweb,打开配置管理网站(ConfigWeb)。
2.使用用户名localadmin,密码yyy登入到http://localhost:8001/tutorial/config配置服务地址。
3.点击Hosted Svcs菜单按钮或链接。
5.点击Add Virtual Host按钮。
6.准确的输入如下的信息:
VHost Friendly Name: Tutorial Service Host
Service Implementation Class: Tutorial.ServiceImplementation.TutorialService
Service Behavior Configuration: SimpleServiceBehaviors
Service Host Environment: Windows Application
6.点击Add按钮。可能需要几秒钟的时间来完成操作。
7.选择Return to Virtual Host List链接。
8.选择上面红色圈圈标记的服务宿主名为Tutorial Service Host的Select链接。
9.点击Add Service Endpoint按钮。
10.你需要输入如上红色圈圈标记的内容。在这里需要注意一下,Hosted Service Name对大小写是敏感的。
Hosted Service Name: Tutorial Service wsHttp
Binding Type: wsHttpBinding
Host Binding Configuration Name: Host_WsHttpBinding
Internal Client Configuration Name: None
Virtual Path: tutorial/svc
Port: 8000
Add MetaData Exchange for this address: checked
补充说明1:在IIS中托管你的服务。示例中是使用自托管的形式托管服务的,但在很多场景下你可能需要使用IIS来托管你的服务,托管服务的虚拟路径一般总是应用程序的虚拟目录+"/"+.svc后缀的服务访问文件(如:"mywebapp/mybusinessservice.svc")。服务访问文件内容看起来如下所示:
Service="[你服务的虚拟主机名称]"
Factory="TutorialClient.ClientConfigurationImplementation.ConfigurationActions" %>
注意在一个宿主应用程序中所有的托管服务(node, configuration, node cache, primary business services)上面的factory的内容总是相同的,factory是通过service名称来区分不同的服务的,这个名称就是你为服务设置的虚拟主机的名称。IIS托管服务的端口一般总是80;443(SSL);或者808(在IIS 7中WAS托管TCP终结点)。在宿主中,.svc文件中的这个factory类应该总是指向ConfigurationActions类。
补充说明2:在其它非IIS中托管你的服务。你应该确保你想要设置的每个虚拟主机使用唯一的端口号,或者保证一个端口相同的网络配置只使用一次(http, net.tcp, https等)。例如:如果你有多个宿主终结点(包含node和configuration服务)已经在一个http地址6000端口上监听;尝试在同一个宿主程序中在6000端口上为其指定另一种不同网络配置的终结点(如net.tcp)就是非法了;但是为其指定同样基于http网络配置的其它终结点就是合法的。
11.其它的字段保留默认值,点击Add按钮。
12.在添加操作完成以后(可能需要几秒钟),选择Return to Hosted Services for this Virtual Host链接。
13.选择Configuration菜单项,然后点击SOA Map按钮。
14.你可以看到主服务终结点已经添加(并已经激活);但是现在的状态是灰色的;并且Tutorial Service Cluster高亮显示为黄色。这是因为我们还没有在宿主应用程序的配置文件中提供一个内部的客户端配置,通过宿主自处理,这个配置将允许我们为终结点执行在线检查。手工实现这个目的是很容易的,我们将使用Windows SDK svcutil.exe工具来为我们生成客户端定义,可以参考它的使用示例。如果你没有安装Windows SDK,省略以下这些步骤直到24以后,然后在你的TutorialHost项目的app.config文件中进行手动输入。
15.这里的关键是因为宿主要使用这些定义检查它们拥有的服务终结点,所以我们将总是为宿主拥有的服务生成客户端绑定配置和客户端定义配置节;并且这样保证宿主能它自己的(对等节点的)服务终结点通讯。
16.从Windows开始菜单,打开Windows SDK应用程序组,并且打开一个CMD Shell。
17.确保Tutorial.Host.exe自托管应用程序已经启动。
18.在命令输入提示打开后,输入如下命令:Svcutil.exe http://localhost:8000/tutorial/svc/mex,当你运行在Vista SP1 或者 Windows Server 2008上时,如果svcutil.exe出错,查看命令提示的第2页,运行它并且修正这个问题。
19.工具将为我们生成2个文件,现在我们只对output.config文件感兴趣(生成的文件在运行svcutil.exe的相同目录中)。
20.在Visual Studio打开output.config文件。文件看起来如下所示,同时包含了一个<client>定义和与这个定义相关的客户端绑定配置。
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="WSHttpBinding_ITutorialService"
closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas
maxDepth="32" maxStringContentLength="8192"
maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://lglvista4.redmond.corp.microsoft.com:8000/tutorial/svc"
binding="wsHttpBinding"
bindingConfiguration="WSHttpBinding_ITutorialService"
contract="ITutorialService"
name="WSHttpBinding_ITutorialService" />
</client>
</system.serviceModel>
</configuration>
21.根据配置服务和配置网站期望的命名规范(查看Configuration Service 2.0 Technical Guide)我们将重新命名这些元素。
22.首先在<binding>配置节,binding元素的name属性将被从"WsHttpBinding_ITutorialService"重命名为"Client_WsHttpBinding_ITutorialService"。
23.接下来,在<client>配置节,endpoint元素的bindingConfiguration属性的将被新的取代,同时我们将把name的属性值也改为相匹配的"Client_WsHttpBinding_ITutorialService"(注意我们也可以使用不匹配的;在绝大多数情况下,改成匹配的会让事情变得更加清晰)。
24.最后在<endpoint>配置节,把服务的contract属性改为实际使用的服务契约由全限定命名空间组成(Tutorial.ServiceContract.ITutorialService)。在这个示例中我们同时拥有服务和客户端代码,因此我们可以重用已存在的契约。 Svcutil.exe工具在生成的TutorialService.cs文件中也包含了它自己的服务契约(ITutorialService);但是,因为我们在现有的程序集中已经包含了此契约,所以我们不需要再使用它了。通常,如果服务端和客户端的开发都受你控制(与通过svcutil工具生成的代理来创建额外的专用程序集比较),在服务和客户端之间最好能共享这些架构如服务契约和数据契约(但不需要实现逻辑)。当服务的开发不受你控制时,在客户端你就应该需要使用svcutil.exe工具生成的TutorialService.cs文件来提供服务契约和数据契约(可以是任何符合工业标准的服务,不用关心托管的是何种平台)。
25.修改完以后,保存文件,文件看起来如下所示:
<configuration>
<system.serviceModel>
<bindings>
<wsHttpBinding>
<binding name="Client_WsHttpBinding_ITutorialService"
closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" messageEncoding="Text"
textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas
maxDepth="32" maxStringContentLength="8192"
maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="None">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
<client>
<endpoint address="http://lglvista4.redmond.corp.microsoft.com:8000/tutorial/svc"
binding="wsHttpBinding"
bindingConfiguration="Client_WsHttpBinding_ITutorialService"
contract="Tutorial.ServiceContract.ITutorialService"
name="Client_WsHttpBinding_ITutorialService" />
</client>
</system.serviceModel>
</configuration>
26.接下来复制<binding></binding>配置节中的信息。
27.接下来在Visual Studio TutorialHost项目中,停止Tutorial.Host.exe程序实例的运行。
28.打开这个项目的app.config文件。
29.将刚才复制到信息拷贝到配置文件中,注意需要放在</wsHttpBinding>结束标签的前面。实际上,添加的这个绑定跟模板上面提供的没有安全设置的wsHttpBinding是一样的,之所以这么做的目的就是让我们学习如何添加自定义或者svcutil工具生成的绑定配置,让配置网站ConfigWeb可以显示他们。模板中提供的很多绑定并不会全部都用到,你可以删除这些不使用的绑定,或者修改他们的定义,但是在修改宿主的Node_Svc和Config_Svc已存在绑定的时候要特别小心,除非你已经修改了Node和Config的终结点的定义。当然把所有的模板提供的定义都保留在配置文件中也没有什么危害,并且当你需要使用到这些配置的时候你就可以快速的利用了。注意在新添加和修改的时候需要注意在(Configuration Service 2.0 Technical Guide)中提及的命名约定。
30.总是保证在正确的配置节添加你的绑定配置(包括重命名),这些绑定配置节是通过绑定类型进行分组(netTcpBinding, basicHttpBinding, wsHttpBinding等)。
31.保存App.config文件,现在文件包含了新的绑定配置节。
32.接下来,你将需要使用svcutil.exe工具生成的在output.config文件中的<client>配置节定义。在你的宿主客户端配置节中只需要放入<endpoint>的定义。再次打开output.config文件,复制刚才你修改的<endpoint>配置节信息。
binding="wsHttpBinding"
bindingConfiguration="Client_WsHttpBinding_ITutorialService"
contract="Tutorial.ServiceContract.ITutorialService"
name="Client_WsHttpBinding_ITutorialService" />
33.现在把复制的内容添加到TutorialHost项目的app.config文件的<client>配置节定义中,在<client>配置节中其它已经存在的定义是为Configuration和Node Services生成的默认客户端定义。
34.现在app.config文件的这个配置节点看起来如下所示。
<endpoint address="http://configserver" binding="basicHttpBinding" bindingConfiguration="Client_ConfigSvc_BasicHttpBinding"
contract="ConfigService.ServiceConfigurationContract.IServiceConfiguration" name="Client_ConfigSvc_BasicHttpBinding" />
<endpoint address="http://configserver" binding="wsHttpBinding" bindingConfiguration="Client_ConfigSvc_WsHttpBinding"
contract="ConfigService.ServiceConfigurationContract.IServiceConfiguration" name="Client_ConfigSvc_WsHttpBinding"/>
<endpoint address="http://configserver" binding="netTcpBinding" bindingConfiguration="Client_ConfigSvc_TcpBinding"
contract="ConfigService.ServiceConfigurationContract.IServiceConfiguration" name="Client_ConfigSvc_TcpBinding" />
<endpoint address="http://configserver" binding="basicHttpBinding" bindingConfiguration="Client_NodeSvc_BasicHttpBinding"
contract="ConfigService.ServiceNodeCommunicationContract.INodeCommunication" name="Client_NodeSvc_BasicHttpBinding" />
<endpoint address="http://configserver" binding="wsHttpBinding" bindingConfiguration="Client_NodeSvc_WsHttpBinding"
contract="ConfigService.ServiceNodeCommunicationContract.INodeCommunication" name="Client_NodeSvc_WsHttpBinding" />
<endpoint address="http://configserver" binding="netTcpBinding" bindingConfiguration="Client_NodeSvc_TcpBinding"
contract="ConfigService.ServiceNodeCommunicationContract.INodeCommunication" name="Client_NodeSvc_TcpBinding" />
<endpoint address="http://lglvista4.redmond.corp.microsoft.com:8000/tutorial/svc" binding="wsHttpBinding" bindingConfiguration="Client_WSHttpBinding_ITutorialService"
contract="ITutorialService" name="Client_WSHttpBinding_ITutorialService" />
</client>
35. 注意在这个配置节中的终结点的绝对地址(URI)并没有实际使用,但是我们必须提供一个有效的终结点。在这里你可以保留原来的定义,或者把终结点改为http://configserver或者虚构的其它终结点(但是必须提供合法的URI,如http://configserver; https://configserver; net.tcp//configserver; net.msmq://configserver)。可能会在下一个配置服务发布版本中使用<client>配置节中的所有内容,当然也可能移除这些内容;但是现在它为我们这些WCF开发人员去生成客户端配置,这些配置可能包含终结点行为(可能提供需要使用X509客户端证书的信息),提供了一个易于理解的方式;并且有时候还能为终结点提供唯一性的标识;虽然这儿为TutorialService这个示例既不需要用到,更不用靠svcutil.exe来生成。但你可以参考StockTraderWeb application和StockTrader Business Services的<client>配置来了解如何使用这些元素。
36. 如果在SOA Map中查看在线的节点的终结点仍然显示红色(或灰色),那就需要检查配置控制台(Configuration Console)或者应用程序的事件日志(Application Event Log)了(这个事件的来源将基于你开始使用配置数据库生成工具时所命名的来源)。这个控制台(或事件日志)将记录在.NET检查状态时返回的异常信息;大部分原因都是在配置<client>节终结点时的不正确操作所致。返回的异常信息将帮助我们解决这些问题。
37.在TutorialHost解决方案中,保存app.config文件,并重新编译应用程序,按F5启动。
38.为TutorialHost中重新配置网站(地址http://localhost:8001/tutorial/config)。
39.选择上面红色圈圈标注的Hosted Svcs。
40.在虚拟主机列表中选择Tutorial Service Host的Select链接。
41.点击上面红色圈圈标注的主服务终结点的Edit链接。
42.现在Internal Client Configuration Name的下拉列表中应该你的新客户端配置(Client_WsHttpBinding_ITutorialService)。选择这个客户端终结点配置名。如果还没有下拉列表中显示,重新回到20步并且检查所以的东西都已经设置正确。如果在<endpoint>配置节中服务的契约没有正确命名(需要全限制命名空间,不仅仅是接口名);那么就不会显示在下拉列表中,因为配置网站会检查全限定的契约名称是否和绑定相匹配,所以在24步要看仔细了。
43.点击Update按钮。
44.接下来回到配置网站主页选择主配置菜单链接。
45.点击SOA Map按钮,并且你现在应该看到没有任何警告;主服务终结点应该变成绿色,而不是原先的灰色了。
其它服务终结点,使用不同的绑定也能通过这种方式添加进去。注意,在添加服务终结点前,你还能选择创建你自己的宿主绑定配置;它们依照"Host_"命名约定定义;一旦将这些添加到配置文件中(在绑定类型的适当配置节中);并且会重新启动宿主,然后再在UpdateHostedService.aspx页面添加服务终结点时,在这个页上通过单选按钮选择绑定类型,只要已经有存在的匹配绑定类型,它们就会显示在Host Binding Configuration Name的项中。