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

本文关键字:上的 Fabric WCF 通信 侦听器 Service Azure 客户端 访问 外部 | 更新日期: 2023-09-27 17:55:42

我正在尝试使用 WCF 通信侦听器将运行 WCF 的 Azure Web 角色迁移到 Azure Service Fabric 中的无状态服务。在我的本地服务集群中一切正常。发布到 Azure 后,群集中的其他服务可以访问无状态 WCF 服务,但外部(Internet)客户端(包括我的开发计算机)无法连接,并出现暂时性网络错误。

我验证了资源组中的负载均衡器是否具有端口 80 和 8080 的规则/探测,并已使用 TCP 和 HTTP 进行了测试。我还尝试在 WCF 客户端上设置分区解析程序,以将"客户端连接终结点"指向服务群集(从默认值开始,它在服务群集中工作)。

此时,我

不确定我是否有配置问题,或者外部(Internet)客户端是否有可能连接到运行 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;
    }

服务清单.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)
    {
    }
}

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

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

尝试更改代码,使其不使用endpointResourceName而是address包含显式 URL。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);

这是我基于 LoekD 的答案更新的代码。

服务变更:若要使服务可供 Internet 客户端使用,必须向 WCFCommunicationListener 添加一个"Address"属性,以告知服务要侦听哪个终结点(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;
        }
    }
}

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

[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]