【问题标题】:How to MultiThread HttpURLConnection in Java如何在 Java 中实现多线程 HttpURLConnection
【发布时间】:2020-11-25 03:34:57
【问题描述】:

大家好,我有一个客户想检查他们网站的变化。他们有 500 万个 URL 需要检查。如果我要同步发送请求/ping,我需要 23 天。所以我正在寻找一个多线程的解决方案。我最初是在 Python 中解决这个问题的,但没有看到太大的改进/无法很好地扩展,所以我在 Java 中,如果这也失败了,我会在认输之前在 Go 中尝试。 em>

问题是我没有看到多线程有任何改进。也许我执行错了,有人可以帮助我吗?

编辑:

我只是在这里进行编辑,新来的人可以查看这篇文章的历史,看看我是如何解决这个问题的。

这是套接字建议,当我尝试在线程中运行它时失败,不确定我在这里也做错了什么。

主类:

package com.company;
import java.io.IOException;
import java.util.ArrayList;
import java.util.concurrent.TimeUnit;

public class Main extends Thread{
public static void main(String[] args) throws IOException {
long startTime = System.nanoTime();
Helpers.get("www.google.com", 80); // works here
String path = "test.txt";
boolean append = true;
for (int x = 0; x < 1; x++) {
ArrayList<String> urls = new ArrayList<String>();
// when x = 0, y = 0 | 10 /\ when x = 1, y = 10 | 20
for (int y= x * 10;y < ((x + 1) * 10); y++){
urls.add(String.format("www.google%d.com/", y)); // doesn't work here
}
Thread thread = new Thread(new Helpers(path, append, urls, 80));
thread.start();
thread.interrupt();
}
long endTime = System.nanoTime();
long duration = TimeUnit.NANOSECONDS.toMillis(endTime - startTime);
System.out.println(duration + " ms");
}
}

助手类:

package com.company;
import java.io.IOException;
import java.net.*;import java.io.FileWriter;
import java.io.PrintWriter;import java.util.ArrayList;
public class Helpers extends Thread{
public Helpers(String path, boolean append, ArrayList<String> urls, int port) throws IOException {
this.run(path, append, urls, port);
}
public void run(String path, boolean append, ArrayList<String> urls, int port) throws IOException {
for (String url : urls) {
String status = Helpers.get(url, port);Helpers.writeToFile(path, append, status);
System.out.println(status);
}
}
public static String get(String url, int port) throws IOException {
try {
Socket conn = new Socket(url, 80);
conn.close();
return url + " | Success";
}catch (UnknownHostException error){return url + " | Failed";
}
}

【问题讨论】:

  • 您正在泄漏连接。如果您设法得到它,您至少需要关闭HttpURLConnection 的输入流。
  • @MarquisofLorne 对不起,我不太明白,你能举个例子吗?
  • HttpURLConnection的使用示例,更简单的one
  • 使用ForkJoinPool/CompletableFuture 来利用你的cpu 的核心(人们现在往往有更多),然后你需要将你的任务写成Runnable 对象。从那时起,您需要从任务运行的角度考虑设计,而不会相互碰撞;共享资源需要是原子的或锁定的,并且您处理信息的方式可能会有所不同。例如,也许您有 4 个工作线程,其唯一目的是为您的 cpu 提供尽可能多的 http 连接(它们收到请求,添加到他们的队列并提供结果)
  • @Lebecca 感谢您提供的示例,我认为我按照他们的方式实现了它,但我认为它对性能的影响不大。

标签: java multithreading httpurlconnection


【解决方案1】:

您可以尝试不同的方法。您可以尝试创建与 Web 服务器的套接字连接,然后对不同的 URL 进行多次调用 (GET/HEAD),而不是为每个调用创建 HTTPConnection。

/**
 * hostname of the webserver e.g. www.w3.org
 * @param hostname
 * @param urlList
 * @throws IOException
 */
public static void makingHTTPCall( String hostname , List<String> urlList) throws IOException {

    SSLSocketFactory factory = (SSLSocketFactory)SSLSocketFactory.getDefault();
    SSLSocket socket =
            (SSLSocket)factory.createSocket(hostname, 443);


    BufferedReader in
            = new BufferedReader(
            new InputStreamReader(socket.getInputStream()));
    PrintWriter out
            = new PrintWriter(socket.getOutputStream(), true);

    /**
     *  if required create different url List and pass those list to separate thread for better performance
     */

    urlList.forEach(
            url -> {
                System.out.println("Making call to url /" + url);
                out.println("HEAD "  + url + " HTTP/1.1\r\n");
                out.flush();

                String line = "";

                try {
                    while ((line = in.readLine()) != null) {
                        System.out.println("Response" + line);
                        break;
                    }

                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
    );

    try {
            in.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
    out.close();

}

我已尝试使用 SSL 套接字,您可以根据自己的配置进行更改。

【讨论】:

  • 感谢您抽出宝贵时间回复。我试过这个,同步工作,但不适用于线程。
  • 创建连接是一项繁重的操作,尤其是当您面临百万级请求时。答案往往会减少连接创建费用。
  • @Lebecca 好的,这就是我们使用 Socket 而不是 Http 连接类的原因?套接字只是尝试建立连接,然后退出,而 HTTP 连接更适合传输数据和退出?
  • 如果所有 URL 都在同一个网络服务器上:在主函数中创建一次套接字,然后将此套接字传递给 Helper 类,该类将为每个 URL 进行单独的 HTTP 调用。使用某种线程池来进行 HTTP 调用。例如URL:www.ggogle.com/abc.html 为 www.google.com 建立 Socket 连接,“/abc.html”将成为 urlList 的一部分。如果 URL 位于不同的主机上,则使用连接池。
【解决方案2】:

您执行错误。您应该使 Helpers 类扩展线程或实现可运行。将你需要的所有东西传递给这个类,例如,url、文件指针等。

在您的主类中,创建 Helper 对象,然后将其作为线程运行。

【讨论】:

  • 感谢您抽出宝贵时间提供帮助,我已按照您的建议实施,但仍未看到任何改进。是不是我做错了什么?
  • 我不确定您希望看到哪些改进。您需要检查每个线程需要多长时间才能完成。您还可以更精细地查看写入文件需要多长时间。在代码中,所有线程都写入同一个文件,这可能是个问题。您也没有关闭 HTTP URL 连接。您需要在 finally 块中关闭它。
  • 我只是不认为它在同步运行,因为它是从 1-100 打印出来的,而不是像 1 - 13 - 2 - 23 - 4 - 3,你知道吗?我确实尝试关闭 URL 连接,但没有帮助。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-10-31
  • 2018-10-06
  • 1970-01-01
  • 1970-01-01
  • 2010-12-09
  • 2023-03-03
相关资源
最近更新 更多