开发者

Must I explicitly close the ResponseStream of an async http request?

开发者 https://www.devze.com 2023-03-29 17:09 出处:网络
I have an Azure worker role running that, among other functions, makes some HTTP requests once every 80 seconds or so. This happens continuously. As we scale up, it might be making a lot more HTTP req

I have an Azure worker role running that, among other functions, makes some HTTP requests once every 80 seconds or so. This happens continuously. As we scale up, it might be making a lot more HTTP requests, so I wrote the code to use BeginGetResponse, EndGetResponse and a callback. The problem is... we have a memory leak somewhere. As this process runs, it slowly but surely loses memory until it runs out completely. Sometimes the GC will kick in and free up some unused objects, but it continues its slow downward trend..

When our callback executes and we finish the request with EndGetResponse(), we don't touch the response stream. All we need to know is the HTTP status code, which we save for our own records. We never call GetResponseStream() and subsequ开发者_开发百科ently Close() it. We do Close() the HttpWebResponse.

My question is: do we need to do something with the Response Stream and then Close() it? Would not doing so cause a memory leak? All of the MS examples/other SO discussions that I've seen do something with the stream. I'm wondering if we should add GetResponseStream().Close()...

Here's the code:

// the request state class, passed along with async request
public class RequestState
{             
    public HttpWebRequest Request { get; set; }
    public HttpWebResponse Response { get; set; }

    // some other properties to track which request this is..
}

...... in some other class .....

// code to perform the request
public void DoHttpRequest() 
{
    HttpWebRequest req = (HttpWebRequest)WebRequest.Create("http://myurl.....");
    RequestState state = new RequestState(req); // this just sets the Request property on RequestState
    req.BeginGetResponse(OnRequestComplete, state);
}

// the callback, request has finished
public void OnRequestComplete(IAsyncResult result)
{
    RequestState state = (RequestState)result.AsyncState;
    HttpWebRequest req = state.Request;
    state.Response = (HttpWebResponse)req.EndGetResponse(result);

    // we do not care about the body of the response
    // all we want is the status code, which we store somewhere else..

    if (state.Response.StatusCode == HttpStatusCode.OK || state.Response.StatusCode == HttpStatusCode.Created)
    {
         // comm was successful
         // save this result code somewhere...
    }
    else if (state.Response.StatusCode == HttpStatusCode.RequestTimeout || state.Response.StatusCode == HttpStatusCode.GatewayTimeout)
    {
          // comm timed out
          // save this result code somewhere..
    }
    else
    {
          // something else, comm failed
          // save this result code somewhere..
    }

    // we've got the relevant data from the HttpWebResponse object, dispose of it
    state.Response.Close();
}

Thank you!


I checked in Reflector (.NET 4.0 latest, as used by Azure applications): HttpWebResponse.Close does close the stream that would be returned by GetResponseStream.

Sounds like there's some problem elsewhere.

From a brief look, closing the stream should also call Abort on the original HttpWebRequest, but the logic is rather convoluted. You may want to try calling Abort explicitly and see if the memory usage clears up.


you could use an using statement to consume the response, ensuring it was disposed! if you are not planning to read the stream contents again then i dont see any harm in closing! on a second thought: in your callback, are you sure that there are no exceptions while trying to record the result some where?


I've encountered a very similar issue to you. I had some worker roles that were periodically going into a state where Azure could no longer communicate with it and they weren't restarting as I expected they would when an error occurs on an Azure instance.

After a friendly chat with the people at MS support it turned out that our instances were running out of memory. When this happens the app fabric controller service (which does all of the talking between your VM and all of the Azure management stuff outside of your VM) crashes meaning you can't do anything with the instance in the management portal.

It turned out that we had an object that we were using quite frequently and we weren't calling dispose on it when we were done.

So in answer to your question, as a general principle if an object has a dispose method, when you're finished working with it you should call dispose.


To pinpoint whats leaking use the latest profiling tools for Windows Azure see msdn - Profiling a Windows Azure Application and use the Memory profiling option.

You can get the latest tools from WebPI here

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号