Загрузка больших файлов через веб-сервис на Java - PullRequest
3 голосов
/ 02 ноября 2009

У меня есть веб-приложение, которое является хранилищем файлов. Это веб-приложение предоставляет веб-службы, которые позволяют клиентам выполнять поиск в хранилище и загружать любые вложения через SOAP.

В настоящее время я пытался использовать Spring-WS 1.5.8 с MTOM для отправки вложения клиенту, но я продолжаю получать ошибки памяти. Я не верю, что эти ошибки связаны с моим экземпляром Tomcat 6, потому что мой сервер имеет 8 ГБ памяти, и я настроил Tomcat на использование 4 ГБ. Я получаю эти ошибки на файлах размером до 200 МБ.

Мне нужно использовать SOAP, хотя это, вероятно, не самый лучший подход. Я бы предпочел решение весной, но если это невозможно, я открыт для других идей. Я читал, что можно использовать AxiomSoapMessageFactory для потоковой передачи файлов на сервер для загрузки, но не наоборот. Это правда? Я использую Java 6.

Вот ошибка, которую я постоянно получаю в Spring WS Framework:

java.lang.OutOfMemoryError: Java heap space
    com.sun.xml.internal.messaging.saaj.util.ByteOutputStream.ensureCapacity(Unknown Source)
    com.sun.xml.internal.messaging.saaj.util.ByteOutputStream.write(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.find(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.readBody(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.getNextPart(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.parse(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.BMMimeMultipart.parse(Unknown Source)
    com.sun.xml.internal.messaging.saaj.packaging.mime.internet.MimeMultipart.getCount(Unknown Source)
    com.sun.xml.internal.messaging.saaj.soap.MessageImpl.initializeAllAttachments(Unknown Source)
    com.sun.xml.internal.messaging.saaj.soap.MessageImpl.getAttachments(Unknown Source)
    org.springframework.ws.soap.saaj.Saaj13Implementation.getAttachment(Saaj13Implementation.java:305)
    org.springframework.ws.soap.saaj.SaajSoapMessage.getAttachment(SaajSoapMessage.java:226)
    org.springframework.ws.support.MarshallingUtils$MimeMessageContainer.getAttachment(MarshallingUtils.java:109)
    org.springframework.oxm.jaxb.Jaxb2Marshaller$Jaxb2AttachmentUnmarshaller.getAttachmentAsDataHandler(Jaxb2Marshaller.java:532)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.MTOMDecorator.startElement(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(Unknown Source)
    com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(Unknown Source)
    com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal(Unknown Source)
    javax.xml.bind.helpers.AbstractUnmarshallerImpl.unmarshal(Unknown Source)
    org.springframework.oxm.jaxb.Jaxb2Marshaller.unmarshal(Jaxb2Marshaller.java:421)
    org.springframework.ws.support.MarshallingUtils.unmarshal(MarshallingUtils.java:62)
    org.springframework.ws.client.core.WebServiceTemplate$3.extractData(WebServiceTemplate.java:374)
    org.springframework.ws.client.core.WebServiceTemplate.doSendAndReceive(WebServiceTemplate.java:560)

Ответы [ 3 ]

3 голосов
/ 03 ноября 2009

Это может быть как-то связано с тем, что ваше пространство в раю слишком мало. Пространство eden - это часть кучи, где новые объекты размещаются и остаются до тех пор, пока они не пережили GC. Пространство рая не очень большое. (у меня нет значения по умолчанию, но при настройке по умолчанию с кучей 1 ГБ это всего 64 МБ)

Ваш файл, вероятно, будет загружен в пространство eden. Либо нет 200 МБ свободного места, либо байтовый массив выделен небольшому количеству и нуждается в увеличении. Единственный способ для массива расти в Java - это выделить новый и больший массив и сделать memcopy. Это приведет к росту от 100 МБ до 200 МБ, очевидно, что потребуется 300 МБ общего пространства кучи eden.

Вы можете попробовать установить -XX:NewSize=4196M, который выделит 4 ГБ пространства кучи eden.

Я должен сказать, что я не знаю, что Tomcat работает в каком-либо серверном режиме, который использует другую стратегию GC / heap.

Вы можете использовать visualgc из jvmstat 3.0 (не дистрибутив в комплекте с Java 5 и 6) для мониторинга кучи и определения того, какое пространство кучи заполнено.

Вы также можете проверить: Настройка сборки мусора с помощью виртуальной машины Java [tm] 5.0 *

Если вы решите эту проблему, вы все равно столкнетесь с низкой производительностью и не масштабируемым решением. Вы, вероятно, будете лучше с какой-то прямой потоковой передачей. Это не должно быть трудно реализовать простой сервлет для этой цели.

2 голосов
/ 02 ноября 2009

SOAP / XML в Java всегда требует много ресурсов и требует много памяти. В этом конкретном случае он пытается выделить (слишком большой) byte [] в памяти вместо прямой записи потока в другой тип OutputStream (что угодно, кроме ByteArrayOutputStream).

Рассматривали ли вы просто забыть об интерфейсе SOAP и вернуться к основам, используя java.net.URLConnection, и продолжить на этом? Таким образом, вы можете записать InputStream непосредственно на диск, используя FileOutputStream, который более эффективен, чем хранение всего этого в памяти.

0 голосов
/ 02 ноября 2009

Похоже, вы обрабатываете весь файл в памяти, а не читаете его при отправке клиенту.

Можете ли вы вместо этого перевести это на веб-сервер, если создадите URL-адрес, который преобразуется в фактический файл для отправки, и оставьте его для него?

...