Establecer KeepAlive en false para interactuar con hosts externos en un complemento

Categoría: rendimiento

Potencial de impacto: alto

Síntomas

Si un complemento hace solicitudes web externas e intenta usar KeepAlive en una conexión cerrada, el complemento no podrá en definitiva ejecutar la solicitud web. Si se registra el complemento:

  • Los usuarios pueden experimentar de forma sincrónica:

    • Aplicaciones basadas en modelos que dejan de responder
    • Interacciones lentas con el cliente
    • El explorador deja de responder
  • De forma asincrónica, las ejecuciones de complementos pueden prolongarse durante más tiempo antes de dar error.

Instrucciones

  1. En HTTP 1.1, todas las conexiones se consideran persistentes (KeepAlive es true) a menos que se declaren al contrario. Debido al hecho de que los complementos se ejecutan de forma aislada, el servicio de espacio aislado traduce en ellos creando ejecuciones muy cortas que por lo general no se benefician de KeepAlive. Para evitar problemas al establecer la conexión con los servicios externos, se recomienda deshabilitar KeepAlive en los complementos. Si el servicio externo específico se beneficiaría de usar sesiones persistentes por razones de rendimiento, envíe KeepAlive activamente en un intervalo de la mitad de tiempo de espera inactivo (30 segundos) para evitar que se cierre la conexión.

Los ejemplos siguientes muestran cómo definir KeepAlive explícitamente en false según el método que use para establecer conexiones a un servicio externo:

  • HttpWebRequest

    HttpWebRequest req = WebRequest.Create("https://www.contoso.com/api/stuff") as HttpWebRequest;
    
    if (req != null)
    {
        req.KeepAlive = false;
        HttpWebResponse response = (HttpWebResponse)req.GetResponse();
    }
    
  • WebClient

    private string RequestString(Uri location)
    {
        using (var client = new MyWebClient())
        {
            return client.DownloadString(location);
        }
    }
    
    internal class MyWebClient : WebClient
    {
        // Overrides the GetWebRequest method and sets keep alive to false
        protected override WebRequest GetWebRequest(Uri address)
        {
            HttpWebRequest req = (HttpWebRequest)base.GetWebRequest(address);
            req.KeepAlive = false;
    
            return req;
        }
    }
    
  • Instancia de WCF ClientBase< T >

    OrganizationServiceClient client = null;
    
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var transport = new HttpsTransportBindingElement();
        transport.KeepAliveEnabled = false;
    
        var binding = new CustomBinding(transport);
    
        client = new OrganizationServiceClient(binding, address);
    
        WhoAmIResponse response = client.Execute(new WhoAmIRequest()) as WhoAmIResponse;
    }
    catch (Exception ex)
    {
        client.Abort();
    }
    finally
    {
        client.Close();
    }
    
  • Método estático WCF ChannelFactory< TChannel >

    IRequestChannel channel = null;
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var transport = new HttpsTransportBindingElement();
        transport.KeepAliveEnabled = false;
    
        var binding = new CustomBinding(transport);
    
        channel = ChannelFactory<IRequestChannel>.CreateChannel(binding, address);
    
        Message request = Message.CreateMessage(MessageVersion.Soap12, "some action", "message body");
        Message response = channel.Request(request);
    }
    catch (Exception ex)
    {
        channel.Abort();
    }
    finally
    {
        channel.Close();
    }
    
  • Instancia de WCF ChannelFactory< TChannel >

    ChannelFactory<IRequestChannel> factory = null;
    IRequestChannel channel = null;
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var transport = new HttpsTransportBindingElement();
        transport.KeepAliveEnabled = false;
    
        var binding = new CustomBinding(transport);
    
        factory = new ChannelFactory<IRequestChannel>(binding, address);
        channel = factory.CreateChannel();
    
        Message request = Message.CreateMessage(MessageVersion.Soap12, "some action", "message body");
        Message response = channel.Request(request);
    }
    catch (Exception ex)
    {
        channel.Abort();
        factory.Abort();
    }
    finally
    {
        channel.Close();
        factory.Close();
    }
    

Patrones problemáticos

Para reemplazar el valor de KeepAlive predeterminado para las interacciones que no sean de WCF, el método debe centrarse en usar HttpWebRequest para realizar las interacciones con el servidor web, pero primero hay establecer la propiedad KeepAlive en false.

Los ejemplos siguientes muestran cómo el patrón problemático según el método que use para establecer conexiones a un servicio externo:

