web-dev-qa-db-ja.com

閉じるJava HTTPクライアント

Java.net.http.HttpClientを閉じて、保持しているリソースを即座に解放する方法はありますか?

内部的には、セレクター、接続プール、およびExecutor(デフォルトのものが使用されている場合)を保持します。ただし、Closeable/AutoCloseableは実装されていません。

お気づきのとおり、Java.net.http.HttpClientCloseableまたはAutoCloseableを実装していません。したがって、私は2つのオプションしか考えられませんが、どちらも本当に防弾ではなく、さらには優れていません。

プログラムが保持しているHttpClientへのすべての 強力な参照 および ガベージコレクションの要求 を削除できます。ただし、直接制御できない何かがそれまたはそのコンポーネントの1つを保持しているという本当のリスクがあります。強い参照が残っていると、参照されているオブジェクト、および強い参照を保持しているオブジェクトがガベージコレクションされなくなります。それにもかかわらず、これは間違いなく、代替手段よりも慣用的なオプションです。

別のオプション も見つかりました。

final class HttpClientImpl extends HttpClient implements Trackable {
    ...
    // Called from the SelectorManager thread, just before exiting.
    // Clears the HTTP/1.1 and HTTP/2 cache, ensuring that the connections
    // that may be still lingering there are properly closed (and their
    // possibly still opened SocketChannel released).
    private void stop() {
        // Clears HTTP/1.1 cache and close its connections
        connections.stop();
        // Clears HTTP/2 cache and close its connections.
        client2.stop();
    }
    ...
}

他に選択肢がない限り、これを使うのは快適ではありません。参照はおそらくタイプHttpClientであるため、HttpClientImplにキャストする必要があります。 HttpClientインターフェースではなく、将来のリリースで変更される可能性のある具体的な実装に依存するのは悪いことです。メソッドもプライベートです。 これを回避する方法 がありますが、面倒です。

2
Floegipoky

明らかに、HttpClientは自己管理するように設計されています。したがって、接続プールを維持し、ttlを単独でキャッシュする責任があります。

HttpClientCodeには、次のコードがあります。

if (!owner.isReferenced()) {
                                Log.logTrace("{0}: {1}",
                                        getName(),
                                        "HttpClient no longer referenced. Exiting...");
                                return;
                            }

これは、SelectorManagerループを終了し、すべてのリソースをクリーンアップするための適切な方法です。

 @Override
 public void run() {
            ...

            try {

                while (!Thread.currentThread().isInterrupted()) {

                    ...

                    if (!owner.isReferenced()) {
                        Log.logTrace("{0}: {1}",
                                getName(),
                                "HttpClient no longer referenced. Exiting...");
                        return;
                    }

                    ...
                }
            } catch (Throwable e) {
                ...
            } finally {
                ...
                shutdown();
            }
        }




    final boolean isReferenced() {
            HttpClient facade = facade();
            return facade != null || referenceCount() > 0;
        }

したがって、HttpClientオブジェクトが参照されない場合、すべてのリソースがクリーンアップされます。

UPD:また、タイムアウトを渡してリクエストを調整する必要があります

1

Java 11、各HttpClientは、フライリクエストを処理することになっているselmgrというデーモンスレッドを生成します。このスレッドは、ない場合は閉じられます。コード内のHttpClientへの参照。ただし、私の経験では、信頼性はありません。特に、将来のタイムアウトで非同期メソッドを使用する場合はそうです。

これは、HttpClientを確実に閉じるためにリフレクションを使用して作成したコードです。

static void shutDownHttpClient(HttpClient httpClient)
{
    ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) httpClient.executor().get();
    threadPoolExecutor.shutdown();
    try {
        Field implField = httpClient.getClass().getDeclaredField("impl");
        implField.setAccessible(true);
        Object implObj = implField.get(httpClient);
        Field selmgrField = implObj.getClass().getDeclaredField("selmgr");
        selmgrField.setAccessible(true);
        Object selmgrObj = selmgrField.get(implObj);
        Method shutDownMethod = selmgrObj.getClass().getDeclaredMethod("shutdown");
        shutDownMethod.setAccessible(true);
        shutDownMethod.invoke(selmgrObj);
    }
    catch (Exception e) {
        System.out.println("exception " + e.getMessage());
        e.printStackTrace();
    }

}

ご覧のとおり、これは実装に依存し、将来のJavaバージョンでは機能しない可能性があります。Java 11およびJava 12。

また、Javaコマンドに--add-opens Java.net.http/jdk.internal.net.http=ALL-UNNAMEDを追加する必要があります。

1
Hadi