MTOM无法在块中编写数据

时间:2015-12-18 10:26:29

标签: java file jboss nio mtom

我们有一个用例,我们必须通过http将大数据文件从环境A传输到环境B.我们想要实现的是发送方以块的形式发送数据,接收方开始以块的形式将其写入文件中。因此我们决定使用MTOM。

网络服务代码:

@MTOM(enabled = true, threshold=1024)
@WebService(portName = "fileUploadPort", endpointInterface = "com.cloud.receiver.FileUploadService", serviceName = "FileUploadService")
@BindingType(value = SOAPBinding.SOAP12HTTP_MTOM_BINDING)
public class FileUploadServiceImpl implements FileUploadService {

@Override
public void uploadFile(FileUploader Dfile) {

    DataHandler handler = Dfile.getFile();
    try {
        InputStream is = handler.getInputStream();

        OutputStream os = new FileOutputStream(new File(absolutePath));
        byte[] b = new byte[10000000];
        int bytesRead = 0;
        while ((bytesRead = is.read(b)) != -1) {
            os.write(b, 0, bytesRead);
        }
        os.flush();
        os.close();
        is.close();

    } catch (IOException e) {
        e.printStackTrace();
    }

}
}

客户代码:

public static void main(String args[]) throws Exception {

    URL url = new URL("http://localhost:8080/CloudReceiver/FileUploadService?wsdl");

    QName qname = new QName("http://receiver.cloud.com/", "FileUploadService");

    Service service = Service.create(url, qname);
    FileUploadService port = service.getPort(FileUploadService.class);
    // enable MTOM in client
    BindingProvider bp = (BindingProvider) port;
    SOAPBinding binding = (SOAPBinding) bp.getBinding();
    binding.setMTOMEnabled(true);

    FileUploader f = new FileUploader();
    DataSource source = new FileDataSource(new File("G:\\Data\\Sender\\temp.csv"));
    DataHandler dh = new DataHandler(source);
    Map<String, Object> ctxt = ((BindingProvider) port).getRequestContext();
    // Marking Chunk size at Client.
    ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 100);

    f.setFile(dh);
    port.uploadFile(f);
}

当我们传输小于100MB的数据时,每件事都可以正常工作。 超过(100MB +)的数据文件应用服务器(JBoss 8.2)在接收端结束时抛出异常。

java.io.IOException: UT000020: Connection terminated as request was larger than 104857600

我认为这个错误是因为standalone.xml中的属性

<http-listener name="default" socket-binding="http" max-post-size="104857600"/>

这意味着数据不会写入Chunks中的文件,而是保存在内存中,而不是在传输完成后写入文件。

我们如何在Chunks中实现将数据写入文件?我们不希望后期内存增加。文件大小可以达到1 TB的范围。

环境: JBoss 8.2 Wild Fly,Java 8

2 个答案:

答案 0 :(得分:0)

首先,提到的限制

<http-listener name="default" socket-binding="http" max-post-size="104857600"/>

表示服务器将接受的整个POST请求的最大大小。

无论是否使用MTOM,是否使用大块-您在此处仅发送一个POST请求即可发送文件。使用块不会改变这一点-所有带有文件数据的块都是同一POST请求的一部分。

因此,它可以按预期的方式工作:100M +个文件因限制而过大。

那我很确定这个结论

这意味着数据不会以块的形式写入文件,而是会保存在内存中,而不会在传输完成后写入文件。

完全是错误的。在内存中保留潜在的大卷并不安全,因此服务器通常不这样做,而是将这些卷放入一些临时文件中。

答案 1 :(得分:0)

一些解释

Sudeep :这意味着数据不会以块的形式写入文件,而是会保存在内存中,然后在传输完成后写入文件。

-> 那肯定不是真的。

以下限制:

<http-listener name="default" socket-binding="http" max-post-size="104857600"/>

..表示单个(最终成块的)POST请求的最大正文大小不能超过104857600字节。这与 HTTP content-length 标头和JBoss自身功能(如缓冲区)的配置有关。

Cf。 https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html The Content-Length entity-header field indicates the size of the entity-body, in decimal number of OCTETs, sent to the recipient or, in the case of the HEAD method, the size of the entity-body that would have been sent had the request been a GET.

我建议执行Wireshark分析以说服自己。


Sudeep :我们要实现的是,发送方以块的形式发送数据,接收方开始以块的形式将其写入文件。因此,我们 决定使用MTOM。

-> 我认为这是一个错误的决定。

首先

MTOM并非专门用于分块数据SOAP Message Transmission Optimization Mechanism (MTOM)主要定义了两个优化:

  • 一种抽象功能,用于通过选择性编码消息的某些部分来优化SOAP消息的传输和/或有线格式,同时仍将XML Infoset呈现给SOAP应用程序。
  • 一种优化的SOAP消息的MIME多部分/相关序列化,以独立于绑定的方式实现了抽象SOAP传输优化功能。此实现依赖于 XML二进制优化打包(XOP)格式。仅元素内容可以优化。
  • ...知道属性,非与base64兼容的字符数据以及不在base64Binary数据类型的规范表示中的数据无法通过XOP成功优化”。

总而言之,MTOM不能解决您的问题。

第二

HTTP协议并非旨在交换非常大的数据文件。正如您提到的1TB(!)数据,我建议采取完全不同的策略。

原因是与更合适的协议相比,HTTP协议具有大量开销(即使可以说,如果从A到Z最佳使用(即具有较大的MTU和块大小),开销比率将显着降低)。但是请求-响应模型也不适用于文件传输。如果没有有效的请求和恢复方法的并行化,您可能还会遇到非常大的文件传输问题。甚至没有考虑防火墙,(反向)代理服务器和其他工具,这些工具可能会认为这种流量特别异常或“越界”。

我的建议实际上是找到使用方式 (S)FTP 进行大型文件传输。该协议的目的是。如果您的用例匹配,最终考虑使用对等替代方案。

但是绝对不要在此用例中使用HTTP。除非您决定进行切片(因此要进行多个请求),否则请使用压缩方法(最终进行堆叠)并优化整个传输链。

祝你好运。