Java socket性能瓶颈:在哪里?

时间:2010-12-15 15:08:14

标签: 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();
}

TcpRequest类只是一个没有任何特殊内容的虚拟类。

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

如果你在发送之前保留一个相同的对象并修改其内容,你也会有很高的转移率...但是陷入了令人讨厌的陷阱:

  

使用对象序列化时   重要的是要记住这一点   ObjectOutputStream维护一个   哈希表映射所写的对象   进入流程到句柄。当一个   object被写入流中   第一次,它的内容将是   复制到流。随后   然而,写入会产生一个句柄   被写入的对象   流。

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

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

解决:

替换:

out = new ObjectOutputStream(kkSocket.getOutputStream());

使用:

out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()))

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

4 个答案:

答案 0 :(得分:8)

找到它:

而不是:

out = new ObjectOutputStream(kkSocket.getOutputStream());

您应该使用:

out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()));

out.flush();
发送消息时

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

答案 1 :(得分:1)

我不相信该基准测试的结果。它不能确保JVM预热;即,在测量执行时间之前,将类加载,初始化并编译为本机代码。通过运行基准测试,本机代码编译很可能会在某种程度上发挥作用,这会增加一个或多个循环所花费的时间。

答案 2 :(得分:0)

您遇到的问题不是套接字的吞吐量低;它是默认的Java序列化。

当你一遍又一遍地发送同一个对象时,它只被序列化一次,然后每次都会发送一个对它的引用;这就解释了为什么它如此迅速。

每次创建新对象时,必须使用相对较慢的Java序列化机制序列化新对象,这可能比您需要的更复杂。

您可以采取的措施是为您的类实现自定义序列化代码,或者创建自己的类外部对象序列化协议(并使用DataOutput而不是ObjectOutputStream)。

这篇文章有很多好的信息,即使它有点过时了:http://java.sun.com/developer/technicalArticles/Programming/serialization/ 请参阅性能注意事项的最后一部分

答案 3 :(得分:0)

在处理小数据包时,应设置TCP_NODELAY选项。否则,它们将被延迟以尝试优化网络通信(也称为Nagle's algorithm)。

    socket.setTcpNoDelay(true);

使用缓冲区有帮助的原因是,延迟仅影响 small 数据包。因此,缓冲本身并不是帮助,而是数据包的大小。