La detección de WCF sobre UDP provoca un alto uso de CPU y congestión de red
Este artículo le ayuda a resolver el error que se produce al usar la compatibilidad con Windows Communication Foundation (WCF) para WS-Discovery udp.
Versión del producto original: Windows Communication Foundation
Número KB original: 2777305
Síntomas
Al usar la compatibilidad con WCF WS-Discovery sobre UDP, es posible que vea uno o varios de los siguientes problemas:
- Un aumento inesperado en el uso de la CPU en el servidor.
- Tráfico de multidifusión superior al esperado.
- Un número inesperado de mensajes de error SOAP que se envían a través de la red en un entorno con .NET 4.5 instalado.
El problema puede producirse incluso si el servicio no se compiló para .NET 4.5 de destino, pero se ejecuta en un entorno donde .NET 4.5 está instalado.
Causa
Estos problemas pueden producirse por los siguientes motivos:
Control incorrecto de tipos de mensajes inesperados en algunos de los extremos de infraestructura interna de WCF Discovery.
El servicio WCF hospeda varios servicios de multidifusión UDP donde más de un servicio comparte una dirección de multidifusión y un puerto determinados.
Solución
Para resolver este problema, aplique una de las actualizaciones que se describen en los siguientes artículos de Knowledge Base (KB):
Solución alternativa
Para evitar estos problemas, elija una de las siguientes opciones:
Solución alternativa 1:
Forzar mediante programación el transporte para usar el canal dúplex, en lugar del que se
IReplyChannelintrodujo en 4.5. A continuación se muestra C# ejemplo de código para lograrlo en el lado del servicio (en el servidor). Con la siguiente clase, podemos tener una implementación personalizada deCanBuildChannelListenerque devuelve false paraIReplyChannel:class DisableIReplyChannelShapeBindingElement : BindingElement { public override BindingElement Clone() { return this; } public override T GetProperty<T>(BindingContext context) { return null; } public override bool CanBuildChannelListener<TChannel>(BindingContext context) { if (typeof(TChannel) == typeof(IReplyChannel)) { return false; } return context.CanBuildInnerChannelListener<TChannel>(); } }Al insertar esto en el enlace, la aplicación puede ocultar el hecho de que el transporte UDP admite la forma de , lo que
IReplyChannelannouncementEndpointevitará el problema. A continuación se muestra un método auxiliar que se usará más adelante:public static void DisableIReplyChannelShape(ServiceEndpoint endpoint) { CustomBinding customBinding = new CustomBinding(endpoint.Binding); //only do this if using the UdpTransport. if (customBinding.Elements.Find<UdpTransportBindingElement>()!= null) { customBinding.Elements.Insert(customBinding.Elements.Count - 1, new DisableIReplyChannelShapeBindingElement()); endpoint.Binding = customBinding; } }Puede impedir que el código del lado servidor envíe los mensajes de error que están causando problemas haciendo lo siguiente a su
announcementEndpoint:UdpAnnouncementEndpoint announcementEndpoint = new UdpAnnouncementEndpoint(); DisableIReplyChannelShape(announcementEndpoint);Solución alternativa 2:
Si no tiene acceso al código del lado servidor, puede agregar lo siguiente en su lugar en el lado cliente. Agregue este comportamiento al lado cliente
UdpDiscoveryEndpoint. Esta solución alternativa debe aplicarse a todos los clientes de detección tanto si se compilan en .NET 4.5 como en .NET 4.0.Nota
Microsoft sugiere encarecidamente la solución 1 (del lado servidor), ya que evita que el problema se produzca en clientes de detección wcf y que no son WCF. La solución alternativa 2 solo se aplica a los clientes WCF.
El código de cliente:
UdpDiscoveryEndpoint clientEndpoint = new UdpDiscoveryEndpoint(); clientEndpoint.Behaviors.Add(new WorkaroundBehavior()); var discoveryClient = new DiscoveryClient(clientEndpoint);Clase:
class WorkaroundBehavior : IEndpointBehavior { public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) { if (clientRuntime == null) { throw new ArgumentNullException("clientRuntime"); } clientRuntime.CallbackDispatchRuntime.UnhandledDispatchOperation.Invoker = new UnhandledActionOperationInvoker(); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) { } public void Validate(ServiceEndpoint endpoint) { } class UnhandledActionOperationInvoker : IOperationInvoker { public bool IsSynchronous { get { return true; } } public object[] AllocateInputs() { return new object[1]; } public object Invoke(object instance, object[] inputs, out object[] outputs) { outputs = new object[0]; return Message.CreateMessage(MessageVersion.None, string.Empty); } public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state) { throw new NotImplementedException(); } public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result) { throw new NotImplementedException(); } } }Solución alternativa 3:
Use una dirección de multidifusión única y/o un puerto para cada servicio que escuche en el
SOAP.UDPprotocolo.