2011-08-22 32 views
2

我有一个Azure工作者角色运行,除其他功能之外,每80秒左右发出一次HTTP请求。这种情况持续发生随着我们扩大规模,它可能会产生更多的HTTP请求,所以我编写了使用BeginGetResponse,EndGetResponse和回调的代码。问题是......我们有一处内存泄漏。当这个过程运行时,它会缓慢但确定地失去记忆,直到完全耗尽为止。有时候GC会启动并释放一些未使用的对象,但它会继续缓慢下降趋势。我必须显式关闭异步HTTP请求的ResponseStream吗?

当我们的回调执行并且我们用EndGetResponse(),完成请求时,我们不会触及响应流。所有我们需要知道的是HTTP状态码,我们将其保存为我们自己的记录。我们永远不会调用GetResponseStream(),然后再调用Close()。我们关闭()HttpWebResponse。

我的问题是:我们需要做的事情与响应流,然后关闭()它?不这样做会导致内存泄漏?我所见过的所有MS示例/其他SO讨论都与流有关。我不知道我们是否应该添加的GetResponseStream()关闭()...

下面的代码:

// 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(); 
} 

谢谢!

+0

最新的工具“直到它用完完全” - 你得到一个'OutOfMemoryException'? –

+0

由于它位于Azure云上,因此我无法追踪该点的实际行为。我们的HTTP状态代码存储从80秒延长到更长时间,然后角色“降级”并完全停止运行。每10秒对角色的可用内存进行分析,表明内存趋势向下,直到达到0 ..需要几天时间。 –

回答

1

我在Reflector(.NET 4.0 latest,Azure应用程序使用的最新版本)中进行了检查:HttpWebResponse.Close确实关闭了将由GetResponseStream返回的流。

听起来像其他地方有一些问题。

从简单的介绍一下,关闭流应该也呼吁Abort原始HttpWebRequest,但逻辑是颇为曲折。您可能想要尝试明确地调用Abort并查看内存使用情况是否清除。

+0

感谢您检查反射器;没有想到这一点。 Abort()会像原始HttpWebRequest对象上的Dispose()一样工作吗?我没有看到在任何示例(http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.endgetresponse.aspx)中的调用,但它绝对值得一试 –

+0

它似乎清理一些资源,但它不是一个真正的'Dispose',因为它通常不会被调用。 –

+0

发现处理System.IO.Compression.GZipStream对象的类似问题。调用request.Abort()首先解决它。 –

0

您可以使用using语句来消费响应,确保它被处置!如果你不打算再次读取流内容,那么我没有看到关闭的任何伤害!在第二个想法:在你的回调中,你确定有没有例外,而试图记录结果在哪里?

+0

是的,使用声明绝对是更好的方法。我们留下了一些try-catch块,我们确实成功地保存了HTTP代码,所以它看起来像*没有发生异常。但使用是最安全的方式。我希望有人可以告诉我们是否必须关闭()流,如果是,为什么不关闭()顶级HttpWebResponse照顾它? –

+0

我会在这种情况下做记忆分析!我曾与Azure合作,但不应该很难记住配置文件这个逻辑,以确保你不会错过任何东西,因为考虑到你的应用可以随时扩展! – ioWint

0

我遇到了与您非常相似的问题。我有一些工作人员角色定期进入Azure无法再与之通信的状态,并且他们没有重启,因为我预料他们会在Azure实例发生错误时重启。

经过与MS支持人员的友好交谈,结果发现我们的实例内存不足。发生这种情况时,应用结构控制器服务(会执行虚拟机与您的虚拟机之外的所有Azure管理内容之间的所有通话)崩溃,这意味着您无法对管理门户中的实例执行任何操作。

事实证明,我们有一个我们使用得非常频繁的对象,当我们完成时我们没有调用它。

所以在回答你的问题时,作为一个普通的原则,如果一个对象有一个dispose方法,当你完成它的工作时,你应该调用dispose。

+0

是的,这听起来很熟悉。我们将开始使用'using'语句,因为它确保即使异常中断流,也会调用Dispose()。感谢您的答复。 –