Замедление сериализации сокетов

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

Я пытаюсь создать очень простое промежуточное ПО типа RPC, где клиент может вызывать методы на сервере и извлекать объект обратно, если метод создает такой объект. Я использую библиотеку FST-Serializer для сериализации моего объекта и отправки их по сети / сокету.

В то время как я получил его и запустил, я обнаружил очень странную проблему, когда производительность сериализации значительно снижается, когда это происходит (примеры кода ниже):

Client Serialize params/method name -> Server retrieves information and executes method.

Это означает, что сеть работает медленно или реализация не может вызвать ее достаточно быстро, однако, если это происходит, она значительно ускоряется:

Client Serialize params/method name -> Server retrieves information and executes method -> Server serializes an object sends it back to client.

Хотя поначалу это не кажется большим делом, для пустых методов это может раздражать, так как мне приходится отправлять фиктивные данные, что означает, что я должен был совершить поездку по сети без всякой причины.

Я подумал, что, возможно, буфер недостаточно велик, но я экспериментировал с буфером другого размера и, похоже, ничего не решило, я также хочу уточнить, что приложение все еще работает нормально (т.е. без блокировок), только из-за снижения производительности. Итак, мой вопрос, что может вызвать такие, как замедление и это можно предотвратить / исправить?

Я запустил производительность YouKit, чтобы увидеть анализ горячих точек (новичок в этом тоже), и, похоже, метод BufferedReader.Read сильно замедляется.

С пустыми данными, отправленными обратно: Без фиктивных данных, отправленных обратно:

Фрагмент ServerThread, который прослушивает входящие вызовы (аналогичный код клиента создает один и тот же ввод / вывод с тем же размером буфера):

 public ServerThread(Socket connection){
        this.connection = connection;
        isActive = true;
    }

@Override
public void run() {
    try {
        input = new BufferedInputStream(connection.getInputStream(), BUFFER_SIZE);
        output = new BufferedOutputStream(connection.getOutputStream(), BUFFER_SIZE); //Buffersize is 512_000 atm

        do{
            Method m = (Method) readObject(input);
            //Invoke method via reflection
            Server.methodNames.get(m.getName()).invoke(this, m.getParams());
        }while(isActive);

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

@Override
public void transactionWillEnd() {
    this.currentTransactionLog = null;
    // This sends dummy data back to client or slowdown will occur
    try{
        writeObject(output, "SUCCESS");
        output.flush();
    } catch(Exception e){
        e.printStackTrace();
    }
}

Класс метода, который сериализован от клиента, отправленного на Serverthread

public class Method implements Serializable{
    private String name;
    private Object[] params;

    public Method(String name, Object...params) {
        this.name = name;
        this.params = params;
    }

    public String getName() {
        return name;
    }

    public Object[] getParams() {
        return params;
    }
}

TCPSerializer основан на TCPObjectSocket в FST (наследуется сервером и клиентом:

public class TCPSerializer {

private static FSTConfiguration config;
public static int BUFFER_SIZE = 512_000;

AtomicBoolean readLock = new AtomicBoolean(false);
AtomicBoolean writeLock = new AtomicBoolean(false);

public TCPSerializer() {
    config = FSTConfiguration.createDefaultConfiguration();
}

public Object readObject(InputStream input) throws Exception{
    try {
        while ( !readLock.compareAndSet(false,true) );
        return config.decodeFromStream(input);
    } finally {
        readLock.set(false);
    }
}

public void writeObject(OutputStream output, Object toWrite) throws Exception{
    try {
        while ( !writeLock.compareAndSet(false,true) );
        config.encodeToStream(output, toWrite);
    } finally {
        writeLock.set(false);
    }
}

Пример того, как Клиент вызывает метод:

@Override
public void transactionWillEnd() {
    String methodName = Helpers.getMethodName(0);
    Method m = new Method(methodName);
    try {
        client.writeObject(client.getOutput(), m);
        client.flush();
//Read dummy data before continuing.
        String verify = (String) client.readObject(client.getInput());
        if(!verify.equals("SUCCESS")) throw new Exception(verify);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

1 ответ

Решение

Алгоритм Nagle может значительно замедлить соединение, если он не выполняет действие запрос / ответ. Я видел, как это ожидало 40 мсек, пока другой блок байтов не объединился в пакет.

Я предлагаю вам попробовать отключить его, если вы передаете данные без ответа.

socket.setTcpNoDelay(true);

https://docs.oracle.com/javase/9/docs/api/java/net/Socket.html

Другие вопросы по тегам