2

私はソケットのシリアライゼーションに慣れていないことを序文にしたいと思います。反対票を投じる前に、編集に追加できるものを提案してください。プロジェクトがかなり大きいため、コードをできるだけ小さく分割しようとしました。

クライアントがサーバー上のメソッドを呼び出し、メソッドがそのようなオブジェクトを作成した場合にオブジェクトを取得できる、非常に単純な 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.

これは最初は大したことではないように思えますが、void メソッドの場合、ダミー データを送り返す必要があるため、理由もなくネットワーク旅行をしなければならないため、煩わしくなります。

これはバッファが十分に大きくない可能性があると思いましたが、さまざまなバッファサイズを試してみましたが、何も解決しないようです. だから私の質問は、スローダウンなどの原因は何ですか?それは予防可能/修正可能ですか?

ホットスポット分析を確認するために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();
    }
}

サーバースレッドに送信されたクライアントからシリアル化されたメソッド クラス

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;
    }
}

FST の TCPObjectSocket に基づく TCPSerializer (serverthread&client によって継承:

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();
    }
}
4

1 に答える 1

1

Nagle のアルゴリズムは、要求/応答アクションを実行していない場合、接続を大幅に遅くする可能性があります。バイトの別のブロックがパケットに合体するまで 40 ミリ秒待機するのを見てきました。

返信なしでデータをストリーミングしている場合は、オフにすることをお勧めします。

socket.setTcpNoDelay(true);

https://docs.oracle.com/javase/9​​/docs/api/java/net/Socket.html#setTcpNoDelay(ブール値)

于 2018-03-01T20:43:37.930 に答える