web-dev-qa-db-ja.com

現在開いているネットワークソケットを調べるJava VM

Javaプログラムがすべてのリソース(スレッド、サーバーソケット、クライアントソケット)を解放するというエンドツーエンドのテストを書いています。これはライブラリなので、JVMを終了してリソースを解放するのはオプションではありません。 スレッドの解放のテスト は簡単でした。なぜなら、その中のすべてのスレッドについて ThreadGroup を要求できるからですが、取得するための良い方法はまだ見つかりません。現在のJVMが使用しているすべてのネットワークソケットのリスト。

netstatと同様に、JVMからすべてのクライアントソケットとサーバーソケットのリストを取得する方法はありますか?使用しています Netty with [〜#〜] oio [〜#〜] (ie Java.net.ServerSocket および Java.net.Socket ) Java 7。ソリューションはWindowsとLinuxの両方で機能する必要があります。

私の最初の好みは、純粋なJavaを使用してJVMから質問することです。 MX Beanなどを探しましたが、見つかりませんでした。

もう1つのオプションは、JVMのプロファイリング/デバッグAPIに接続して、SocketとServerSocketのすべてのインスタンスを要求することですが、その方法と、ネイティブコードなしで実行できるかどうかはわかりません(AFAIK、 [ 〜#〜] jvmti [〜#〜] はネイティブのみです)。また、テストが遅くなることはありません(私の最も遅いエンドツーエンドのテストでさえ、別のJVMプロセスの開始を含めてわずか0.5秒です)。

JVMへの問い合わせが機能しない場合、3番目のオプションは、作成されたすべてのソケットを追跡する設計を作成することです。これには、ソケットが作成される場所を見逃す可能性があるという欠点があります。 Nettyを使用しているので、 ChannelFactory をラップし、 ChannelGroup を使用することで実装できるようです。

22
Esko Luontola

_Java.net.Socket_と_Java.net.ServerSocket_にフックして、それらのクラスのすべてのall新しいインスタンスをスパイすることができました。完全なコードを見ることができます ソースリポジトリ内 。アプローチの概要は次のとおりです。

SocketまたはServerSocketがインスタンス化されると、そのコンストラクターで最初に行われるのは、ソケット機能を実際に実装するオブジェクトをインスタンス化するsetImpl()の呼び出しです。デフォルトの実装は_Java.net.SocksSocketImpl_のインスタンスですが、カスタム _Java.net.SocketImplFactory_ から _Java.net.Socket#setSocketImplFactory_ および-を設定することでオーバーライドできます。 _Java.net.ServerSocket#setSocketFactory_

これは、 _Java.net.SocketImpl_ がパッケージプライベートであるすべての実装によって少し複雑になりますが、それほど難しくはありません。

_private static SocketImpl newSocketImpl() {
    try {
        Class<?> defaultSocketImpl = Class.forName("Java.net.SocksSocketImpl");
        Constructor<?> constructor = defaultSocketImpl.getDeclaredConstructor();
        constructor.setAccessible(true);
        return (SocketImpl) constructor.newInstance();
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
_

作成時にすべてのソケットをスパイするためのSocketImplFactory実装は、次のようになります。

_    final List<SocketImpl> allSockets = Collections.synchronizedList(new ArrayList<SocketImpl>());
    ServerSocket.setSocketFactory(new SocketImplFactory() {
        public SocketImpl createSocketImpl() {
            SocketImpl socket = newSocketImpl();
            allSockets.add(socket);
            return socket;
        }
    });
_

SetSocketFactory/setSocketImplFactoryは1回しか呼び出せないため、それを実行するテストを1つだけ行う必要があるか(私が持っているように)、そのスパイを保持するための静的シングルトンを作成する必要があります(うん!)。

次に、問題は、ソケットが閉じているかどうかを確認する方法です。 SocketとServerSocketの両方にメソッドisClosed()がありますが、それは閉じられたかどうかを追跡するためにそれらのクラスの内部のブール値を使用します-SocketImplインスタンスには閉じられたかどうかをチェックする簡単な方法がありません。 (ところで、SocketとServerSocketはどちらもSocketImplによってサポートされています。「ServerSocketImpl」はありません。)

ありがたいことに、SocketImplには、それがサポートしているSocketまたはServerSocketへの参照があります。前述のsetImpl()メソッドはimpl.setSocket(this)またはimpl.setServerSocket(this)を呼び出し、_Java.net.SocketImpl#getSocket_または_Java.net.SocketImpl#getServerSocket_を呼び出すことでその参照を取り戻すことができます。

繰り返しになりますが、これらのメソッドはパッケージプライベートであるため、少し反省する必要があります。

_private static Socket getSocket(SocketImpl impl) {
    try {
        Method getSocket = SocketImpl.class.getDeclaredMethod("getSocket");
        getSocket.setAccessible(true);
        return (Socket) getSocket.invoke(impl);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

private static ServerSocket getServerSocket(SocketImpl impl) {
    try {
        Method getServerSocket = SocketImpl.class.getDeclaredMethod("getServerSocket");
        getServerSocket.setAccessible(true);
        return (ServerSocket) getServerSocket.invoke(impl);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}
_

GetSocket/getServerSocketはSocketImplFactory内で呼び出されない場合があることに注意してください。これは、Socket/ServerSocketがSocketImplがそこから返された後にのみ設定されるためです。

これで、Socket/ServerSocketについて必要なテストをチェックインするために必要なすべてのインフラストラクチャができました。

_    for (SocketImpl impl : allSockets) {
        assertIsClosed(getSocket(impl));
    }
_

完全なソースコードは ここ です。

16
Esko Luontola

自分で試したことはありませんが、JavaSpecialistsニュースレターにも同様の問題があります。

http://www.javaspecialists.eu/archive/Issue169.html

一番下で、彼はAspectJを使用したアプローチについて説明しています。おそらく、ソケットを作成するコンストラクターの周りにポイントカットを配置し、そこにソケットの作成を登録するコードを含めることができます。

5
James Kingsbery