Advertencia

Estos patrones deben evitarse.

  • HttpWebRequest

    WebRequest request = WebRequest.Create("https://www.contoso.com/api/stuff");
    //KeepAlive not explicitly defined as true.  However, default behavior is KeepAlive = true.
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    response.Close();
    
  • WebClient

    using (var client = new WebClient())
    {
        string url = "https://www.contoso.com/api/stuff";
        //KeepAlive not explicitly defined as true.  However, default behavior is KeepAlive = true.
        string result = client.DownloadString(url);
    }
    
  • Instancia de WCF ClientBase< T >

    OrganizationServiceClient client = null;
    
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var binding = new BasicHttpsBinding();
        //KeepAlive not explicitly defined as true.  However, default behavior is KeepAlive = true.
        client = new OrganizationServiceClient(binding, address);
    
        WhoAmIResponse response = client.Execute(new WhoAmIRequest()) as WhoAmIResponse;
    }
    catch (Exception ex)
    {
        client.Abort();
    }
    finally
    {
        client.Close();
    }
    
  • Método estático WCF ChannelFactory< TChannel >

    IRequestChannel channel = null;
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var binding = new BasicHttpsBinding();
        //KeepAlive not explicitly defined as true.  However, default behavior is KeepAlive = true.
    
        channel = ChannelFactory<IRequestChannel>.CreateChannel(binding, address);
    
        Message request = Message.CreateMessage(MessageVersion.Soap12, "some action", "message body");
        Message response = channel.Request(request);
    }
    catch (Exception ex)
    {
        channel.Abort();
    }
    finally
    {
        channel.Close();
    }
    
  • Instancia de WCF ChannelFactory< TChannel >

    ChannelFactory<IRequestChannel> factory = null;
    IRequestChannel channel = null;
    try
    {
        var address = new EndpointAddress("https://www.contoso.com/Custom.svc");
        var binding = new BasicHttpsBinding();
        //KeepAlive not explicitly defined as true.  However, default behavior is KeepAlive = true.
    
        factory = new ChannelFactory<IRequestChannel>(binding, address);
        channel = factory.CreateChannel();
    
        Message request = Message.CreateMessage(MessageVersion.Soap12, "some action", "message body");
        Message response = channel.Request(request);
    }
    catch (Exception ex)
    {
        channel.Abort();
        factory.Abort();
    }
    finally
    {
        channel.Close();
        factory.Close();
    }
    

Información adicional

Los complementos que interactúan con los servicios externos pueden experimentar tiempos de ejecución anormalmente superiores. El problema se debe a la (propiedad KeepAlive) de las solicitudes HTTP emitidas a servidores web externos. Si la propiedad KeepAlive se establece en true o no se define en absoluto (el comportamiento predeterminado es true), el cliente de espacio aislado en el servidor puede continuar intentando y usando una sesión persistente aunque la sesión haya expirado debido a la configuración del dispositivo de red. La causa del tiempo de ejecución extendido se debe a la red que reintenta la solicitud hasta que la conexión se restablezca finalmente.

Importante

Esto afecta a todo el código que inicia solicitudes HTTP para un servidor web mediante System.Net.WebClient, System.Net.WebRequest o System.Net.HttpWebRequest, así como comunicaciones de cliente de Windows Communications Foundation (WCF) mediante un transporte HTTP que se enlaza directa o indirectamente a través de ChannelFactory<TChannel> o ClientBase<T>.

Este comportamiento es transparente para los usuarios finales y el registro es tradicional ya que la ejecución se debe a retrasos de red. Si el complemento se registró como un evento sincrónico, los usuarios podrían experimentar problemas de rendimiento intermitentes durante aquella operaciones registradas que podrían producir ausencia de respuesta de aplicaciones basadas en modelos. Si el complemento se registra de forma asincrónica, el problema solo se observaría revisando los tiempos de ejecución del complemento y observando que la ejecución tomar más tiempo del normal o esperado.

Vea también

Ejemplo: acceso web desde un complemento de espacio aislado
Equilibrio de cargas (WCF)
Propiedad HttpTransportBindingElement.KeepAliveEnabled
Propiedad HttpWebRequest.KeepAlive

Nota

¿Puede indicarnos sus preferencias de idioma de documentación? Realice una breve encuesta. (tenga en cuenta que esta encuesta está en inglés)

La encuesta durará unos siete minutos. No se recopilan datos personales (declaración de privacidad).