Azure Service Bus Relay - 启用压缩

时间:2012-09-07 18:50:03

标签: c# wcf azure wcf-binding azureservicebus

netTcpRelayBindingbasicHttpRelayBinding使用 Azure Service Bus Relay 时,我们遇到速度问题。对于小消息大小( 10K ),继电器以低延迟( 100ms )运行,但随着消息大小的增加( 100K ),我们会遇到看似随机的响应时间( 600ms-1000ms )。我们希望改善较大邮件的延迟成本。

是否正在使用通过服务总线中继支持的邮件压缩( gzipprotobuf-net)?有没有人通过中继启用请求/响应压缩成功?它是trivial to support response compression through IIS,但我们希望支持请求压缩以提高延迟成本。由于我们无法使用Fiddler来描述中继,我们如何知道该消息在通过中继时仍然被压缩?


我们发现一个有趣的观点是,在后续消息中继( 2s )之间引入延迟,我们可以获得更好的性能( 100K - 200ms )。可能是更大的消息被自动限制?知道触发限制条件的消息大小截止值会很好。

对于我们的测试 - 我们只是向服务中继发送一个随机消息字符串,并从服务器回送请求字符串。我们已尝试从多个地理位置(排除防火墙/网络过滤器问题)的此客户端/服务器,并遇到相同的延迟行为。

服务器端

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
}

2 个答案:

答案 0 :(得分: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);
        }
    }
}

答案 1 :(得分:1)

您可以尝试的一件事是通过System.IO.Compression压缩消息(请参阅下面的util - 您可以添加为扩展名)。

但是在排队/节流问题上。较大的消息总是比较小/优化的块更多地滞后于网络。如果你可以将数据分解到50k或更低或者使用udp,它可以通过比tcp更少的排队和验证来传输。

  

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

     

例如,以太网的MTU(最大传输单元)为1500字节。某些类型的网络(如令牌环)具有较大的MTU,而某些类型的MTU较小,但每种物理技术的值都是固定的。

从这里开始:maximum packet size for a TCP connection

当你分手时,事情会变得更顺畅,多人游戏对此有所帮助,他们也会使用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;
        }
    }
}
相关问题