外部客户端无法访问 Azure Service Fabric 上的 WCF 通信侦听器

Posted

技术标签:

【中文标题】外部客户端无法访问 Azure Service Fabric 上的 WCF 通信侦听器【英文标题】:External Clients unable to access WCF Communication Listener on Azure Service Fabric 【发布时间】:2016-11-09 06:37:04 【问题描述】:

我正在尝试使用 WCF 通信侦听器将运行 WCF 的 Azure Web 角色迁移到 Azure Service Fabric 中的无状态服务。一切都在我的本地服务集群中运行。发布到 Azure 后,集群中的其他服务能够访问无状态 WCF 服务,但外部(互联网)客户端(包括我的开发机器)由于暂时的网络错误而无法连接。

我验证了资源组中的负载均衡器具有针对端口 80 和 8080 的规则/探测,并且已经使用 TCP 和 HTTP 进行了测试。我还尝试在 WCF 客户端上设置分区解析器以指向服务集群上的“客户端连接端点”(默认情况下,它在服务集群中工作)。

此时,我不确定我是否有配置问题,或者外部(互联网)客户端是否有可能连接到运行 WCF 通信侦听器的无状态服务。

这是我的配置:

WCF 通信监听器

    private Func<StatelessServiceContext, ICommunicationListener> CreateListener()
    
        return delegate (StatelessServiceContext context)
        

            var host = new WcfCommunicationListener<IHello>(
                wcfServiceObject: this,
                serviceContext: context,
                endpointResourceName: "ServiceEndpoint",
                listenerBinding: CreateDefaultHttpBinding()
            );
            return host;
        ;
    

WCF 绑定

    public static Binding CreateDefaultHttpBinding()
    
        var binding = new WSHttpBinding(SecurityMode.None)
        
            CloseTimeout = new TimeSpan(00, 05, 00),
            OpenTimeout = new TimeSpan(00, 05, 00),
            ReceiveTimeout = new TimeSpan(00, 05, 00),
            SendTimeout = new TimeSpan(00, 05, 00),
            MaxReceivedMessageSize = int.MaxValue,
        ;
        var quota = new XmlDictionaryReaderQuotas
        
            MaxArrayLength = int.MaxValue,
            MaxDepth = int.MaxValue
        ;
        binding.ReaderQuotas = quota;
        return binding;
    

ServiceManifest.xml(我还使用了各种端口的默认 TCP 绑定)

<Endpoints>
  <Endpoint Name="ServiceEndpoint" Protocol="http" Port="8080" />
</Endpoints>

WCF 控制台应用程序

var address = new Uri("fabric:/ServiceFabricWcf.Azure/ServiceFabricWcf");
var client = GetClient(address, CreateDefaultHttpBinding());

try
  
     var results = client.InvokeWithRetry(x => x.Channel.Hello());
     System.WriteLine($"Results from WCF Service: 'results'");
     Console.ReadKey();
  
  catch (Exception e)
  
     System.Console.WriteLine("Exception calling WCF Service: 'e'");
  

WCF 客户端

    public static WcfServiceFabricCommunicationClient<IHello> GetClient(Uri address, Binding binding)
    
        //ServicePartitionResolver.GetDefault(); Works with other services in cluster
        var partitionResolver = new ServicePartitionResolver("<clientConnectionEndpointOfServiceCluster>:8080");
        var wcfClientFactory = new WcfCommunicationClientFactory<IHello>(binding, null, partitionResolver);
        var sfclient = new WcfServiceFabricCommunicationClient<IHello>(wcfClientFactory, address, ServicePartitionKey.Singleton);
        return sfclient;
    

WCF 客户端工厂

    public class WcfServiceFabricCommunicationClient<T> : ServicePartitionClient<WcfCommunicationClient<T>> where T : class

    public WcfServiceFabricCommunicationClient(ICommunicationClientFactory<WcfCommunicationClient<T>> communicationClientFactory,
                                               Uri serviceUri,
                                               ServicePartitionKey partitionKey = null,
                                               TargetReplicaSelector targetReplicaSelector = TargetReplicaSelector.Default,
                                               string listenerName = null,
                                               OperationRetrySettings retrySettings = null
                                               )
        : base(communicationClientFactory, serviceUri, partitionKey, targetReplicaSelector, listenerName, retrySettings)
    

    

【问题讨论】:

【参考方案1】:

这是一种适用于具有WebHttpBinding 的 WCF 服务的方法:https://github.com/loekd/ServiceFabric.WcfCalc

尝试更改您的代码,使其不使用 endpointResourceName,而是使用包含显式 URL 的 address。 URL 应该是集群的公共名称,例如 mycluster.region.cloudapp.azure.com

编辑:url 应该使用节点名,这样更容易。

string host = context.NodeContext.IPAddressOrFQDN;
  var endpointConfig = context.CodePackageActivationContext.GetEndpoint    
    ("CalculatorEndpoint");
  int port = endpointConfig.Port;
  string scheme = endpointConfig.Protocol.ToString();
  string uri = string.Format(CultureInfo.InvariantCulture, 
    "0://1:2/", scheme, host, port);

