2010-12-15 4 views
8

Недавно я начал разработку приложения, интенсивно использующего сети. Первая попытка была сделана с использованием RMI, и по нескольким причинам мы переключились на чистые сокеты. Однако при тестировании сокетов по сети или даже на локальном хостинге мы опустились до 25 запросов в секунду. При использовании RMI он был на два порядка выше.Узкое место в производительности сокетов Java: где?

С немного больше испытаний, мы получили следующее (для локального хоста):

  • Отправка всегда один и тот же объект: 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 поддерживает хэш-таблицу , отображающую объекты, написанные , в поток для дескриптора. Когда объект записывается в поток для в первый раз, его содержимое будет , скопированное в поток. Последующее сообщение приводит к тому, что дескриптор записывается в поток .

... который произошел с нами и вызвал несколько часов отладки, прежде чем вычислять его.

Итак, в основном ... как вы достигаете высокой пропускной способности с помощью сокетов? (... Я имею в виду, что с RMI, являющимся оберткой вокруг него, мы уже были на два порядка выше!)

РЕШИТЬ:

Заменив:

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

С:

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

Представления являются нормальными снова (почти такой же высокой пропускной способностью, как и с тем же объектом случае)

+1

же, как этот вопрос: http://stackoverflow.com/questions/2251051/ performance-issue-use-javas-object-streams-with-socket ... нет ответов. – dagnelies 2010-12-15 15:13:32

+0

Если ваши объекты не ссылаются друг на друга, вы можете вызвать ObjectOutputStream.reset() после записи, чтобы предотвратить запоминание потока всеми объектами, которые он написал. Хотя это обычно вызывает проблемы с памятью на стороне чтения, а не производительность на стороне сервера. Пробовали ли вы профилировать свое приложение, чтобы узнать, где он проводит больше времени? – 2010-12-15 15:19:22

ответ

6

Найдено:

Вместо:

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

Вы должны использовать:

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

И

out.flush(); 

при отправке сообщения.

... имеет огромное значение ... хотя я точно не знаю почему.

+0

Это может быть частью проблемы, но только ее частью. Пожалуйста, см. Мой комментарий. – 2010-12-15 15:20:12

1

Я не верю результатам этого теста. Это не гарантирует, что JVM разогревается; то есть эти классы загружаются, инициализируются и скомпилируются в собственный код перед измерением времени выполнения. Существует хорошая вероятность того, что компиляция собственного кода запускается частично путем запуска теста, и это увеличивает время, затрачиваемое на один или несколько циклов.

0

Проблема, с которой вы сталкиваетесь, - это не низкая пропускная способность с разъемами; это медленная сериализация Java по умолчанию.

Когда вы отправляете один и тот же объект снова и снова, он действительно только сериализуется один раз, а затем ссылка на него отправляется каждый раз; это объясняет, почему это происходит так быстро.

Когда вы каждый раз создаете новый объект, этот новый объект должен быть сериализован с использованием относительно медленного механизма сериализации Java, который также, вероятно, намного сложнее того, что вам нужно.

Что вы можете сделать, чтобы улучшить это, либо реализовать пользовательский код сериализации для вашего класса, либо сделать свой собственный протокол сериализации объектов, который является внешним по отношению к классу (и использовать DataOutput вместо ObjectOutputStream).

В этой статье есть много хорошей информации, даже если она несколько устарели: http://java.sun.com/developer/technicalArticles/Programming/serialization/ См последнюю часть из соображений производительности

Смежные вопросы