Java - 用于TCP / HTTP通信的最快流?

时间:2012-08-19 08:20:49

标签: java performance http proxy network-programming

我正在尝试编写Java HTTP代理隧道程序,我需要有关用于通信的最佳和最快流的专家建议。

我已经实现了基本功能,一切正常。唯一的问题是通信速度或性能。我的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.net套接字和java.io流,我会获得任何性能提升吗?

1 个答案:

答案 0 :(得分:10)

不要自己动手

建议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[]后二进制数据将格式不正确,请参阅:String, byte[] and compression

  • 即使协议是基于文本的,您也使用系统的默认编码。我打赌这不是你想要的

  • 最后,最重要的部分:不缓冲整个请求。从传入请求中读取数据块,并在一次迭代中立即将其转发到目标服务器。绝对不需要额外的内存开销和延迟。收到几个字节后立即发送并忘记它们。

不要使用Executors.newCachedThreadPool()

此池可以无限增长,在峰值期间创建数千个线程。基本上,每个连接创建一个线程(除了池重用免费线程,但如果没有可用则创建新线程)。考虑Executors.newFixedThreadPool(100) - 在大多数情况下,100-200个线程就足够了。除此之外,您很可能在上下文切换中几乎不会耗费CPU,而无需做太多工作。不要害怕延迟,向外扩展。

使用非阻塞netty堆栈

这给我们带来了最后的建议。完全删除阻塞套接字。它们很方便,但由于每个连接的线程要求而无法很好地扩展。用于保持堆栈的内存太多,过多的CPU浪费在上下文切换上。 netty很棒,它构建了强大的NIO抽象。

签出the examples,它们包括HTTP客户端/服务器代码。有一点学习曲线,但你可以期待几个量级的性能增长。