在Worker Role中承载WCF服务,首先需要配置Endpoint,这个例子配置了Internal和External两个Endpoint,Internal Endpoint仅供Cloud Service内部调用(外部无法访问),External Endpoint可供本地(云之外)的应用调用,配置如图所示:
此处使用TCP协议,注意到对于Internal Endpoint,其端口是动态分配的,在运行时才能获取到分配的端口号。
配置好Endpoint以后,则可以发布WCF服务,代码如下:
this.serviceHost = new ServiceHost(typeof(UserService));
ServiceMetadataBehavior metadataBehavior;
metadataBehavior = this.serviceHost.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
this.serviceHost.Description.Behaviors.Add(metadataBehavior);
}
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.None);;
Binding mexTcpBinding = MetadataExchangeBindings.CreateMexTcpBinding();
//Get the Internal Endpoint(dynamic port) at runtime
RoleInstanceEndpoint internalEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["Internal"];
this.serviceHost.AddServiceEndpoint(
typeof(IUserContract),
tcpBinding,
String.Format("net.tcp://{0}/Internal", internalEndpoint.IPEndpoint));
this.serviceHost.AddServiceEndpoint(
typeof(IMetadataExchange),
mexTcpBinding,
String.Format("net.tcp://{0}/MEX", internalEndpoint.IPEndpoint));
RoleInstanceEndpoint externalEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["External"];
this.serviceHost.AddServiceEndpoint(
typeof(IUserContract),
tcpBinding,
String.Format("net.tcp://{0}/External", externalEndpoint.IPEndpoint));
this.serviceHost.AddServiceEndpoint(
typeof(IMetadataExchange),
mexTcpBinding,
String.Format("net.tcp://{0}/MEX", externalEndpoint.IPEndpoint));
try
{
this.serviceHost.Open();
Trace.WriteLine("WCF service host started successfully.", "Information");
}
catch (TimeoutException timeoutException)
{
Trace.WriteLine("The service operation timed out. " + timeoutException.Message, "Error");
}
catch (CommunicationException communicationException)
{
Trace.WriteLine("Could not start WCF service host. " + communicationException.Message, "Error");
}
catch (Exception ex)
{
Trace.WriteLine(ex.Message, "Error");
}
这段代码是典型的发布WCF服务的代码,通过添加MEX终结点使得可以解析元数据。在WorkerRole的Run方法中执行该段代码,当Worker Role启动时则会启动WCF服务。
WCF服务发布完成以后,我们来尝试通过Cloud Service内部和外部方式来访问这两个Endpoint。
首先,我们在Cloud Service内部的Web Role中来访问Internal Endpoint。
在访问内部Endpoint时,需要获得Worker Role的Instance,通过InstanceEndpoints获取动态分配的端口号。Client端调用服务的代码如下:
IUserContract service = null;
NetTcpBinding tcpBinding = new NetTcpBinding(SecurityMode.None, false);
EndpointAddress address = null;
string endpointType = "Internal";
string serviceRoleName = "WorkerRoleName";
Role serviceRole = RoleEnvironment.Roles[serviceRoleName];
if (serviceRole.Instances.Count > 0)
{
RoleInstance instance = serviceRole.Instances[0];
RoleInstanceEndpoint endpoint = instance.InstanceEndpoints[endpointType];
address = new EndpointAddress(string.Format("net.tcp://{0}/" + endpointType, endpoint.IPEndpoint));
}
if(address != null)
{
service = ChannelFactory<IUserContract>.CreateChannel(tcpBinding, address);
}
return service;
请注意:在初始化NetTcpBinding时,由于服务端的安全模式为None,所以客户端必须保持一致。
接下来,我们通过本地WcfTestClient.exe来测试External Endpoint。
(WcfTestClient.exe所在目录,C:\Program Files (x86)\Microsoft Visual Studio 11.0\Common7\IDE)
在WcfTextClient加入服务地址:net.tcp://[yourdomain.cloudapp.net]:10100/mex,其中yourdomain需要替换成自己的Cloud Service的名字。
具体地址请登录Portal获取,如图所示:
通过WcfTestClient解析后,我们发现不仅是External Endpoint,Internal Endpoint同样被解析出来了。如配置文件所示:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding name="NetTcpBinding_IUserContract" sendTimeout="00:05:00">
<security mode="None" />
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://10.146.150.136:20000/Internal" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IUserContract" contract="IUserContract"
name="NetTcpBinding_IUserContract" />
<endpoint address="net.tcp://10.146.150.136:10100/External" binding="netTcpBinding"
bindingConfiguration="NetTcpBinding_IUserContract" contract="IUserContract"
name="NetTcpBinding_IUserContract1" />
</client>
</system.serviceModel>
</configuration>
我们需要将Internal Endpoint的配置删除,因为根据Azure的安全策略,该Endpoint从外部无法访问。
还有一点非常关键的是,我们看到在address URL中,域名被替换成了IP,这是Worker Role所在虚拟机的内部IP,通过这个IP我们是无法成功调用服务的,需要将IP替换成域名,让Azure DNS去自动解析。修改后的配置如下:
<?xml version="1.0" encoding="utf-8"?> <configuration> <system.serviceModel> <bindings> <netTcpBinding> <binding name="NetTcpBinding_IUserContract" sendTimeout="00:05:00"> <security mode="None" /> </binding> </netTcpBinding> </bindings> <client> <endpoint address="net.tcp://[yourdomain.cloudapp.net]:10100/External" binding="netTcpBinding" bindingConfiguration="NetTcpBinding_IUserContract" contract="IUserContract" name="NetTcpBinding_IUserContract1" /> </client> </system.serviceModel> </configuration>
同理,我们在Visual Studio通过Add Service Reference来添加WCF服务时也需要做出上述修改。然后,我们就能成功从外部调用Worker Role Input类型的终结点了。