我正在开展一项任务,我需要从我的java程序向HTTPS网址发出多个请求并阅读响应。这个过程将在不同的请求下重复多次。
如果我只使用1个线程,则每个请求的延迟(请求和响应之间的时间差异)大约为300毫秒 - 顺序发出请求。吞吐量大约是每秒3.3个请求。
然而,由于目标是获得高吞吐量数字,我决定使用多个线程,每个线程在给定的时间点发出请求。
一些重要细节:
我只使用那些数量的URL实例作为线程数。我们的想法是每个线程使用一个URL实例,并在每次发出请求时调用新的URL(url).openConnection()。
我每次读取响应后都使用inputStream.close()关闭输入流,这次关闭将使套接字可重用。
我没有调用httpConnectionURL.disconnect(),因为这会关闭底层套接字。
我已使用System.setProperty(“http.maxConnections”,threadCount)将http.maxConnections设置为线程数;
我还使用“netstat -a | grep | wc -l”检查在任何给定时间点打开的连接数,这总是给出一个等于或高于threadcount的数字。
即使完成所有这些操作,我也无法获得预期的吞吐量。 对于1个线程,当我获得3.3的吞吐量时,我假设使用100个线程,我应该获得至少每秒300个的吞吐量。
任何人都可以向我解释我哪里出错了。或任何其他更好的解决方案 以下是我的代码段。
Main Class:
public static void main(String[] args)
{
URL[] urlConnArray = new URL[threadCount];
for(int j = 0;j < urlConnArray.length;j++)
urlConnArray[j] = new URL(regURL);
System.setProperty("http.keepalive", "true");
System.setProperty("http.maxConnections", String.valueOf(threadCount));
for(int i=0;i<1000000;i++)
{
Thread regThread = new Thread(new RegisterThread(urlConnArray[i]));
regThread.start();
}
}
RegisterThread Class:
public class RegisterThread implements Runnable
{
httpConn = (HttpURLConnection) urlConnArray[i].openConnection();
httpConn.setUseCaches(false);
httpConn.setDoOutput(true);
httpConn.setRequestMethod("POST");
httpConn.setRequestProperty("Content-Type", "application/json" );
//Prepare the request body.....
long requestTime = System.currentTimeMillis();
InputStream is = httpConn.getInputStream();
long responseTime = System.currentTimeMillis();
long latency = responseTime - requestTime;
reader = new BufferedReader(new InputStreamReader(is));
StringBuffer response = new StringBuffer();
String line = "";
while ((line = reader.readLine()) != null)
{
response.append(line);
}
is.close();
}
答案 0 :(得分:3)
线程太多
代码在1,000,000处创建太多并发线程(代码循环1M次创建新线程并每次启动)。
合并网址定义并每次打开新连接
您可以从各种来源(Apache,Grizzly,Netty等)获得的自定义REST客户端获得比内置JDK URL / Connnection类更好的性能。
I / O界限
应用程序是I / O绑定的,而不是CPU绑定的。
这个应用程序应该有比核心更多的线程,但不是100万! (我很惊讶它没有内存耗尽)。
你应该使用更多线程的原因
当I / O等待来自远程系统的数据时,I / O会阻塞当前线程
在此期间,可以更好地利用CPU做其他工作
因此,拥有比CPU内核更多的线程来阻止I / O会导致更好的CPU使用率和更好的I / O.
Paul Tyma's overview on synchronous I/O & Non-blocking I/O(2008)是一本非常有用的读物。
Java 8 Streaming
虽然Java 8中的JDK为CPU绑定任务提供了流式处理基础结构(这里不合适),但我们有一个专门为您的目的而设计的库simple-react - 在您阻塞IO的情况下提高系统吞吐量。通过简单的反应,您可以创建像这样的流
LazyReact streamBuilder = new LazyReact(threadCount); //create a Stream builder with x threads
streamBuilder.range(0,1000000)
.map(i-> new RegisterThread(urlConnArray[i])))
.forEach(url-> url.run());
答案 1 :(得分:0)
不幸的是,你的假设是错误的。
当我获得3.3的吞吐量时,对于1个线程。我假设使用100个线程,我应该获得每秒至少300的吞吐量。
多线程的性能取决于CPU的核心数量。如果您在一个核心CPU上运行应用程序,您可能不会注意到多线程的任何增益。由于单核CPU导致多个线程之间的上下文切换,即使事情也会变得更糟。在这种情况下,对于相同的用例,您的结果不如单线程处理。
如果你有100个核心CPU,你可以获得类似于每个线程300-500ms的结果,因为线程没有在对象/方法上使用很多共享锁。
如果您想微调性能,我建议您进行以下更改(一般而非针对您的问题)
1)使用像ExecutorService等java高级线程功能,并将线程池计数声明为CPU的核心数。
2)尽量避免线程之间的共享锁。
在java support for parallel processing
查看多核CPU中对多线程的java支持执行者服务代码示例。
// Here replace 10 with number > number of CPU cores for better performance
ExecutorService executorService = Executors.newFixedThreadPool(10);
executorService.execute(new Runnable() {
public void run() {
// Here add your business logic
System.out.println("Asynchronous task");
}
});
executorService.shutdown();