Azure服务总线中继-启用压缩

17
我们在使用Azure Service Bus Relay的netTcpRelayBinding和basicHttpRelayBinding时存在速度问题。在消息大小较小(10K)时,中继操作具有较低的延迟(100毫秒),但随着消息大小的增加(100K),我们遇到了看似随机的响应时间(600ms-1000ms)。我们希望改善较大消息的延迟成本。
使用消息压缩(例如gzipprotobuf-net等)是否支持通过Service Bus中继?有人成功启用了请求/响应压缩吗?支持响应压缩通过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
}
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);
        }
    }
}

1

你可以尝试使用 System.IO.Compression (参见下面的 util - 你只需要添加作为扩展即可)来压缩消息。

但在队列和限流方面,大型消息会比小型/优化的块更容易使网络延迟。如果您可以将数据分成50k或更少的块,或者使用udp,它可能比tcp传输更快,且队列和验证较少。

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

例如,Ethernet的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;
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接