17

と の両方でAzure Service Bus Relayを使用しているときに、速度の問題が発生しています。小さなメッセージ サイズ ( 10K ) では、リレーは低遅延 ( 100ms ) で動作しますが、メッセージ サイズが大きくなると ( 100K )、一見ランダムな応答時間 ( 600ms-1000ms ) が発生します。大きなメッセージのレイテンシ コストを改善したいと考えています。netTcpRelayBindingbasicHttpRelayBinding

メッセージ圧縮 ( gzipprotobuf-netなど) の使用は Service Bus Relay でサポートされていますか? リレーを介してリクエスト/レスポンス圧縮の両方を有効にすることに成功した人はいますか? IIS を介して応答圧縮をサポートするのは簡単ですが、遅延コストを改善するために要求圧縮をサポートしたいと考えています。Fiddlerを使用してリレーをプロファイリングすることはできないため、メッセージがリレーを通過するときにまだ圧縮されていることをどのように知ることができるでしょうか?


私たちが発見した興味深い点は、後続のメッセージリレー間に遅延 (2 秒) を導入すると、パフォーマンスが向上する ( 100K - 200ms ) ことです。より大きなメッセージが自動的に調整されている可能性はありますか? スロットリング条件をトリガーするメッセージ サイズのカットオフを知っておくとよいでしょう。

私たちのテストでは、ランダムなメッセージ文字列をサービス リレーに送信し、サーバーから要求文字列をエコー バックするだけです。このクライアント/サーバーを複数の地理的な場所から (ファイアウォール/Web フィルターの問題を除外するために)試してみましたが、同じ遅延動作が発生しました。

サーバ側

public class ServiceRelayProfiler : IServiceRelayProfiler
{
    public string HelloProfiler(string name)
    {
        return string.Format("Hello {0}", name);
    }
}

クライアント側

ChannelFactory<IServiceRelayProfiler> channelFactory = new ChannelFactory<IServiceRelayProfiler>("helloProfilerTcp");
IServiceRelayProfiler channel = channelFactory.CreateChannel();
string message = RandomString(100000); // 100K
for (int i = 0; i < 100; i++)
{
    DateTime start = DateTime.Now;
    string response = channel.HelloProfiler(message);
    DateTime end = DateTime.Now;
    TimeSpan duration = end - start;
    Console.WriteLine("Response is: {0} at {1}\tDuration: {2}ms", response.Substring(0, 20) + "....", end, duration.Milliseconds);
    //Thread.Sleep(2000); // delay makes response times more consistent
}
4

2 に答える 2

2

完全な答えではありませんが、サーバー側では、これをに追加してglobal.asax.cs、リクエストの解凍を許可できます。

public class MvcApplication : System.Web.HttpApplication
{    
    protected void Application_BeginRequest(Object sender, EventArgs e)
    {
        //Activate request decompression
        string contentEncoding = Request.Headers["Content-Encoding"];
        if (contentEncoding != null && contentEncoding.Equals("gzip", StringComparison.CurrentCultureIgnoreCase))
        {
            Request.Filter = new GZipStream(Request.Filter, CompressionMode.Decompress, true);
        }
    }
}
于 2013-03-12T22:14:29.933 に答える
1

System.IO.Compression を使用して自分でメッセージを圧縮することもできます (以下の util を参照してください - 拡張子として追加するだけです)。

しかし、キューイング/スロットリングの質問について。大きなメッセージは常に、小さな/最適化されたチャンクよりもネットワークから遅れます。50k 以下のデータを分割するか、udp を使用できる場合、tcp よりも少ないキューイングと検証でより高速に送信できます。

TCP パケット サイズの絶対的な制限は 64K (65535 バイト) ですが、実際には、下位層 (イーサネットなど) のパケット サイズが小さいため、これは表示されるパケットのサイズよりもはるかに大きくなります。

たとえば、イーサネットの MTU (Maximum Transmission Unit) は 1500 バイトです。一部のタイプのネットワーク (トークン リングなど) は MTU が大きく、MTU が小さいタイプもありますが、値は物理テクノロジごとに固定されています。

ここから: TCP 接続の最大パケット サイズ

