Java socket性能瓶颈:在哪里?

dag*_*ies 8 java sockets

我最近开始开发一个密集使用网络的应用程序.第一次使用RMI尝试,由于几个原因,我们切换到纯套接字.但是,当通过网络或甚至在localhost上测试套接字时,我们降低到25个请求/秒的速率.使用RMI时,它高出两个数量级.

通过更多测试,我们获得了以下(对于localhost):

  • 始终发送相同的对象:31628个请求/秒
  • 始终发送新对象:25个请求/秒
  • 只有对象创建率:每秒3-4百万(这不是瓶颈)

这是客户端代码:(服务器端只回复"ACK")

public static void main(String[] args) throws IOException, ClassNotFoundException
{
    Socket kkSocket = null;
    ObjectOutputStream out = null;
    ObjectInputStream in = null;


    kkSocket = new Socket("barium", 4444);
    out = new ObjectOutputStream(kkSocket.getOutputStream());
    in = new ObjectInputStream(kkSocket.getInputStream());


    long throughput;
    long millis;

    TcpRequest hello = null;


    throughput = 0;
    millis = System.currentTimeMillis();
    while (System.currentTimeMillis() < millis + 1000)
    {
        hello = new TcpRequest();
        hello.service = "hello";
        hello.payload = Math.random();
        throughput++;
    }

    System.out.println("-------------------------------------------------------");
    System.out.println("|        Objects created: " + (throughput)  + " requests/sec.");
    System.out.println("-------------------------------------------------------");


    throughput = 0;
    millis = System.currentTimeMillis();
    while (System.currentTimeMillis() < millis + 1000)
    {
        out.writeObject(hello);
        Object res = in.readObject();
        throughput++;
    }
    System.out.println("-------------------------------------------------------");
    System.out.println("|        Same object throughput: " + (throughput)  + " requests/sec.");
    System.out.println("-------------------------------------------------------");


    throughput = 0;
    millis = System.currentTimeMillis();
    while (System.currentTimeMillis() < millis + 1000)
    {
        hello = new TcpRequest();
        out.writeObject(hello);
        Object res = in.readObject();
        throughput++;
    }
    System.out.println("-------------------------------------------------------");
    System.out.println("|        New objetcs throughput: " + (throughput)  + " requests/sec.");
    System.out.println("-------------------------------------------------------");


    out.close();
    in.close();

    kkSocket.close();
}
Run Code Online (Sandbox Code Playgroud)

TcpRequest类只是一个没有任何特殊功能的虚拟类.

因此,如果创建对象很快,如果通过网络发送它很快...为什么地球上通过网络发送新对象这么慢?!?!

如果您在发送之前保留相同的对象并修改其内容,您也将具有较高的传输速率......但是陷入了令人讨厌的陷阱:

当使用对象序列化工作是要记住的是,ObjectOutputStream中保持哈希表映射写进流的句柄的对象是很重要的.当第一次将对象写入流时,其内容将被复制到流中.但是,后续写入会导致正在写入流的对象的句柄.

......发生在我们身上并在搞清楚之前引起了几个小时的调试.

所以基本上......你如何用套接字实现高吞吐量?(...我的意思是,随着RMI成为它的包装,我们已经高了两个数量级!)

解决了:

通过替换:

out = new ObjectOutputStream(kkSocket.getOutputStream());
Run Code Online (Sandbox Code Playgroud)

附:

out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()))
Run Code Online (Sandbox Code Playgroud)

性能再次正常(与同一对象案例几乎相同的高吞吐量)

dag*_*ies 8

找到了:

代替:

out = new ObjectOutputStream(kkSocket.getOutputStream());
Run Code Online (Sandbox Code Playgroud)

你应该使用:

out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()));
Run Code Online (Sandbox Code Playgroud)

out.flush();
Run Code Online (Sandbox Code Playgroud)

发送消息时

...有很大的不同......虽然我不确切知道为什么.

  • 我可以解释一下.如果使用不使用缓冲区的流,则每个`write`都会产生一个系统调用.每个系统调用可能需要数千个额外的机器指令. (5认同)