web-dev-qa-db-fra.com

HttpWebRequest.GetResponse () n'arrête pas de perdre du temps

j'ai écrit une simple fonction C # pour récupérer l'historique des transactions de MtGox avec l'appel suivant:

https://data.mtgox.com/api/1/BTCUSD/trades?since=<trade_id>

documenté ici: https://en.bitcoin.it/wiki/MtGox/API/HTTP/v1#Multi_currency_trades

voici la fonction:

string GetTradesOnline(Int64 tid)
{
    Thread.Sleep(30000);

    // communicate
    string url = "https://data.mtgox.com/api/1/BTCUSD/trades?since=" + tid.ToString();
    HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
    StreamReader reader = new StreamReader(response.GetResponseStream());

    string json = reader.ReadToEnd();
    reader.Close();
    reader.Dispose();
    response.Close();

    return json;
}

je commence à tid = 0 (identifiant de commerce) pour obtenir les données (depuis le début). pour chaque demande, je reçois une réponse contenant 1000 informations sur les échanges. J'envoie toujours le Trade ID de la réponse précédente pour la prochaine demande. cela fonctionne bien pour exactement 4 demandes et réponses. mais après cela, la ligne suivante génère une "System.Net.WebException", indiquant que "l'opération a expiré":

HttpWebResponse response = (HttpWebResponse)request.GetResponse();

voici les faits:

  • attraper l'exception et retying continue à causer la même exception
  • les valeurs par défaut HttpWebRequest .Timeout et .ReadWriteTimeout sont déjà suffisamment élevées (plus d'une minute)
  • changer HttpWebRequest.KeepAlive en false n'a rien résolu non plus
  • il semble toujours fonctionner dans le navigateur même lorsque la fonction échoue
  • il n'a aucun problème à récupérer la réponse de https://www.google.com
  • le nombre de réponses réussies avant les exceptions varie d'un jour à l'autre (mais le navigateur fonctionne toujours)
  • à partir de l'ID de commerce qui a échoué la dernière fois provoque l'exception immédiatement
  • l'appel de cette fonction à partir du thread principal a tout de même causé l'exception
  • l'exécuter sur une autre machine ne fonctionnait pas
  • l'exécuter à partir d'une adresse IP différente n'a pas fonctionné
  • augmenter Thread.Sleep entre les demandes ne aide pas

des idées de ce qui pourrait être faux?

13
symbiont

Il existe deux types de délais d'attente. Délai d'expiration du client et du serveur. Avez-vous essayé de faire quelque chose comme ça:

request.Timeout = Timeout.Infinite;
request.KeepAlive = true;

Essayez quelque chose comme ça ...

18
kevin c

J'avais le même problème… .. Pour moi, le correctif était aussi simple que d'envelopper le code HttpWebResponse en utilisant block.

using (HttpWebResponse response = (HttpWebResponse) request.GetResponse())
{
    // Do your processings here....
}

Détails : Ce problème se produit généralement lorsque plusieurs demandes sont adressées au même hôte et que WebResponse n'est pas éliminé correctement. C’est là que le bloc using éliminera correctement l’objet WebResponse et résoudra ainsi le problème.

21
Habeeb

J'ai eu des problèmes similaires en appelant un service REST sur un serveur LINUX via SSL. Après avoir essayé de nombreux scénarios de configuration, j'ai découvert que je devais envoyer un UserAgent dans la tête http.

Voici ma dernière méthode pour appeler l'API REST.

        private static string RunWebRequest(string url, string json)
    {
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);

        // Header
        request.ContentType = "application/json";
        request.Method = "POST";
        request.AllowAutoRedirect = false;
        request.KeepAlive = false;
        request.Timeout = 30000;
        request.ReadWriteTimeout = 30000;
        request.UserAgent = "test.net";
        request.Accept = "application/json";
        request.ProtocolVersion = HttpVersion.Version11;
        request.Headers.Add("Accept-Language","de_DE");
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls;
        ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
        byte[] bytes = Encoding.UTF8.GetBytes(json);
        request.ContentLength = bytes.Length;
        using (var writer = request.GetRequestStream())
        {
            writer.Write(bytes, 0, bytes.Length);
            writer.Flush();
            writer.Close();
        }

        var httpResponse = (HttpWebResponse)request.GetResponse();
        using (var streamReader = new StreamReader(httpResponse.GetResponseStream()))
        {
            var jsonReturn = streamReader.ReadToEnd();
            return jsonReturn;
        }
    }
8
Karl

Pour ce que ça vaut, je rencontrais les mêmes problèmes avec les délais d'attente chaque fois que je l'utilisais, même si des appels étaient acheminés vers le serveur que j'appelais. Le problème dans mon cas était que j'avais Expect réglé sur application/json, alors que ce n'était pas ce que le serveur retournait.

1
bech

Ce n'est pas une solution, mais simplement une alternative: Ces jours-ci, j'utilise presque uniquement WebClient au lieu de HttpWebRequest. En particulier WebClient.UploadString pour POST et PUT et WebClient.DownloadString. Ceux-ci prennent simplement et retournent des chaînes. De cette façon, je n'ai pas à traiter avec les objets de flux, sauf lorsque je reçois une exception WebException. Je peux également définir le type de contenu avec WebClient.Headers ["Content-type"] si nécessaire. La déclaration using facilite également la vie en appelant Dispose pour moi.

Rarement pour la performance, j'attribue une valeur élevée à System.Net.ServicePointManager.DefaultConnectionLimit et j'utilise plutôt HttpClient avec ses méthodes Async pour des appels simultanés.

Voilà comment je le ferais maintenant

string GetTradesOnline(Int64 tid)
{
    using (var wc = new WebClient())
    {
        return wc.DownloadString("https://data.mtgox.com/api/1/BTCUSD/trades?since=" + tid.ToString());
    }
}

2 autres exemples POST

// POST
string SubmitData(string data)
{
    string response;
    using (var wc = new WebClient())
    {
        wc.Headers["Content-type"] = "text/plain";
        response = wc.UploadString("https://data.mtgox.com/api/1/BTCUSD/trades", "POST", data);
    }

    return response;
}

// POST: easily url encode multiple parameters
string SubmitForm(string project, string subject, string sender, string message)
{
    // url encoded query
    NameValueCollection query = HttpUtility.ParseQueryString(string.Empty);
    query.Add("project", project);
    query.Add("subject", subject);

    // url encoded data
    NameValueCollection data = HttpUtility.ParseQueryString(string.Empty);
    data.Add("sender", sender);
    data.Add("message", message);

    string response;
    using (var wc = new WebClient())
    {
        wc.Headers[HttpRequestHeader.ContentType] = "application/x-www-form-urlencoded";
        response = wc.UploadString( "https://data.mtgox.com/api/1/BTCUSD/trades?"+query.ToString()
                                  , WebRequestMethods.Http.Post
                                  , data.ToString()
                                  );
    }

    return response;
}

La gestion des erreurs

try
{
    Console.WriteLine(GetTradesOnline(0));

    string data = File.ReadAllText(@"C:\mydata.txt");
    Console.WriteLine(SubmitData(data));

    Console.WriteLine(SubmitForm("The Big Project", "Progress", "John Smith", "almost done"));
}
catch (WebException ex)
{
    string msg;
    if (ex.Response != null)
    {
        // read response HTTP body
        using (var sr = new StreamReader(ex.Response.GetResponseStream())) msg = sr.ReadToEnd();
    }
    else
    {
        msg = ex.Message;
    }

    Log(msg);
    throw; // re-throw without changing the stack trace
}
0
symbiont