24

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

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

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

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

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

4

2 に答える 2

18

これらのクラスのすべての新しいインスタンスにフックしてスパイすることができましjava.net.Socketjava.net.ServerSocket完全なコードは、ソース リポジトリで確認できます。アプローチの概要は次のとおりです。

Socket または ServerSocket がインスタンス化されると、そのコンストラクターで最初に行われるのはsetImpl()、ソケット機能を実際に実装するオブジェクトをインスタンス化する呼び出しです。デフォルトの実装は のインスタンスですが、 とを介してjava.net.SocksSocketImplカスタムを設定することでオーバーライドできます。java.net.SocketImplFactoryjava.net.Socket#setSocketImplFactoryjava.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 つだけ持つ必要があることに注意してください (私が持っているように)、またはそのスパイを保持するための静的シングルトンを作成する必要があります (yuck!)。

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

ありがたいことに、SocketImpl には、それがサポートしている Socket または ServerSocket への参照があります。前述のsetImpl()メソッドは or を呼び出し、impl.setSocket(this)orimpl.setServerSocket(this)を呼び出すことでその参照を取得できます。java.net.SocketImpl#getSocketjava.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));
    }

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

于 2012-07-27T21:48:18.400 に答える
5

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

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

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

于 2012-07-26T13:20:48.467 に答える