物事を分割すると物事はよりスムーズになります。マルチプレイヤーゲームはこれを行う必要があり、さらに udp を使用して追加の検証を制限します (信頼できる udp を実装して個々のメッセージを検証し、必要な場合にのみ順序付けます)。

CompressionUtil クラス、送信前/受信後にメッセージを圧縮します: https://gist.github.com/drawcode/8948293

public static class CompressUtil {

    public static string ToCompressed(this string val) {
        if (!IsStringCompressed(val)) {
            return CompressString(val);
        }
        return val;
    }

    public static string ToDecompressed(this string val) {
        if (IsStringCompressed(val)) {
            return DecompressString(val);
        }
        return val;
    }

    public static string CompressString(string text) {
        byte[] buffer = Encoding.UTF8.GetBytes(text);
        var memoryStream = new MemoryStream();
        using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Compress, true)) {
            gZipStream.Write(buffer, 0, buffer.Length);
        }

        memoryStream.Position = 0;

        var compressedData = new byte[memoryStream.Length];
        memoryStream.Read(compressedData, 0, compressedData.Length);

        var gZipBuffer = new byte[compressedData.Length + 4];
        Buffer.BlockCopy(compressedData, 0, gZipBuffer, 4, compressedData.Length);
        Buffer.BlockCopy(BitConverter.GetBytes(buffer.Length), 0, gZipBuffer, 0, 4);
        return Convert.ToBase64String(gZipBuffer);
    }

    public static string DecompressString(string compressedText) {
        byte[] gZipBuffer = Convert.FromBase64String(compressedText);
        using (var memoryStream = new MemoryStream()) {
            int dataLength = BitConverter.ToInt32(gZipBuffer, 0);
            memoryStream.Write(gZipBuffer, 4, gZipBuffer.Length - 4);

            var buffer = new byte[dataLength];

            memoryStream.Position = 0;
            using (var gZipStream = new GZipStream(memoryStream, CompressionMode.Decompress)) {
                gZipStream.Read(buffer, 0, buffer.Length);
            }

            return Encoding.UTF8.GetString(buffer);
        }
    }

    public static bool IsStringCompressed(string data) {
        if (IsStringCompressedGZip(data) || IsStringCompressedPKZip(data)) {
            return true;
        }
        return false;
    }

    public static bool IsStringCompressedGZip(string data) {
        return CheckSignatureString(data, 3, "1F-8B-08");
    }

    public static bool IsStringCompressedPKZip(string data) {
        return CheckSignatureString(data, 4, "50-4B-03-04");
    }

    public static bool CheckSignatureFile(string filepath, int signatureSize, string expectedSignature) {
        if (String.IsNullOrEmpty(filepath))
            throw new ArgumentException("Must specify a filepath");
        if (String.IsNullOrEmpty(expectedSignature))
            throw new ArgumentException("Must specify a value for the expected file signature");
        using (FileStream fs = new FileStream(filepath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
            if (fs.Length < signatureSize)
                return false;
            byte[] signature = new byte[signatureSize];
            int bytesRequired = signatureSize;
            int index = 0;
            while (bytesRequired > 0) {
                int bytesRead = fs.Read(signature, index, bytesRequired);
                bytesRequired -= bytesRead;
                index += bytesRead;
            }
            string actualSignature = BitConverter.ToString(signature);
            if (actualSignature == expectedSignature)
                return true;
            else
                return false;
        }
    }

    public static bool CheckSignatureString(string data, int signatureSize, string expectedSignature) {

        byte[] datas = Encoding.ASCII.GetBytes(data);
        using (MemoryStream ms = new MemoryStream(datas)) {
            if (ms.Length < signatureSize)
                return false;
            byte[] signature = new byte[signatureSize];
            int bytesRequired = signatureSize;
            int index = 0;
            while (bytesRequired > 0) {
                int bytesRead = ms.Read(signature, index, bytesRequired);
                bytesRequired -= bytesRead;
                index += bytesRead;
            }
            string actualSignature = BitConverter.ToString(signature);
            if (actualSignature == expectedSignature)
                return true;
            else
                return false;
        }
    }
}
于 2014-02-12T01:21:24.287 に答える