c# - Relé de bus de servicio de Azure: habilitación de la compresión
wcf wcf-binding (2)
No es una respuesta completa, pero en el lado del servidor, puede agregar esto a global.asax.cs
para permitir la descompresión de la solicitud:
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);
}
}
}
Estamos teniendo problemas de velocidad al usar el relé de bus de servicio de Azure con netTcpRelayBinding
y basicHttpRelayBinding
. Con tamaños de mensaje pequeños ( 10K ), el relé opera con baja latencia ( 100 ms ), pero a medida que aumenta el tamaño del mensaje ( 100 K ) experimentamos tiempos de respuesta aparentemente aleatorios ( 600 ms-1000 ms ). Nos gustaría mejorar el costo de latencia para mensajes más grandes.
¿Se protobuf-net compresión de mensajes ( gzip , protobuf-net , etc. ) a través del relé de bus de servicio? ¿Alguien ha tenido éxito en habilitar la compresión de solicitud / respuesta a través del relé? Es trivial admitir la compresión de respuestas a través de IIS , pero nos gustaría admitir la compresión de solicitudes para mejorar nuestros costos de latencia. Ya que no podemos perfilar el relé con Fiddler , ¿cómo sabemos que el mensaje todavía está comprimido cuando pasa a través del relé?
Un punto interesante que descubrimos es que al introducir un retraso entre los relés de mensajes subsiguientes ( 2s ) recibimos un mejor rendimiento ( 100K - 200ms ). ¿Podría ser que los mensajes más grandes se están acelerando automáticamente? Sería bueno saber el límite de tamaño del mensaje que desencadena una condición de regulación.
Para nuestra prueba: solo enviamos una cadena de mensaje al azar al servicio de retransmisión y devolvemos la cadena de solicitud desde el servidor. Hemos intentado este cliente / servidor desde varias ubicaciones geográficas ( para descartar problemas de firewall / filtro web ) y experimentamos el mismo comportamiento de latencia.
Lado del servidor
public class ServiceRelayProfiler : IServiceRelayProfiler
{
public string HelloProfiler(string name)
{
return string.Format("Hello {0}", name);
}
}
Lado del cliente
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
}
Una cosa que podría intentar es comprimir los mensajes a través de System.IO.Compression (vea la sección de utilidad a continuación, puede agregar como una extensión).
Pero en la cola / la pregunta de estrangulamiento. Los mensajes más grandes siempre demorarán una red más que los fragmentos más pequeños / optimizados. Si puede dividir los datos de menos de 50k o menos o usar udp, puede transmitir más rápido con menos cola y validación que en tcp.
La limitación absoluta en el tamaño del paquete TCP es 64K (65535 bytes), pero en la práctica esto es mucho más grande que el tamaño de cualquier paquete que verá, porque las capas más bajas (por ejemplo, Ethernet) tienen tamaños de paquete más bajos.
La MTU (Unidad de transmisión máxima) para Ethernet, por ejemplo, es de 1500 bytes. Algunos tipos de redes (como Token Ring) tienen MTU más grandes, y algunos tipos tienen MTU más pequeñas, pero los valores son fijos para cada tecnología física.
desde aquí: tamaño máximo de paquete para una conexión TCP
Las cosas se pondrán más suaves cuando se dividan las cosas, los juegos multijugador tienen algo que ver con esto y también usan udp more para limitar la validación adicional (implementar udp confiable para validar mensajes individuales y hacer pedidos solo cuando sea necesario).
La clase CompressionUtil, comprime los mensajes dentro y fuera antes de enviar / después de recibir: 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;
}
}
}