Java socket性能瓶颈:在哪里?

我最近开始开发一个密集使用网络的应用程序。 第一次使用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类只是一个没有任何特殊function的虚拟类。

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

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

使用对象序列化时,请务必记住ObjectOutputStream维护一个哈希表,该哈希表将写入流的对象映射到句柄。 当对象第一次写入流时,其内容将被复制到流中。 但是,后续写入会导致正在写入流的对象的句柄。

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

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

解决了:

通过替换:

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

附:

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

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

找到了:

代替:

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

你应该使用:

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

 out.flush(); 

发送消息时

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

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

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

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

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

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

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