3

ここでは関係のないことを行うHTTPプロキシを作成しましたが、クライアントのサービス提供時間が大幅に増加しています(プロキシなしで600us、プロキシありで60000us)。私は、その時間の大部分がどこから来ているのかを見つけたと思います-プロキシがクライアントに送り返しを終えてからクライアントがそれを受け取り終えるまでの間。今のところ、サーバー、プロキシ、クライアントは同じホスト上で実行されており、アドレスとしてlocalhostを使用しています。

プロキシが送信を終了したら(少なくともsend()から戻ったら)、絶対時間を与えるgettimeofdayの結果を出力します。クライアントが受信すると、gettimeofdayの結果が出力されます。両方とも同じホスト上にあるため、これは正確である必要があります。すべてのsend()呼び出しにはフラグがないため、ブロックされています。2つの違いは約40000usです。

クライアント接続をリッスンするプロキシのソケットは、ヒントAF_UNSPEC、SOCK_STREAM、およびAI_PASSIVEで設定されます。おそらく、accept()からのソケットは同じパラメーターを持ちますか?

私がこれらすべてを正しく理解している場合、Apacheは600usですべてを実行できます(この40000usの遅延を引き起こしているものと同等のものを含む)。誰かがこれを引き起こしている可能性があるものを提案できますか?TCP_NODELAYオプションを設定してみましたが(違いがあったかどうかを確認するだけです)、送信の終了と受信の終了の間の遅延がすぐに減少しました。数を忘れましたが、1000us未満です。

これはすべてUbuntuLinux2.6.31-19にあります。助けてくれてありがとう

4

7 に答える 7

43

40 ミリ秒は Linux での TCP ACK 遅延です。これは、遅延 ACK とNagle アルゴリズムの間で不適切な相互作用が発生している可能性があることを示しています。これに対処する最善の方法は、応答を待つ前に、send()またはへの 1 回の呼び出しを使用してすべてのデータを送信することです。sendmsg()それが不可能な場合は、(受信側)、(送信側) 、(送信側)などの特定のTCP ソケット オプションが役立ちますが、不適切に使用すると問題が発生することもあります。 Nagle アルゴリズムを無効にするだけで、ソケットでの 1 回限りの設定ですが、他の 2 つは接続の存続期間中に適切なタイミングで設定する必要があるため、使用するのが難しい場合があります。TCP_QUICKACKTCP_CORKTCP_NODELAYTCP_NODELAY

于 2010-02-12T17:10:27.127 に答える
5

クライアント、プロキシ、オリジン サーバーが同じホスト上にあるプロキシでは、意味のあるパフォーマンス測定を実際に行うことはできません。

それらをすべてネットワーク上の異なるホストに配置します。それらすべてに実際のハードウェア マシンを使用するか、専用のハードウェア テスト システム (Spirent など) を使用します。

あなたの方法論は意味がありません。いずれにせよ、実際にはオリジンサーバーまでのレイテンシが 600us になる人はいません。すべてのタスクを同じホストで実行すると、競合が発生し、ネットワーク環境がまったく非現実的になります。

于 2010-02-12T11:52:08.547 に答える
4

前書き:

レイテンシーを下げるという一般的な質問に対する本当に正しい答えについて、私はすでにmark4oを賞賛しました. ほとんどの人がここに来て探している答えになると思うので、それが私のレイテンシの問題を解決するのにどのように役立ったかという観点から答えを翻訳したいと思います.

答え:

ノード間でできるだけ早く短いメッセージを取得することが重要なリアルタイム ネットワーク アプリ (マルチプレイヤー ゲームなど) では、NAGLE をオフにします。ほとんどの場合、これは「no-delay」フラグを true に設定することを意味します。

免責事項:

これは OP 固有の問題を解決しない可能性がありますが、ここに来るほとんどの人はおそらく、レイテンシの問題に関する一般的な質問に対するこの回答を探しているでしょう。

逸話的なバックストーリー:

2 つのメッセージを別々に送信するコードを追加するまで、私のゲームは問題なく動作していましたが、実行時間は互いに非常に接近していました。突然、250 ミリ秒の遅延が発生しました。これは大規模なコード変更の一部であったため、問題の原因を突き止めるために 2 日間を費やしました。2 つのメッセージを 1 つにまとめると、問題は解決しました。ロジックが mark4o の投稿につながったので、.Net ソケット メンバー "NoDelay" を true に設定し、必要な数のメッセージを連続して送信できるようにしました。

于 2012-01-15T06:24:21.500 に答える
3

たとえば、RedHat のドキュメントから:

