0

我有一个 Java 客户端,它通过 HTTP 访问我们的服务器端,发出几个小请求来加载每个新的数据页面。我们维护一个线程池来处理所有非 UI 处理,因此任何后台客户端任务和任何想要与服务器建立连接的任务。我一直在研究一些性能问题,但我不确定我们的线程池设置是否尽可能好。目前我们使用核心池大小为 8 的 ThreadPoolExecutor,我们使用 LinkedBlockingQueue 作为工作队列,因此忽略最大池大小。毫无疑问,在所有情况下都没有简单的做这件事的答案,但是有没有最佳实践。我此刻的想法是

1) 我将切换到使用 SynchronousQueue 而不是 LinkedBlockingQueue 以便池可以增长到最大池大小数字。2)我将最大池大小设置为无限制。

基本上,我目前担心的是,由于线程池大小的上限,服务器端偶尔出现的性能问题会导致无关的客户端处理停止。我对无界的恐惧是在客户端上管理这些线程的额外打击,可能只是两个弊端中更好的一个。

有什么建议、最佳实践或有用的参考吗?干杯,罗宾

4

4 回答 4

1

一般来说,网络延迟很容易比客户端内存分配或线程管理可能发生的任何事情高几个数量级。因此,作为一般规则,如果您遇到性能瓶颈,请首先查看网络链接。

如果问题是您的服务器根本无法跟上来自客户端的请求,那么增加客户端的线程将无济于事:您只需从等待获得响应的 8 个线程升级到更多线程等待(由于它管理的连接数量更多,您甚至可能通过增加其负载来加剧服务器端问题)。

JDK 中的两个并发队列都是高性能的;选择实际上归结为使用语义。如果你有非阻塞管道,那么使用非阻塞队列更自然。如果你不这样做,那么使用阻塞队列更有意义。(您始终可以指定 Integer.MAX_VALUE 作为限制)。如果 FIFO 处理不是必需的,请确保您没有指定公平排序,因为这会严重影响性能。

于 2009-12-16T12:45:54.913 回答
1

听起来您可能会更好地限制队列大小:当有许多请求排队时,您的应用程序是否仍然正常运行(是否可以接受所有任务长时间排队,对其他任务更重要)?如果仍有排队的任务并且用户退出应用程序会发生什么?如果队列变得非常大,服务器是否有机会赶上(很快)以完全隐藏用户的问题?

我会说为更新用户界面需要响应的请求创建一个队列,并保持其队列非常小。如果此队列太大,请通知用户。

对于真正的后台任务,保留一个单独的池,队列更长,但不是无限的。为这个池定义优雅的行为,当它增长或当用户想要退出但还有任务时,应该发生什么?

于 2009-12-16T12:48:03.430 回答
1

正如alphazero所说,如果您遇到瓶颈,那么无论您使用哪种方法,您的客户端等待作业的数量都会继续增长。

真正的问题是你想如何处理瓶颈。或者更准确地说,您希望您的用户如何处理瓶颈。

如果您使用无界队列,那么您不会收到有关已发生瓶颈的反馈。在某些应用程序中,这很好:如果用户正在启动异步任务,则无需报告积压(假设最终清除)。但是,如果用户在执行下一个客户端任务之前需要等待响应,那就很糟糕了。

如果您LinkedBlockingQueue.offer()在有界队列上使用,那么您将立即得到一个响应,说明队列已满,并且可以采取诸如禁用某些应用程序功能、弹出对话框等操作。但是,这将需要您做更多的工作,尤其是在可以从多个地方提交请求的情况下。我建议,如果您还没有它,您可以在服务器队列上创建一个 GUI 感知层以提供常见行为。

而且,当然,永远不要LinkedBlockingQueue.put()从事件线程调用(除非您不介意挂起的客户端)。

于 2009-12-16T13:44:45.047 回答
0

为什么不创建一个无界队列,而是在队列达到一定大小时拒绝任务(甚至可能通知用户服务器正忙(取决于应用程序!))?然后,您可以记录此事件并找出服务器端发生了什么以进行备份,此外,除非您连接到多个远程服务器,否则池中的线程可能没有多大意义,尽管这确实取决于您的应用程序及其功能以及与谁交谈。

拥有无界池通常是危险的,因为它通常不会优雅地降级。最好记录问题,发出警报,防止进一步的操作排队,并弄清楚如何扩展服务器端,如果问题存在,以防止再次发生这种情况。

于 2009-12-16T12:07:24.960 回答