3

我有一个可以自定义线程号的多线程程序。该程序负责生成 HTTP 请求,将请求发送到 Web 服务,接收响应并解析响应以获取一些结果值。

由于每个请求需要将近 1 秒才能得到响应,为了让程序得到尽可能多的响应,因此启动了多个线程。

以下代码用于启动多线程:

    ...
    for (int i = 0; i < threadNum; i++) {
        threadArray[i] = new Thread(requestGeneratorArray[i]);
        threadArray[i].start();
    }

    for (int i = 0; i < threadNum; i++) {
        try {
            threadArray[i].join();
        } catch (InterruptedException e) {
            logger.error(e);
        }
    }

...

当线程数为50,总共产生并发送10K个请求时,程序运行良好。当线程数仍为 50,总请求数为 100K 时。当发送 95K+ 请求时,程序挂起。也不例外,程序就挂在那里。

然后我添加了一些这样的 JVM 参数: java -Xmx1024M -Xms512M -XX:MaxPermSize=256m ... 使用这些参数,50 个线程/100K 请求有效。但是,50 个线程/1M 请求再次挂起。我将线程号设置为 20 并将请求号设置为 1M,它再次工作。

我想将线程数设置为 50,因为在使用更少的请求数(10K)进行测试时,50 个线程的效率最高。请求数可能比 10M、100M、事件 1B 大得多。在这种情况下,增加 -Xmx -Xms 或 MaxPermSize 的大小不是一个好主意。我应该怎么办?程序挂起的根本原因是什么?

==================================================== =========

我直接使用 Executor Service 而不是线程。但问题也出现了。我重新检查了代码,发现有一个错误:我为每个请求实例化了一个 HttpClient 对象。我将代码更改为为每个线程实例化一个 HttpClient 实例,并且程序不再挂起。

我认为程序挂起的根本原因是它与 Web 服务建立了太多的 HTTP 连接,并用完了 Web 服务的所有线程。这使得 Web 服务无法响应任何新到达的请求。这是对的吗?

4

3 回答 3

1

仅从这些信息很难判断,但从堆设置影响结果这一事实来看,我敢打赌,内容生成和内容解析(存储)之间的调度会很差。

此类应用程序中的一个常见情况是生成内容的线程比获取该内容并将其存储起来的线程生成速度更快。这将逐渐增加用于在内存中保存内容的堆内存量,并且在某些时候吞吐量将开始直线下降。

首先要做的是通过附加一个像 VisualVM 这样的堆查看器来确认这个假设。如果您的堆使用量逐渐增加并开始固定在高水平并且您的吞吐量下降,这可能是罪魁祸首(您还可以确认内存中的内容确实是生成的内容)。

通常,瓶颈是用于存储内容的持久层的 IO。根据您的代码在做什么,您可能在解析代码(或其他地方)中遇到 CPU 瓶颈,但这通常很少见。

这种情况最常见的补救措施是使用有界队列让生成进程等待解析(存储)进程赶上来。看看这个 SO 答案:How to make ThreadPoolExecutor's submit() method block if it is饱和?. 您将不得不了解线程池,但它确实是对原始线程的巨大改进,并且它是处理此类问题的最干净的方法。

于 2013-01-01T08:46:54.963 回答
1

由于我们不知道线程挂在哪里:您是否考虑过某种线程监控,使用分析器或使用 ThreadMXBeans 获取线程的定期堆栈跟踪?正如其他一些海报所提到的,如果有任何可扩展性问题,您也应该关注 gc.log。并观察你的记忆足迹。但这可能不是这里的问题,因为即使是完整的 gc 也应该最终完成,而您的程序却没有。

于 2013-01-03T13:56:22.830 回答
1

+1 对使用Executor Service的所有建议。

您还提到您正在使用 Http 客户端。很少有配置参数可以使 http 客户端更快地并发使用

关于程序挂起。它可能是大型垃圾收集器运行的死锁。我相信像jconsoleVisual VM这样的工具可以帮助你调试这两个场景。

于 2013-01-01T10:18:39.167 回答