【问题标题】:Java - Fastest Stream for TCP/HTTP Communication?Java - TCP/HTTP 通信的最快流?
【发布时间】:2012-08-19 08:20:48
【问题描述】:

我正在尝试编写一个 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 流,我会获得任何性能改进吗?

【问题讨论】:

  • 愚蠢的问题:你为什么要重新发明轮子?
  • @Michael-O:当然是为了学习和教育目的!在侮辱别人之前最好三思而后行!

标签: java performance http proxy network-programming


【解决方案1】:

不要自己做

建议 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堆栈

这给我们带来了最后的建议。完全放弃阻塞套接字。它们很方便,但由于 thread-per-connection 要求而不能很好地扩展。太多的内存被用来保存堆栈,太多的 CPU 被浪费在上下文切换上。 netty 很棒,它在 NIO 之上构建了强大的抽象。

查看the examples,它们包含 HTTP 客户端/服务器代码。有一点学习曲线,但您可以预期性能会增长几个数量级。

【讨论】:

  • 好点。我一定会在我的程序中考虑这些。但是你没有回答我的其他问题。我的意思是最后的问题 #2 和 #3。
  • @SeyedMohammad:对。对于#2:通常,是的,只要您不缓冲已经缓冲的流。但是在您的情况下,您已经以块的形式读取数据,缓冲只会稍微增加内存消耗,并且可能会使您的代码变慢。顺便说一句,考虑IOUtils - 非常有用!广告。 #3 - netty 建立在 NIO 之上,我想我很清楚这是正确的方法。裸 NIO 通道通常会更快,但您必须正确使用它们
猜你喜欢
  • 2019-03-06
  • 2015-05-24
  • 2015-03-05
  • 2014-11-05
  • 1970-01-01
  • 1970-01-01
  • 2020-05-10
  • 2016-01-14
  • 2011-08-26
相关资源
最近更新 更多