Загрузка больших файлов через веб-сервис на Java

StackOverflow https://stackoverflow.com/questions/1663162

  •  12-09-2019
  •  | 
  •  

Вопрос

У меня есть веб-приложение, которое является хранилищем файлов.Это веб-приложение предоставляет веб-службы, которые позволяют клиентам выполнять поиск в репозитории и загружать любые вложения через 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)
Это было полезно?

Решение

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

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

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

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

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

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

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

Другие советы

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top