【问题标题】:jersey - StreamingOutput as Response entityjersey - StreamingOutput 作为响应实体
【发布时间】:2021-10-22 07:04:00
【问题描述】:

我已经在我的 Jersey Resource 类中实现了流输出。

@GET
@Path("xxxxx")
@Produces(BulkConstants.TEXT_XML_MEDIA_TYPE})   
public Response getFile() {

    FeedReturnStreamingOutput sout = new FeedReturnStreamingOutput();
    response = Response.ok(sout).build();
    return response;
}

class FeedReturnStreamingOutput implements StreamingOutput {

    public FeedReturnStreamingOutput()

    @Override
    public void write(OutputStream outputStream)  {
        //write into Output Stream
    }
}

问题是即使在调用 FeedReturnStreamingOutput 之前从资源发回响应,泽西客户端仍会等待直到 FeedReturnStreamingOutput 执行完成。

客户代码:

Client client = Client.create();

ClientResponse response = webResource
    //headers
    .get(ClientResponse.class);

//The codes underneath executes after FeedReturnStreamingOutput is executed which undermines the necessity of streaming

OutputStream os = new FileOutputStream("c:\\test\\feedoutput5.txt");
System.out.println(new Date() + " : Reached point A");

if (response.getStatus() == 200) {
    System.out.println(new Date() + " : Reached point B");
    InputStream io = response.getEntityInputStream();

    byte[] buff = new byte[1024000];
    int count = 0;

    while ((count = io.read(buff, 0, buff.length)) != -1) {
        os.write(buff, 0, count);
    }

    os.close();
    io.close();

} else {
    System.out.println("Response code :" + response.getStatus());
}

System.out.println("Time taken -->> "+(System.currentTimeMillis()-startTime)+" ms");

【问题讨论】:

    标签: java jersey jersey-client


    【解决方案1】:

    问题在于 Jersey 使用缓冲 OutputStream 来缓冲实体以确定 Content-Length 标头。缓冲区的大小默认为 8 kb。如果需要,您可以禁用缓冲,或者只是使用属性更改缓冲区的大小

    ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER

    一个整数值,定义用于缓冲服务器端响应实体的缓冲区大小,以确定其大小并设置 HTTP“Content-Length”标头的值。

    如果实体大小超过配置的缓冲区大小,则缓冲将被取消,实体大小将无法确定。小于或等于零的值完全禁用实体的缓冲。

    此属性可用于在服务器端覆盖出站消息缓冲区大小值 - 默认值或使用“jersey.config.contentLength.buffer”全局属性设置的全局自定义值。

    默认值为 8192。

    这是一个例子

    @Path("streaming")
    public class StreamingResource {
    
        @GET
        @Produces("application/octet-stream")
        public Response getStream() {
            return Response.ok(new FeedReturnStreamingOutput()).build();
        }
    
        public static class FeedReturnStreamingOutput implements StreamingOutput {
    
            @Override
            public void write(OutputStream output)
                    throws IOException, WebApplicationException {
                try {
                    for (int i = 0; i < 10; i++) {
                        output.write(String.format("Hello %d\n", i).getBytes());
                        output.flush();
                        TimeUnit.MILLISECONDS.sleep(500);
                    }
                } catch (InterruptedException e) {  throw new RuntimeException(e); }
            }
        }
    }
    

    这是没有设置属性的结果

    这是将属性值设置为0后的结果

    public class AppConfig extends ResourceConfig {
        public AppConfig() {
            ...
            property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0);
        }
    }
    

    【讨论】:

    • 这是一个很好的解决方案。我刚刚发现的一件事-仅当您在输入末尾有换行符时才有效:)
    • @Paul 你的意思是不设置OUTBOUND_CONTENT_LENGTH_BUFFER = 0 我们实际上不能流任何东西?即除非我们将该缓冲区设置为 0,否则它将全部缓冲到内存中,然后将其刷新? (如果响应很大,可能会导致内存超出范围)
    • @Paul Samsotha 有什么方法可以按照标准的 Java/Jakarta EE 方式进行吗?我使用了 jakarta-ee-api 依赖,找不到 ResourceConfig 类,我猜是因为它属于 Jersey?
    • “属性”方法从何而来?
    • @DragonMoon 资源配置
    【解决方案2】:

    尝试从方法FeedReturnStreamingOutput.write(...) 调用outputStream.flush() 每X 个字节写入输出流或类似的东西。

    我猜连接的缓冲区没有填满你返回的数据。因此,在 Jersey 调用 outputStream.close() 之前,该服务不会返回任何内容。

    在我的例子中,我有一个流数据的服务,我正在做的和你完全一样:返回Response.ok(&lt;instance of StreamingOutput&gt;).build();

    我的服务从数据库返回数据,并在将每一行写入输出流后调用outputStream.flush()

    我知道服务流数据,因为我可以看到客户端在服务完成发送整个结果之前开始接收数据。

    【讨论】:

    • 我每次写入输出流时都添加了 outputstream.flush 。没什么区别
    • @user1016496 你怎么知道服务没有流式传输?会不会是服务实际上是在流式传输数据,但客户端并没有写入它在流式模式下获得的内容?
    【解决方案3】:

    您的响应太小并且永远不会得到chunked,因此服务器会立即刷新整个请求。或者您有一个服务器端问题,即您的 jax-rs 库在刷新之前等待获得完整的流。

    不过,这看起来更像是客户端问题。而且您似乎使用的是旧版本的 jersey-client。

    另外,.get(ClientResponse.class) 看起来很可疑。

    尝试使用现在的 JAX-RS 标准 (at least in the client):

    import javax.ws.rs.client.Client;
    import javax.ws.rs.client.ClientBuilder;
    import javax.ws.rs.client.WebTarget;
    import javax.ws.rs.core.Response;
    
    Client client = ClientBuilder.newBuilder().build();
    WebTarget target = client.target("http://localhost:8080/");
    Response response = target.path("path/to/resource").request().get();
    

    在类路径中有 jersey client 2.17 时:

    <dependency>
        <groupId>org.glassfish.jersey.core</groupId>
        <artifactId>jersey-client</artifactId>
        <version>2.17</version>
    </dependency>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-29
      • 1970-01-01
      • 2012-11-30
      • 2017-04-20
      相关资源
      最近更新 更多