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

Sey*_*mad 4 java performance proxy network-programming http

我正在尝试编写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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

服务器程序使用类似的方式并将HTTP请求发送到目标服务器(例如www.stackoverflow.com)并将响应转发到客户端程序,其中客户端程序将响应转发到本地浏览器.

  1. 如何提高这些TCP/HTTP通信的性能?
  2. 是否使用缓冲流,如BufferedInputSreamBufferedOutputStream提高通信性能?
  3. 如果我使用java.nioChannels和Buffers而不是java.netSockets和java.ioStream ,我会获得任何性能改进吗?

Tom*_*icz 11

不要自己动手

建议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());
Run Code Online (Sandbox Code Playgroud)

这有缺陷有几个原因:

  • 你假设你的HTTP调用只是文本(ASCII),二进制数据转换为String和返回后会出错byte[],请参阅:String,byte []和压缩

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

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

不要用 Executors.newCachedThreadPool()

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

使用非阻塞网络堆栈

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

查看示例,它们包括HTTP客户端/服务器代码.有一点学习曲线,但你可以期待几个量级的性能增长.

  • @SeyedMohammad:对.对于#2:通常,是的,只要你没有缓冲已经缓冲的流.但是在你的情况下你已经以块的形式读取数据,缓冲只会略微增加内存消耗并可能使代码变慢.BTW考虑[`IOUtils`](http://commons.apache.org/io/apidocs/org/apache/commons/io/IOUtils.html) - 非常有用!广告.#3 - netty建立在NIO之上,我想我很清楚它是正确的方法.裸NIO频道通常会更快,但你必须*正确*. (2认同)