17

在将Azure 服务总线中继netTcpRelayBinding和结合使用时,我们遇到了速度问题basicHttpRelayBinding。对于较小的消息大小 ( 10K ),中继以低延迟 ( 100ms ) 运行,但随着消息大小的增加 ( 100K ),我们会经历看似随机的响应时间 ( 600ms-1000ms )。我们希望改善较大消息的延迟成本。

服务总线中继是否支持使用消息压缩(gzipprotobuf-net等)?有没有人成功通过中继启用请求/响应压缩?通过 IIS 支持响应压缩很简单,但我们希望支持请求压缩以降低延迟成本。由于我们无法使用Fiddler分析中继,我们如何知道消息在通过中继时仍然被压缩?


我们发现的一个有趣的点是,在后续消息中继之间引入延迟 ( 2s ) 我们会获得更好的性能 ( 1​​00K - 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 自己压缩消息(请参阅下面的实用程序 - 您可以添加为扩展名)。

但是关于排队/节流问题。与较小/优化的块相比,较大的消息总是比网络滞后。如果您可以分解小于或等于 50k 或更少的数据或使用 udp,它可能会比在 tcp 上以更少的排队和验证传输更快。

TCP 数据包大小的绝对限制是 64K(65535 字节),但实际上这远远大于您将看到的任何数据包的大小,因为较低层(例如以太网)的数据包大小较小。

例如,以太网的 MTU(最大传输单元)为 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 回答