Transferencia de grandes cargas útiles de datos(objetos serializados) utilizando wsHttp en WCF con seguridad de mensajes
large-data-volumes wshttpbinding (5)
Algunas soluciones más ligeras, pero no garantizadas, serían:
- use el
DataContractSerializer
lugar ya que posee ambos lados. Esto no requiere información de tipo incrustada, que es significativamente grande. - use
[DataMember(EmitDefaultValue = false)]
que se analiza en una pregunta que[DataMember(EmitDefaultValue = false)]
, de nuevo porque posee ambos lados; al hacerlo, se reducirá parte del tamaño del mensaje (cuánto depende, por supuesto, de cuántos campos en el gráfico están predeterminados). - use
[DataContract(IsReference=true)]
, especialmente si tiene muchos objetos de valor repetido o datos de referencia - use algún tipo de regulación en el servidor para reducir la presión de memoria de los resultados simultáneos
Estos son, por supuesto, compensaciones, por ejemplo, con la legibilidad.
Tengo un caso en el que necesito transferir grandes cantidades de gráficos de objetos serializados (a través de NetDataContractSerializer ) usando WCF usando wsHttp. Estoy usando seguridad de mensajes y me gustaría continuar haciéndolo. Utilizando esta configuración, me gustaría transferir un gráfico de objeto serializado que a veces puede aproximarse a unos 300 MB, pero cuando intento hacerlo, empiezo a ver una excepción de tipo System.InsufficientMemoryException.
Después de una pequeña investigación, parece que, de forma predeterminada, en WCF, el resultado de una llamada de servicio está incluido en un solo mensaje que contiene los datos serializados y estos datos se almacenan de manera predeterminada en el servidor hasta que todo el mensaje se escribe por completo. Por lo tanto, la excepción de memoria se debe al hecho de que el servidor se está quedando sin recursos de memoria que puede asignar porque ese búfer está lleno. Las dos recomendaciones principales que he encontrado son usar la transmisión por secuencias o la fragmentación para resolver este problema, sin embargo, no me queda claro qué implica eso y si alguna de las soluciones es posible con mi configuración actual (wsHttp / NetDataContractSerializer / Message Security). Hasta ahora, entiendo que usar la seguridad de los mensajes de transmisión no funcionará porque el cifrado y descifrado de mensajes deben funcionar en todo el conjunto de datos y no en un mensaje parcial. Sin embargo, parece que la fragmentación podría ser posible, pero no me queda claro cómo se haría con las otras restricciones que he enumerado. Si alguien pudiera ofrecer alguna orientación sobre qué soluciones están disponibles y cómo implementarlas, se lo agradecería enormemente.
Debo agregar que, en mi caso, realmente no me preocupa la interoperabilidad con otros clientes, ya que poseemos y controlamos cada lado de la comunicación y utilizamos el patrón de interfaz compartido para los datos transferidos a cualquier lado. Así que estoy abierto a cualquier idea que se ajuste a las restricciones de usar wsHttp con seguridad de mensajes para transferir gráficos de objetos serializados utilizando NetDataContractSerializer y prefiero una solución donde no tenga que cambiar mis servicios existentes y la infraestructura circundante.
Recursos Relacionados:
- Canal de Chunking
- Cómo: Habilitar Streaming
- Archivos adjuntos grandes sobre WCF
- Codificador de mensaje personalizado
- Otra localización de InsufficientMemoryException
- No se necesita canal de dúplex no dúplex
- Streaming de gran contenido con WCF y ejecución diferida
También estoy interesado en cualquier tipo de compresión que pueda hacerse con estos datos, pero parece que probablemente sería mejor hacerlo en el nivel de transporte una vez que pueda hacer la transición a .NET 4.0 para que el cliente admita automáticamente el gzip Encabezados si entiendo esto correctamente.
Actualización (2010-06-29):
Un poco de historia sobre cómo obtuve la conclusión de que el mensaje del búfer era demasiado grande estaba causando mi problema. Originalmente vi la CommunicationException continuación durante las pruebas.
La conexión subyacente se cerró: la conexión se cerró inesperadamente.
Finalmente, después de ejecutar esto y hacer más registros, encontré la excepción InsufficientMemoryException subyacente que estaba causando el problema con el mensaje especificado.
Error al asignar un búfer de memoria administrada de 268435456 bytes. La cantidad de memoria disponible puede ser baja.
Que se originó a partir del siguiente método.
System.ServiceModel.Diagnostics.Utility.AllocateByteArray (tamaño Int32)
Así que en otras palabras, el error vino de la asignación de la matriz. Cuando escribo los mismos datos serializados en el disco, ocupa alrededor de 146 MB y si los corto a la mitad, entonces dejo de recibir el error. Sin embargo, no he investigado mucho más el umbral específico que rompe mi búfer y si es específico para mi sistema o no.
Actualización (2010-12-06):
Supongo que en este punto estoy buscando alguna aclaración para lo siguiente. Según tengo entendido, de forma predeterminada con WCF wsHttp con seguridad de mensajes, todo el mensaje (generalmente todo el conjunto de datos que estoy devolviendo) debe almacenarse en el búfer antes de que la respuesta se envíe de nuevo al cliente y, por lo tanto, me cause problemas.
Soluciones posibles:
- Restricción del tamaño de los datos: mediante el uso de algún tipo de compresión, codificación o limitación de los datos reales devueltos mediante el uso de algún tipo de método de paginación para evitar consumir la capacidad máxima del búfer saliente.
- Transmisión en tiempo real: permite que se envíen grandes cantidades de datos a través de WCF de forma continua. Sin embargo, esto no es compatible con wsHttp o MessageSecurity, ya que estas técnicas requieren el almacenamiento en búfer de todos los datos.
- Canal de Chunking: permite que los datos se dividan en mensajes separados, pero en este momento no estoy seguro de las restricciones que esto tiene en el diseño del contrato de servicio y si aún puedo usar wsHttp con enlace de mensajes.
La limitación de los datos que puedo devolver solo funciona hasta cierto punto y, al igual que con la opción Transmisión, estas opciones requieren la codificación de gran parte del trabajo de nivel inferior fuera de las llamadas al servicio WCF. Así que supongo que lo que necesito saber es si alguna posible implementación del canal de fragmentación puede evitar los grandes problemas de mensajes al permitir que un solo conjunto de datos se divida en mensajes separados en el servidor y luego se unan en el cliente. de tal manera que no tengo que cambiar la interfaz / forma de los contratos de servicio existentes y de una manera que el proceso está bastante oculto de la parte del cliente y el servidor de cada implementación del servicio mientras uso la seguridad de mensajes y wsHttp. Si el canal de fragmentación va a requerir que vuelva a escribir mis contratos de servicio para exponer las transmisiones, entonces no veo que esto sea realmente diferente a la solución Streaming. Si alguien puede simplemente responder estas preguntas por mí, les concederé la recompensa y la marcaré como la respuesta.
Dado que nadie lo ha publicado, quizás el uso de WebSockets o las técnicas de sondeo largo puedan resolver este problema. Solo he analizado estas soluciones brevemente y no he desarrollado una solución a su alrededor, pero quería proponer estos conceptos para el registro y ampliaré mi respuesta más adelante, si el tiempo lo permite.
La idea subyacente sería lograr algo similar a cómo funciona la idea de cómo funciona el ejemplo de ChunkingChannel, pero sin requerir un canal dúplex completo que normalmente rompe el puerto 80. El modelo de solicitud / respuesta basado en la web es conveniente para evitar tener que hacer firewall y otros. Configuraciones relacionadas para clientes.
Otro material relacionado:
Actualización: después de investigar más sobre esto, parece que al usar WebSockets, conocido como NetHttpBinding, no resolvería la solicitud original, que es usar wsHttp en WCF con seguridad de mensajes. Sin embargo, voy a mantener mi respuesta aquí como información para otros que puedan estar buscando una alternativa.
Si aún desea usar Message Security, le recomendaría que use MTOM para optimizar el ancho de banda de la red que se necesita para transferir los mensajes, y también el canal de fragmentación para usar búferes de memoria más pequeños cuando se aplica seguridad. De lo contrario, WCF intentará cargar todo el mensaje en la memoria para aplicar la seguridad y, por lo tanto, obtendrá la excepción de memoria insuficiente.
Solía implementar un poco de pasar un texto grande a / desde wcf. mi trigono es convertirlo a transmisión y usar GZipStream para comprimirlo y luego enviarlo como byte [], por suerte nunca supera los 10 MB.
En tu caso te recomiendo hacer fragmentación. Convierta el objeto serializado en byte [] y luego fusione y descomprima
psudo
int transferSize = 5000000; // 5MB
byte[] compressed = ...;
var mem = new System.IO.MemoryStream(compressed);
for(int i = 0; i < compressed .length; i+= transferSize )
{
byte[] buffer = new byte[transferSize];
mem.Read(buffer, i, compressed);
mem.Flush();
sendFragmentToWCF(buffer);
}
edición 08 dic 2010
De acuerdo con mi entendimiento, la situación es que el cliente es descargar un gran objeto serializado a través de WCF. No probé particularmente esta solución, pero supongo que debería funcionar. La clave es guardar el objeto serializado en un archivo y usar Response para transmitir ese archivo.
[WebMethod]
public void GetSerializedObject()
{
string path = @"X:/temp.tmp";
var serializer = new System.Runtime.Serialization.NetDataContractSerializer();
var file = new System.IO.FileStream(path, System.IO.FileMode.CreateNew);
try
{
serializer.Serialize(file, ...);
this.Context.Response.TransmitFile(path);
this.Context.Response.Flush();
}
finally
{
file.Flush();
file.Close();
file.Dispose();
System.IO.File.Delete(path);
}
}
WCF debería hacer la transmisión de archivos automáticamente y no debe preocuparse por el tamaño del objeto serializado ya que usamos la transmisión de archivos. No te olvides del límite de respuesta de configuración.
protobuf-net generalmente tiene un importante ahorro de espacio (como en: orden de magnitud) en la mayoría de los datos, y se puede adjuntar a WCF. Lamentablemente en este momento no admite gráficos completos, solo árboles. Sin embargo, tengo planes allí que simplemente no he tenido tiempo de implementar. No puedo prometer nada, pero podría intentar golpear ese trabajo un poco antes.
De otra manera; puede haber formas de modificar su código existente para trabajar con un árbol en lugar de un gráfico.