0

次のようなページがあります。

www.foo1.bar
www.foo2.bar
www.foo3.bar
.
.
www.foo100.bar

ライブラリ jsoup を使用し、 Thread と同時に各ページに接続しています。

Thread matchThread = new Thread(task);
matchThread.start();

各タスクは、次のようにページに接続し、HTML を解析します。

Jsoup.connect("www.fooX.bar").timeout(0).get();

これらの例外を大量に取得します。

java.net.ConnectException: Connection timed out: connect
at java.net.PlainSocketImpl.socketConnect(Native Method)
at java.net.PlainSocketImpl.doConnect(PlainSocketImpl.java:351)
at java.net.PlainSocketImpl.connectToAddress(PlainSocketImpl.java:213)
at java.net.PlainSocketImpl.connect(PlainSocketImpl.java:200)
at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:366)
at java.net.Socket.connect(Socket.java:529)
at sun.net.NetworkClient.doConnect(NetworkClient.java:158)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:388)
at sun.net.www.http.HttpClient.openServer(HttpClient.java:523)
at sun.net.www.http.HttpClient.<init>(HttpClient.java:227)
at sun.net.www.http.HttpClient.New(HttpClient.java:300)
at sun.net.www.http.HttpClient.New(HttpClient.java:317)
at sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(HttpURLConnection.java:970)
at sun.net.www.protocol.http.HttpURLConnection.plainConnect(HttpURLConnection.java:911)
at sun.net.www.protocol.http.HttpURLConnection.connect(HttpURLConnection.java:836)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:404)
at org.jsoup.helper.HttpConnection$Response.execute(HttpConnection.java:391)
at org.jsoup.helper.HttpConnection.execute(HttpConnection.java:157)
at org.jsoup.helper.HttpConnection.get(HttpConnection.java:146)

jsoup は同時に 1 つのスレッドしか許可しませんか? または、私は何を間違っていますか?1つずつ進むには時間がかかるため、ページにすばやく接続する方法についての提案。

編集:

このメソッドを使用している 700 のスレッドすべて。これが問題か何かである可能性があります。このメソッドはこの量のスレッドを処理できますか、それともシングルトンですか?

private static Document connect(String url) {
    Document doc = null;
    try {
        doc = Jsoup.connect(url).timeout(0).get();
    } catch (IOException e) {
        System.out.println(url);
    }
    return doc; 
}

編集:スレッドコード全体

public class MatchWorker implements Callable<Match>{

private Element element;

public MatchWorker(Element element) {
    this.element = element;
}

@Override
public Match call() throws Exception {
    Match match = null;
            Util.connectAndDoStuff();
    return match;
}

}

私のすべての 700 要素:

    Collection<Match> matches = new ArrayList<Match>();
    Collection<Future<Match>> results = new ArrayList<Future<Match>>();

 for (Element element : elements) {
        MatchWorker matchWorker = new MatchWorker(element);
        FutureTask<Match> task = new FutureTask<Match>(matchWorker);
        results.add(task);

        Thread matchThread = new Thread(task);
        matchThread.start();
    }
    for(Future<Match> match : results) {
        try {
            matches.add(match.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
4

2 に答える 2

7

私はこれを試しました:

    ExecutorService executorService = Executors.newFixedThreadPool(5);
    List<Future<Void>> handles = new ArrayList<Future<Void>>();
    Future<Void> handle;
    for (int i=0;i < 12; i++) {
        handle = executorService.submit(new Callable<Void>() {

            public Void call() throws Exception {
                Document d = Jsoup.connect("http://www.google.hr").timeout(0).get();
                System.out.println(d.title());
                return null;
            }
        });
        handles.add(handle);
    }

    for (Future<Void> h : handles) {
        try {
            h.get();
        } 
        catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    executorService.shutdownNow();

ほぼ即座に終了し、正しいタイトルを出力します。おそらくファイアウォールに問題がありますか?(「接続がタイムアウトしました」とは、サーバーにまったく到達できなかったことを意味します)

編集:

私はJSoup 1.7.1を使用しました

編集^ 2:

私の知る限り、これは関係JSoup-Threadに問題がないことを証明するはずです。これは、最終的にスレッドを使用しているためです..

編集^ 3:

また、プロキシの背後にいる場合は、プロキシ設定を設定する方法を次に示します。

編集^ 4:

public static Document connect(String url) {
    Document doc = null;
    try {
        doc = Jsoup.connect(url).timeout(0).get();
    } catch (IOException ex) {
        ex.printStackTrace();
    }
    return doc;
}

call 関数は次のように書き直されました。

public Void call() throws Exception {                    
    System.out.println(App.connect("http://www.google.hr").title());
    return null;
}

同じ結果が得られます。私が考えることができる唯一のことは、いくつかの暗黙的な静的同期ですが、TimeOut 例外があるため、あまり意味がありません:/ pls post thread code

編集:

数時間離れなければなりません。ここで私の3つのクラスすべてが書き直されました

まだ動作しますが、遅くなりますが動作します。パフォーマンスを向上させるために、固定スレッド プールを使用することを強くお勧めします。

しかし、それはネットワークの問題に違いないと思います。幸運を :)

編集:

接続タイムアウトは、宛先サーバーにまったく到達できなかったことを意味します。私の知る限り、これは(サーバーが送信したことがない/クライアントが受信したことがない)TCP SYN + ACKメッセージを意味します。

最初に結論できることは、宛先サーバーがオンラインではないということですが、この問題にはさらに多くの原因が考えられます。1 つは、宛先サーバーが要求で過負荷になっている可能性があります (極端な場合、これは(D)DoS 攻撃です)。

現在、並列アプローチを試みました-すべてのリクエストは独自のスレッドにあります:

1) 700 のスレッドで 700 のリクエストを作成します (正確には 700 ではありませんが、OS が処理できる量です)。

2) n<<700 スレッドのスレッドプールを介して 700 のリクエストを行う

最初に、各リクエストに0〜10秒のランダムなスリープフォームを入れることを試みることができます

Thread.currentThread.sleep(new Random().nextInt(10000)) 

しかし、これまでの結果を考えると、おそらくうまくいかないでしょう。次は、コメントで述べたように、並列を順次アプローチに置き換えることです。すべてのリクエストは、単一のメインスレッドの for ループ内から次々と実行されます。ランダムスリープを入れることもできます。

それはあなたが行くことができる最も穏やかな(最も遅い)方法です.それがうまくいかない場合、私はそれを解決する方法がわかりません:(

編集:

5 スレッドのスレッド プールを使用して、 1141 のサッカーの試合のタイトルを正常にダウンロードしました。

その種のサイトがデータを保護することは論理的であるため、開発およびテスト (繰り返し呼び出すことができる限り多くのスレッドで実行) している間、システムはあなた (r IP) をすべてのデータを必要とするクローラーとして識別します。彼らは明らかにそれを好まず、望んでいないので、あなたを禁止しました。彼らはあなたの要求を拒否することさえせず、死んだふりをすることを決定しただけです-したがって、接続タイムアウト. それは今では理にかなっています。ふぅ:)

これが正しければ、プロキシ経由でデータを取得できるはずですが、礼儀正しく、10 個未満のスレッドのスレッド プールを使用してください :)

于 2012-10-06T12:58:33.623 に答える
0

Jsoup は、HTTPUrlConnection を呼び出して接続するだけです。その接続が失敗するかどうかには影響しません。それに影響を与える可能性があるのは、ファイアウォールと DDOS 防止デバイスです。Web サイトが大量の同時接続を拒否するのは当然のことです。

于 2012-10-06T13:57:01.973 に答える