web-dev-qa-db-ja.com

jersey-応答エンティティとしてのStreamingOutput

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が呼び出される前にリソースから応答が返されるにもかかわらず、Jerseyクライアントが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");
21
PrabhaT

問題は、JerseyがContent-Lengthヘッダーを決定するためにエンティティをバッファリングするために使用するバッファリングOutputStreamです。バッファのサイズはデフォルトで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); }
        }
    }
}

プロパティを設定しない場合の結果は次のとおりです

enter image description here

そして、プロパティ値を0に設定した後の結果は次のとおりです。

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

enter image description here

39
Paul Samsotha

応答が小さすぎて chunked にならないため、サーバーはリクエスト全体を一度にフラッシュします。または、フラッシュする前にjax-rsライブラリが完全なストリームを待機している場合、サーバー側の問題があります。

ただし、これはクライアントの問題のように見えます。また、古いバージョンのjersey-clientを使用しているようです。

また、.get(ClientResponse.class)は怪しげに見えます。

現在のJAX-RS標準を使用してみてください( 少なくともクライアントでは ):

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();

クラスパスにジャージクライアント2.17がある場合:

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

メソッドoutputStream.flush()からFeedReturnStreamingOutput.write(...)を呼び出してみてください。出力ストリームに書き込まれたXバイトごと、またはそのようなものです。

接続のバッファが、返されるデータで満たされていないと思います。そのため、JerseyがoutputStream.close()を呼び出すまで、サービスは何も返しません。

私の場合、データをストリーミングするサービスがあり、Response.ok(<instance of StreamingOutput>).build();を返すことであなたとまったく同じようにしています。

私のサービスはデータベースからデータを返し、各行を出力ストリームに書き込んだ後にoutputStream.flush()を呼び出します。

サービスが結果全体の送信を完了する前にクライアントがデータの受信を開始するのを見ることができるため、サービスがデータをストリーミングすることを知っています。

1
Montecarlo

StreamingOutputのサブクラスを使用する場合、ジャージーは応答をストリーミングしないことに気付きました。

// works fine
Response.ok(new StreamingOutput() { ... }).build();

// don't work
public interface MyStreamingOutput extends StreamingOutput { }
Response.ok(new MyStreamingOutput() { ... }).build();

それはジャージーのバグですか?

0
Mike Dias