Я недавно начал разработку приложения, интенсивно использующего сети.
Первая попытка была сделана с использованием RMI, и по нескольким причинам мы переключились на чистые сокеты. Однако при тестировании сокетов по сети или даже на локальном хосте мы снижали скорость до 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 поддерживает
хэш-таблица, отображающая записанные объекты
в поток ручке. Когда
Объект записывается в поток для
в первый раз его содержимое будет
скопировал в стрим. последующее
пишет, однако, в результате дескриптор
объект записывается в
поток.
... что случилось с нами и вызвало несколько часов отладки, прежде чем понять это.
Так в принципе ... как вы добиваетесь высокой производительности с сокетами? (... Я имею в виду, что RMI была оберткой вокруг него, мы были уже на два порядка выше!)
РЕШИТЬ:
Заменив:
out = new ObjectOutputStream(kkSocket.getOutputStream());
С:
out = new ObjectOutputStream(new BufferedOutputStream(kkSocket.getOutputStream()))
Производительность снова нормальная (почти такая же высокая пропускная способность, как и в случае с тем же объектом)