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");
問題は、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); }
}
}
}
プロパティを設定しない場合の結果は次のとおりです
そして、プロパティ値を0
に設定した後の結果は次のとおりです。
public class AppConfig extends ResourceConfig {
public AppConfig() {
...
property(ServerProperties.OUTBOUND_CONTENT_LENGTH_BUFFER, 0);
}
}
応答が小さすぎて 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>
メソッドoutputStream.flush()
からFeedReturnStreamingOutput.write(...)
を呼び出してみてください。出力ストリームに書き込まれたXバイトごと、またはそのようなものです。
接続のバッファが、返されるデータで満たされていないと思います。そのため、JerseyがoutputStream.close()
を呼び出すまで、サービスは何も返しません。
私の場合、データをストリーミングするサービスがあり、Response.ok(<instance of StreamingOutput>).build();
を返すことであなたとまったく同じようにしています。
私のサービスはデータベースからデータを返し、各行を出力ストリームに書き込んだ後にoutputStream.flush()
を呼び出します。
サービスが結果全体の送信を完了する前にクライアントがデータの受信を開始するのを見ることができるため、サービスがデータをストリーミングすることを知っています。
StreamingOutputのサブクラスを使用する場合、ジャージーは応答をストリーミングしないことに気付きました。
// works fine
Response.ok(new StreamingOutput() { ... }).build();
// don't work
public interface MyStreamingOutput extends StreamingOutput { }
Response.ok(new MyStreamingOutput() { ... }).build();
それはジャージーのバグですか?