4

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

我已经实现了基本功能,一切正常。唯一的问题是通信速度或性能。我的 HTTP 代理系统由一个在远程服务器上运行的服务器程序和一个在本地机器上运行的客户端程序组成。到目前为止,该程序如下所示:

监听器.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);
    }
}

代理隧道.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.nioChannels 和 Buffers 而不是java.netSockets 和java.ioStream,我会获得任何性能改进吗?
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());

这是有缺陷的,原因有几个:

  • String您假设您的 HTTP 调用仅是文本(ASCII),二进制数据在转换为和返回后将是格式错误的byte[],请参阅:字符串、字节 [] 和压缩

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

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

不要使用Executors.newCachedThreadPool()

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

使用非阻塞网络堆栈

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

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

于 2012-08-19T08:39:55.243 回答