【讨论】:

谢谢。我能够保留我的 WsHttpBinding,并使用您发布的 HTTP uri 格式将“地址”添加到 WCF 通信侦听器。它在本地工作,并在发布到 Azure Service Fabric 时工作。值得注意的是,在这种情况下(互联网访问),您可以使用普通的 WCF 客户端,而不是发布示例(假设仅限内部)【参考方案2】:

这是我根据 LoekD 的回答更新的代码。

服务变更: 要使服务对 Internet 客户端可用,您必须向 WCFCommunicationListener 添加一个“地址”属性,以告诉服务要监听哪个端点(http://mycluster.region.azure.com 或 http://localhost)

客户端更改:使用普通的 WCF 客户端,没有任何 WCFCommunicationListener 引用。仅在服务结构内部使用 WCFCommunicationListener 客户端(我的原始代码在这种情况下工作正常)。

WCF 服务器监听器

return delegate (StatelessServiceContext context)
        
            string host = HostFromConfig(context);
            if (string.IsNullOrWhiteSpace(host))
            
                host = context.NodeContext.IPAddressOrFQDN;
            

            var endpointConfig = context.CodePackageActivationContext.GetEndpoint("ServiceEndpoint");
            int port = endpointConfig.Port;
            string scheme = endpointConfig.Protocol.ToString();
            //http://mycluster.region.cloudapp.azure.com or http://localhost
            string uri = string.Format(CultureInfo.InvariantCulture, "0://1:2", scheme, host, port);

            var listener = new WcfCommunicationListener<IHello>(
                wcfServiceObject: this,
                serviceContext: context,
                listenerBinding: CreateDefaultHttpBinding(),
                address: new EndpointAddress(uri)
            );
            return listener;
        ;

WCF 客户端应用程序

static void Main(string[] args)
    
        System.Console.WriteLine("\nPress any key to start the wcf client.");
        System.Console.ReadKey();

        System.Console.WriteLine("*************Calling Hello Service*************");
        try
        
            var binding = CreateDefaultHttpBinding();
            var address = new EndpointAddress("http://cluster.region.cloudapp.azure.com/"); //new EndpointAddress("http://localhost");
            var results = WcfWebClient<IHello>.InvokeRestMethod(x => x.Hello(),binding, address ); 

            System.Console.WriteLine($"*************Results from Hello Service: 'results'*************");
            Console.ReadKey();
        
        catch (Exception e)
        
            System.Console.WriteLine($"*************Exception calling Hello Service: 'e'*************");
        
    

WCF 绑定

public static Binding CreateDefaultHttpBinding()

    var binding = new WSHttpBinding(SecurityMode.None)
    
        CloseTimeout = new TimeSpan(00, 05, 00),
        OpenTimeout = new TimeSpan(00, 05, 00),
        ReceiveTimeout = new TimeSpan(00, 05, 00),
        SendTimeout = new TimeSpan(00, 05, 00),
        MaxReceivedMessageSize = int.MaxValue,
    ;
    var quota = new XmlDictionaryReaderQuotas
    
        MaxArrayLength = int.MaxValue,
        MaxDepth = int.MaxValue
    ;
    binding.ReaderQuotas = quota;
    return binding;

外部/互联网 WCF 客户端示例:

public abstract class WcfWebClient<T> where T : class

    public static TResult InvokeRestMethod<TResult>(Func<T, TResult> method, Binding binding, EndpointAddress address)
    
        var myChannelFactory = new ChannelFactory<T>(binding, address);
        var wcfClient = myChannelFactory.CreateChannel();

        try
        
            var result = method(wcfClient);
            ((IClientChannel)wcfClient).Close();
            return result;
        
        catch (TimeoutException e)
        
            Trace.TraceError("WCF Client Timeout Exception" + e.Message);
            // Handle the timeout exception.
            ((IClientChannel)wcfClient).Abort();
            throw;
        
        catch (CommunicationException e)
        
            Trace.TraceError("WCF Client Communication Exception" + e.Message);
            // Handle the communication exception.
            ((IClientChannel)wcfClient).Abort();
            throw;
        
    

【讨论】:

【参考方案3】:

您还可以尝试通过在服务实现上添加以下属性来为 WCF 服务启用任何地址绑定模式:

[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]

【讨论】:

以上是关于外部客户端无法访问 Azure Service Fabric 上的 WCF 通信侦听器的主要内容,如果未能解决你的问题,请参考以下文章

Azure Service Fabric 群集:无法为 RPC 访问机器 10.0.0.X

Azure Synapse 分析:访问外部表时出错

Azure Service Fabric 错误:访问被拒绝。部署失败

设置service的nodeport以后外部无法访问对应的端口的问题

Azure Service Fabric 中的有状态服务和外部持久性之间的转换

了解何时使用有状态服务以及何时依赖 Azure Service Fabric 中的外部持久性