4

Java HTTP Proxy Tunneling プログラムを作成しようとしていますが、通信に使用する最適かつ最速のストリームについて専門家のアドバイスが必要です。

基本的な機能を実装しましたが、すべて正常に動作します。唯一の問題は、通信速度またはパフォーマンスです。私の HTTP プロキシ システムは、リモート サーバー上で実行されるサーバー プログラムと、ローカル マシン上で実行されるクライアント プログラムで構成されています。これまでのところ、プログラムは次のようになります。

Listener.java :

/**
 * Listens and accepts connection requests from the browser
 */
ServerSocket listener = null;
try {
    listener = new ServerSocket(port, 128);
} catch (IOException ex) {
    ex.printStackTrace(System.err);
}

ExecutorService executor = Executors.newCachedThreadPool();

Socket connection;
while (!shutdown) {
    try {
        connection = listener.accept();
        executor.execute(new ProxyTunnel(connection));
    } catch (IOException ex) {
        ex.printStackTrace(System.err);
    }
}

ProxyTunnel.java :

try {
    byte[] buffer = new byte[8192];  // 8-KB buffer
    InputStream browserInput = browser.getInputStream();
    OutputStream browserOutput = browser.getOutputStream();

    // Reading browser request ...
    StringBuilder request = new StringBuilder(2048);
    do {
        int read = browserInput.read(buffer);
        logger.log(read + " bytes read from browser.");
        if (read > 0) {
            request.append(new String(buffer, 0, read));
        }
    } while (browserInput.available() > 0 && read > 0);

    // Connecting to proxy server ...
    Socket server = new Socket(SERVER_IP, SERVER_PORT);
    server.setSoTimeout(5000);  // Setting 5 sec read timeout
    OutputStream serverOutput = server.getOutputStream();
    InputStream serverInput = server.getInputStream();

    // Sending request to server ...
    serverOutput.write(request.toString().getBytes());
    serverOutput.flush();

    // Waiting for server response ...
    StringBuilder response = new StringBuilder(16384);
    do {
        try {
            read = serverInput.read(buffer);
        } catch (SocketTimeoutException ex) {
            break; // Timeout!
        }
        if (read > 0) {
            // Send response to browser.");
            response.append(new String(buffer, 0, read));
            browserOutput.write(buffer, 0, read);
            browserOutput.flush();
        }
    } while (read > 0);

    // Closing connections ...
    server.close();

} catch (IOException ex) {
    ex.printStackTrace(System.err);
} finally {
    try {
        browser.close();
    } catch (IOException ex) {
        ex.printStackTrace(System.err);
    }
}

サーバー プログラムは同様の方法を使用して、HTTP 要求を宛先サーバー (www.stackoverflow.com など) に送信し、応答をクライアント プログラムに転送します。クライアント プログラムは応答をローカル ブラウザーに転送します。

  1. これらの TCP/HTTP 通信のパフォーマンスを改善するにはどうすればよいですか?
  2. やなどのバッファ付きストリームを使用するBufferedInputSreamBufferedOutputStream、通信パフォーマンスが向上しますか?
  3. ソケットとストリームjava.nioの代わりにチャネルとバッファを使用すると、パフォーマンスが向上しますか?java.netjava.io
4

1 に答える 1

13

自分でやらないで

アドバイス 0: はるかにスケーラブルで安定した成熟したプロキシ サーバーがたくさんあります。本当に自分で書く必要がありますか?

リクエストをバッファリングするためにStringBuilder/を使用しないでくださいString

byte[] buffer = new byte[8192];  // 8-KB buffer
//...
browserInput.read(buffer);
//...
request.append(new String(buffer, 0, read));
//...
serverOutput.write(request.toString().getBytes());

これにはいくつかの理由で欠陥があります:

  • HTTP 呼び出しがテキスト (ASCII) のみであると想定している場合、バイナリ データは に変換しStringたり元に戻したりした後に不正な形式になりbyte[]ます。次を参照してください:文字列、バイト [] および圧縮

  • プロトコルがテキストベースであっても、システムのデフォルトのエンコーディングを使用しています。これはあなたが望むものではないに違いない

  • 最後に、最も重要な部分:リクエスト全体をバッファリングしないでください。着信リクエストからデータのチャンクを読み取り、1 回の繰り返しでターゲット サーバーにすぐに転送します。余分なメモリ オーバーヘッドとレイテンシはまったく必要ありません。数バイトを受信した直後にそれらをディスパッチし、忘れてしまいます。

使用しないでくださいExecutors.newCachedThreadPool()

このプールは無限に拡大する可能性があり、ピーク時には数千のスレッドが作成されます。基本的に、接続ごとに 1 つのスレッドを作成します (ただし、プールは空きスレッドを再利用しますが、利用できない場合は新しいスレッドを作成します)。考慮してくださいExecutors.newFixedThreadPool(100)- ほとんどの場合、100 ~ 200 スレッドで十分です。それを超えると、多くの作業を行わなくても、コンテキストの切り替えで CPU をかろうじて消費する可能性が高くなります。遅延を恐れず、スケールアウトしてください。

ノンブロッキングnettyスタックを使用する

これにより、最終的なアドバイスが得られます。ブロッキング ソケットを完全に削除します。それらは便利ですが、接続ごとにスレッドが必要なため、うまくスケーリングできません。スタックを保持するために消費されるメモリが多すぎ、コンテキストの切り替えのために浪費される CPU が多すぎます。netty は素晴らしく、NIO に強力な抽象化を構築します。

サンプルを確認してください。HTTP クライアント/サーバー コードが含まれています。少し学習曲線がありますが、数桁のパフォーマンスの向上が期待できます。

于 2012-08-19T08:39:55.243 に答える