【问题标题】:Azure Service Bus Relay - Enabling CompressionAzure 服务总线中继 - 启用压缩
【发布时间】:2012-09-01 16:07:56
【问题描述】:

在将 Azure 服务总线中继netTcpRelayBindingbasicHttpRelayBinding 一起使用时,我们遇到了速度问题。对于较小的消息大小 (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
}

【问题讨论】:

    标签: c# wcf azure wcf-binding azureservicebus


    【解决方案1】:

    这不是一个完整的答案,但在服务器端,您可以将其添加到 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);
            }
        }
    }
    

    【讨论】:

      【解决方案2】:

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

      但是关于排队/限制问题。较大的消息总是比较小/优化的块更滞后于网络。如果您可以分解小于或等于 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;
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2014-03-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-13
        • 2015-08-23
        相关资源
        最近更新 更多