【问题标题】:External Clients unable to access WCF Communication Listener on Azure Service Fabric外部客户端无法访问 Azure Service Fabric 上的 WCF 通信侦听器
【发布时间】: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)
    {

    }
}

【问题讨论】:

    标签: c# wcf azure azure-service-fabric


    【解决方案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.comhttp://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)]

      【讨论】:

        猜你喜欢
        • 2016-04-22
        • 2016-01-19
        • 2021-02-09
        • 1970-01-01
        • 2016-07-13
        • 2019-05-29
        • 2017-05-02
        • 2012-10-22
        • 1970-01-01
        相关资源
        最近更新 更多