Производительность с использованием потоков объектов Javas с сокетами

Я пытаюсь сделать локальный IPC с использованием потоков Sockets и Object на Java, но я вижу плохую производительность.

Я тестирую время ping отправки объекта через ObjectOutputStream для получения ответа через ObjectInputStream через Socket.

Здесь Requestor:

public SocketTest(){

    int iterations = 100;
    try {
        Socket socket = new Socket("localhost", 1212);

        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream()); 
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream()); 

        double start = System.currentTimeMillis();
        for (int i = 0; i < iterations; ++i) {

            Request request = new Request();
            objectOutputStream.writeObject(request);

            Response response = (Response)objectInputStream.readObject();
        }
        double finish = System.currentTimeMillis();
        System.out.println("Per ping: " + (finish - start) / iterations );

    } catch (Exception e) {
        e.printStackTrace();
    }
}

Здесь ответчик:

public ServerSocketTest(){

    try {

        ServerSocket serverSocket = new ServerSocket(1212);
        Socket socket = serverSocket.accept();

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());

        Request request = (Request)objectInputStream.readObject();
        while (request != null) {

            Response response = new Response();
            objectOutputStream.writeObject(response);
            request = (Request)objectInputStream.readObject();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

В результате я получаю:

За пинг: 80.35

80 мс мешает локальному трафику.

Класс запроса и ответа очень мал, и их сериализация выполняется быстро.

Я попытался наивно добавить:

socket.setKeepAlive(истина);
socket.setTcpNoDelay(истина);

с небольшим эффектом.

выполнение ping localhost:

64 байта из localhost.localdomain(127.0.0.1): icmp_seq = 0 ttl = 64 раз = 0,035 мс
64 байта из localhost.localdomain(127.0.0.1): icmp_seq = 1 ttl = 64 раз = 0,037 мс
64 байта из localhost.localdomain(127.0.0.1): icmp_seq = 2 ttl = 64 раз = 0,049 мс
64 байта из localhost.localdomain(127.0.0.1): icmp_seq = 3 ttl = 64 раз = 0,039 мс
64 байта из localhost.localdomain(127.0.0.1): icmp_seq = 4 ttl = 64 раз = 0,056 мс

также быстро.

Java-версия 1.6.0_05l Запуск на RedHat 2.4

Любые идеи будут оценены.

Ответ 1

Вы пробовали встраивать оба запроса ts и ответов в BufferedInputStream/BufferedOutputStream? Он должен широко улучшать характеристики.

Ответ 2

Итак, создайте BufferedOutputStream и очистите его перед построением BufferedInputStream. Чтобы избежать зависания.

http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4788782

он говорит в соответствии с документацией

Если вы измените тестовый пример таким образом, чтобы как AServer, так и AClient ObjectOutputStream перед ObjectInputStream, тест не блок. Это ожидаемый поведения, учитывая следующее документация:

Конструктор ObjectOutputStream:
    Создает объект ObjectOutputStream, который записывается в указанный файл OutputStream. Этот конструктор пишет заголовок потока сериализации для основной поток; абоненты могут хотите сбросить поток немедленно, чтобы конструкторы для получения ObjectInputStreams не будут блокироваться, когда чтение заголовка.

Конструктор ObjectInputStream:
    Создает ObjectInputStream, который читает из указанного InputStream. Поток сериализации заголовок считывается из потока и проверено. Этот конструктор блокирует пока соответствующий ObjectOutputStream написал и сбросил заголовок.

Ответ 3

Я бы ожидал, что вам нужно позвонить objectOutputStream.flush() с обеих сторон, чтобы обеспечить немедленное отправку данных в сеть. В противном случае стек TCP может подождать некоторое время для получения дополнительных данных для заполнения частичного IP-пакета.

Ответ 4

В дополнение к использованию буферизованных потоков и вызову flush() перед каждым чтением вы также должны сначала создать ObjectOutputStream с обоих концов. Кроме того, тестирование для readObject() возвращающего нуля бессмысленно, если вы не планируете вызывать writeObject (null). Тест для EOS с readObject() является catch (EOFException exc).

Ответ 5

Я бы все же подумал, что это будет быстрее. Есть ли что-нибудь еще, что я могу сделать, чтобы улучшить это?

Это похоже на микро-бенчмарк. Их всегда трудно понять, но я думаю, что если вы начнете с отправки сообщений 2000 сообщений, прежде чем начинать измерения латентности.

Также см. этот подробный ответ о том, как правильно выполнять микро-тесты.