2

我在Scala应用程序中使用Apache Http Client。Apache HttpClient PoolingHttpClientConnectionManager泄漏连接?

该应用程序具有高吞吐量和高并行性。

我不确定,但我想我可能是在泄漏连接。似乎只要使用客户端的代码段变得繁忙,应用程序就会变得无法响应。我的怀疑是我正在泄漏套接字或其他导致应用程序其他方面停止工作的东西。它可能也不会泄漏连接,因为它们不会足够快地关闭连接。

对于更多的上下文,偶尔某些操作会导致此代码每分钟并行执行数百次。发生这种情况时,应用程序的Rest API(Spray)将无响应。应用程序的其他领域也以高并行性运行,并且这些应用程序响应性永远不会造成问题。

减少这部分代码的并行性似乎可以缓解问题,但不是一个可行的长期解决方案。

我忘了配置什么,或者配置不正确?

我使用的代码是这样的:

class SomeClass { 
    val connectionManager = new PoolingHttpClientConnectionManager() 
    connectionManager.setDefaultMaxPerRoute(50) 
    connectionManager.setMaxTotal(500) 
    val httpClient = HttpClients.custom().setConnectionManager(connectionManager).build() 

    def postData() { 
    val post = new HttpPost("http://SomeUrl") // Typically this URL is fixed. It doesn't vary much if at all. 
    post.setEntity(new StringEntity("Some Data")) 
    try { 
     val response = httpClient.execute(post) 
     try { 
     // Check the response 
     } finally { 
     response.close() 
     } 
    } finally { 
     post.releaseConnection() 
    } 
    } 
} 

编辑

我可以看到我建立了大量处于TIME_WAIT状态的连接。我已经尝试将DefaultMaxPerRoute和MaxTotal调整为各种值,但没有明显的效果。这似乎是我错过了一些东西,因此连接没有被重新使用,但我找不到任何文件表明我缺少的东西。这些连接重新使用至关重要。

EDIT 2

随着进一步的调查,使用lsof的-p,我可以看到,如果我设置MaxPerRoute至10,实际上被列为 “ESTABLISHED” 10个连接。我可以看到端口号不会改变。这似乎暗示我实际上正在重新使用这些连接。

什么不解释是为什么我仍然泄漏在此代码中的连接?在TIME_WAIT状态下显示的重用连接和泄漏连接(与netstat -a一起发现)共享相同的基本URL。所以他们绝对是相关的。是否有可能我正在重新使用连接,但不知何故不正确地关闭响应?

EDIT 3

位于所述TIME_WAIT “泄漏” 的来源。这是在一个不相关的代码部分。所以这与HttpClient没有任何关系。然而,在修复该代码后,所有的TIME_WAIT都消失了,但是当多次访问HttpClient代码时,应用程序仍然没有响应。仍在调查那部分。

+0

要分析为什么您的喷雾应用程序变得无响应(如果您认为这可能是一个问题),您可能需要收集一些堆栈痕迹以查看Akka /喷雾线程在哪里花费时间。在控制台上使用'jps'和'jstack'来做到这一点。随意在喷雾邮件列表上发布关于它的信息。 – jrudolph

回答

1

你应该真的考虑重新使用HttpClient实例或至少是支持它的连接池,而不是为每个新的请求执行创建它们。如果您希望继续执行后者,则还应关闭客户端或在连接池超出范围之前关闭连接池。

至于泄漏而言,它应该是比较容易通过运行带有上下文记录连接管理你的应用程序来跟踪横空出世描述here

+0

上面的示例代码并没有完全清楚,但上面的类只创建一次。因此,HttpClient和ConnectionManager将被重用于每个请求。 – Wally

+1

我明白了。每当此类的实例被解除引用或即将超出范围时,您仍然应该关闭连接池。此外,关于连接管理上下文日志的位仍然存在。这应该至少让你知道所有连接是否被正确释放回池中 – oleg

0

IMO - 你可以为每个使用MAXCONNECTION的要少得多域(如5而不是50),并且仍然完全饱和你的网络带宽,如果你有效地使用http的话。

我不是一个斯卡拉人(android,java),但在http客户端线程池上做了很多很多优化。国际海事组织(IMO) - 盲目地将每个域的连接数增加到50个,正在掩盖其他一些与吞吐量有关的严重问题

2点:

,如果你使用的是共享“sharedPoolingClientConnManager”,正确地将每个域一个小型游泳池,您符合释放你的康恩回池中的推荐方式(你应该能够调试所有这些都看到每个线程池实例的连接状态的运行指标),那么你应该很好。

无论scala的并行性特性如何,您应该了解某个域上池中的5个相应线程如何共享套接字?根据android/java的经验,IMO认为即使每个线程执行者都在该httpclient.exec语句的范围内阻止对服务器的I/O,但实际的通道管理允许非常高的吞吐量,而无需借助ASNyC客户端库HTTP。

Android体验可能不相关,因为客户端只有4个线程。话虽如此,即使你有64或更多的线程可用,我只是不明白每个域需要超过10个连接才能让你的底层http套接字非常非常忙碌。