Почему apache http-клиент примерно в 2 раза медленнее, чем URL.openConnection в java? - PullRequest
2 голосов
/ 10 февраля 2020

Рассмотрим этот код:

package com.zip;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.util.Date;

import static com.diffplug.common.base.Errors.rethrow;

/**
 * @author nsheremet
 */
public class ParallelDownload2 {
  public static int THREADCOUNT = 20;
  private static final String URL = "https://server.com/myfile.zip";
  public static String OUTPUT = "C:\\!deleteme\\myfile.zip";
  public static void main(String[] args) throws Exception {
    System.setProperty("https.protocols", "TLSv1,TLSv1.1,TLSv1.2");
    System.out.println(new Date());

    CloseableHttpClient httpClient = HttpClients.createDefault();

    HttpGet request = new HttpGet(URL);
    request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36");
    CloseableHttpResponse response = rethrow().wrap(() -> httpClient.execute(request)).get();
    Long contentLength = Long.parseLong(response.getFirstHeader("Content-Length").getValue());
    long blocksize = contentLength / THREADCOUNT;

    RandomAccessFile randomAccessFile = new RandomAccessFile(new File(OUTPUT), "rwd");
    randomAccessFile.setLength(contentLength);
    randomAccessFile.close();
    response.close();

    for (long i = 0; i <THREADCOUNT; i++) {
      long startpos = i * blocksize;
      long endpos = (i + 1) * blocksize - 1;
      if (i == THREADCOUNT - 1) {
        endpos = contentLength;
      }
      new Thread(new DownloadTask(i, startpos, endpos)).start();
    }
    System.out.println(new Date());
  }

  public static class DownloadTask implements Runnable {

    public DownloadTask(
        long id,
        long startpos,
        long endpos
    ) {
      this.id = id;
      this.startpos = startpos;
      this.endpos = endpos;
    }

    long id;
    long startpos;
    long endpos;

    @Override
    public void run() {
      try {
        CloseableHttpClient httpClient = HttpClients.createDefault();

        HttpGet request = new HttpGet(URL);
        request.addHeader("Range", "bytes=" + startpos + "-" + endpos + "");
        request.addHeader("Connection", "keep-alive");
        request.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.63 Safari/537.36");
        CloseableHttpResponse response = rethrow().wrap(() -> httpClient.execute(request)).get();

        if (response.getStatusLine().getStatusCode() == 206) {

          InputStream is = response.getEntity().getContent();
          RandomAccessFile randomAccessFile = new RandomAccessFile(new File(OUTPUT), "rwd");
          randomAccessFile.seek(startpos);
          int len = 0;
          byte[] buffer = new byte[1024*10];
          while ((len = is.read(buffer)) != -1) {
            randomAccessFile.write(buffer, 0, len);
          }
          is.close();
          randomAccessFile.close();
          System.out.println("Thread "+ Thread.currentThread().getId() +": Download");
        }
      } catch (IOException e) {
        e.printStackTrace();
      }
      System.out.println(new Date());
    }

  }

}

Это модифицированная копия этой , которая написана простым URL.openConnection. Почему URL.openConnection с многопоточностью не загружает файл со скоростью 10 Мбит / с c, тогда как скорость версии apach http-клиента в основном составляет 1-5 Мбит / с c? Я что-то пропустил в настройках клиента http apache?

ОБНОВЛЕН

  1. Я использую несколько HttpClients, потому что один объект приводит к той же производительности, что и 1 соединение через URL
  2. Http apache клиент используется в высокопроизводительных серверах, поэтому я считаю, что есть определенная проблема конфигурации. Но что именно?

О коде

Это, конечно, не готовый к работе код, и его следует рассматривать как прототип, где я хочу, чтобы загрузка многопоточности работала быстро.

О многопоточности

Я не могу объяснить, почему, поскольку я не являюсь владельцем загружаемого ресурса, но многопотоковая скорость загрузки намного быстрее (в 10 раз) одного потока.

1 Ответ

0 голосов
/ 19 февраля 2020

Мало возможностей.

(1) Разница может быть не в скорости передачи, а в начальной задержке при подключении. У меня была похожая проблема в прошлом, и виновником оказался IPv6. Первоначальный запрос был выполнен по IPv6, и он возвращался к IPv4 в автоматическом режиме, но только после истечения времени ожидания.

Попробуйте запустить с -Djava.net.preferIPv4Stack=true или укажите хост в качестве числового квадрата IPv4 и посмотрите, имеет ли это значение.

(2) Различие может быть связано с тем, что реализация https может проверять пути сертификатов, списки аннулированных заявок и т. Д. c. Проверьте с http URL, если это имеет значение. Если это так, обратитесь к документации Apache, чтобы настроить поведение https по своему вкусу.

(3) В любом случае, запуск tcpdump или wireshark, скорее всего, даст вам более полезную информацию.

...