送信されるすべてのパケットで低レイテンシーを必要とするアプリケーションは、TCP_NODELAY を有効にしたソケットで実行する必要があります。ソケット API を使用して setsockopt コマンドを使用して有効にすることができます。

int one = 1;
setsockopt(descriptor, SOL_TCP, TCP_NODELAY, &one, sizeof(one));

これを効果的に使用するには、アプリケーションは、論理的に関連する小さなバッファ書き込みを避ける必要があります。TCP_NODELAY が有効になっているため、これらの小さな書き込みにより、TCP はこれらの複数のバッファを個別のパケットとして送信し、全体的なパフォーマンスが低下する可能性があります。

于 2010-02-12T11:49:39.347 に答える
1

あなたの場合、その40msはおそらく単なるスケジューラータイムクォンタムです。つまり、システムが他のタスクに戻るのにかかる時間です。実際のネットワークで試してみると、まったく異なる画像が得られます。マルチコアマシンを使用している場合、Virtualboxまたはその他のVMで仮想OSインスタンスを使用すると、実際に何が起こるかをより正確に把握できます。

于 2010-02-12T12:03:12.827 に答える
1

TCP プロキシの場合、linux-netdev および /. 近々。

http://www.amailbox.org/mailarchive/linux-netdev/2010/5/26/6278007

http://developers.slashdot.org/story/10/11/26/1729218/Google-Microsoft-Cheat-On-Slow-Start-mdash-Should-You

Googleによるトピックに関する論文を含め、

http://www.google.com/research/pubs/pub36640.html

また、Google による IETF ドラフト、

http://zinfandel.levkowetz.com/html/draft-ietf-tcpm-initcwnd-00

于 2010-11-30T08:44:30.020 に答える
0

Windows の場合、TCP_NODELAY の設定が役立つかどうかはわかりません。私はそれを試しましたが、レイテンシーは依然として悪かったです。ある人が UDP を試すように勧めてくれたので、うまくいきました。

UDP のいくつかの複雑な例は私にはうまくいきませんでしたが、単純なものに出くわし、うまくいきました...

#include <Winsock2.h>
#include <WS2tcpip.h>
#include <system_error>
#include <string>
#include <iostream>

class WSASession
{
public:
    WSASession()
    {
        int ret = WSAStartup(MAKEWORD(2, 2), &data);
        if (ret != 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "WSAStartup Failed");
    }
    ~WSASession()
    {
        WSACleanup();
    }
private:
    WSAData data;
};

class UDPSocket
{
public:
    UDPSocket()
    {
        sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
        if (sock == INVALID_SOCKET)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Error opening socket");
    }
    ~UDPSocket()
    {
        closesocket(sock);
    }

    void SendTo(const std::string& address, unsigned short port, const char* buffer, int len, int flags = 0)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = inet_addr(address.c_str());
        add.sin_port = htons(port);
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    void SendTo(sockaddr_in& address, const char* buffer, int len, int flags = 0)
    {
        int ret = sendto(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&address), sizeof(address));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "sendto failed");
    }
    sockaddr_in RecvFrom(char* buffer, int len, int flags = 0)
    {
        sockaddr_in from;
        int size = sizeof(from);
        int ret = recvfrom(sock, buffer, len, flags, reinterpret_cast<SOCKADDR *>(&from), &size);
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "recvfrom failed");

        // make the buffer zero terminated
        buffer[ret] = 0;
        return from;
    }
    void Bind(unsigned short port)
    {
        sockaddr_in add;
        add.sin_family = AF_INET;
        add.sin_addr.s_addr = htonl(INADDR_ANY);
        add.sin_port = htons(port);

        int ret = bind(sock, reinterpret_cast<SOCKADDR *>(&add), sizeof(add));
        if (ret < 0)
            throw std::system_error(WSAGetLastError(), std::system_category(), "Bind failed");
    }

private:
    SOCKET sock;
};

サーバ

#define TRANSACTION_SIZE    8

static void startService(int portNumber)
{
    try
    {
        WSASession Session;
        UDPSocket Socket;
        char tmpBuffer[TRANSACTION_SIZE];
        INPUT input;
        input.type = INPUT_MOUSE;
        input.mi.mouseData=0;
        input.mi.dwFlags = MOUSEEVENTF_MOVE;

        Socket.Bind(portNumber);
        while (1)
        {
            sockaddr_in add = Socket.RecvFrom(tmpBuffer, sizeof(tmpBuffer));

            ...do something with tmpBuffer...

            Socket.SendTo(add, data, len);
        }
    }
    catch (std::system_error& e)
    {
        std::cout << e.what();
    }

クライアント

char *targetIP = "192.168.1.xxx";
Socket.SendTo(targetIP, targetPort, data, len);
于 2015-06-14T21:04:30.813 に答える