在Java中具有有限连接的多个HTTPS请求

时间:2015-09-22 05:45:26

标签: java multithreading url connection httpurlconnection

我正在开展一项任务,我需要从我的java程序向HTTPS网址发出多个请求并阅读响应。这个过程将在不同的请求下重复多次。

如果我只使用1个线程,则每个请求的延迟(请求和响应之间的时间差异)大约为300毫秒 - 顺序发出请求。吞吐量大约是每秒3.3个请求。

然而,由于目标是获得高吞吐量数字,我决定使用多个线程,每个线程在给定的时间点发出请求。

一些重要细节:

  1. 我只使用那些数量的URL实例作为线程数。我们的想法是每个线程使用一个URL实例,并在每次发出请求时调用新的URL(url).openConnection()。

  2. 我每次读取响应后都使用inputStream.close()关闭输入流,这次关闭将使套接字可重用。

  3. 我没有调用httpConnectionURL.disconnect(),因为这会关闭底层套接字。

  4. 我已使用System.setProperty(“http.maxConnections”,threadCount)将http.maxConnections设置为线程数;

  5. 我还使用“netstat -a | grep | wc -l”检查在任何给定时间点打开的连接数,这总是给出一个等于或高于threadcount的数字。

  6. 即使完成所有这些操作,我也无法获得预期的吞吐量。 对于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();
         }
    

2 个答案:

答案 0 :(得分:3)

线程太多

  • 代码在1,000,000处创建太多并发线程(代码循环1M次创建新线程并每次启动)。

  • 合并网址定义并每次打开新连接

  • 您可以从各种来源(Apache,Grizzly,Netty等)获得的自定义REST客户端获得比内置JDK URL / Connnection类更好的性能。

I / O界限

应用程序是I / O绑定的,而不是CPU绑定的。

这个应用程序应该有比核心更多的线程,但不是100万! (我很惊讶它没有内存耗尽)。

你应该使用更多线程的原因

  1. 当I / O等待来自远程系统的数据时,I / O会阻塞当前线程

  2. 在此期间,可以更好地利用CPU做其他工作

  3. 因此,拥有比CPU内核更多的线程来阻止I / O会导致更好的CPU使用率和更好的I / O.

  4. 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();

查看Executor Service usage