복붙노트

[SPRING] 비 스트리밍 응용 프로그램 / json을 사용한 Spring WebFlux Flux 비헤이비어

SPRING

비 스트리밍 응용 프로그램 / json을 사용한 Spring WebFlux Flux 비헤이비어

Spring Webflux를 사용하여 평가하고 있지만 application / stream + json이 아닌 application / json을 기대하는 클라이언트를 지원해야합니다. 나는 Spring WebFlux가 application / json이 필요한 클라이언트의 경우 Flux를 직렬화하는 방법을 잘 모르고있다.

플럭스가 application / stream + json이 아닌 application / json으로 직렬화되는 경우 차단 작업입니까?

아래에서는 샘플 컨트롤러를 조합하여 내가 본 것을 보여줍니다. 스트림이 무한하고 application / json을 생성하면 브라우저에 아무것도 반환되지 않습니다. 아마도 스트림이 종료되기를 기다리고 있기 때문에 합당한 것처럼 보입니다. 스트림이 무한하고 application / stream + json을 생성하면 JSON 객체가 예상대로 브라우저에서 계속 표시됩니다. Flux가 유한 요소 일 때 100 개의 요소가 있고 type이 application / json 인 경우 예상대로 한 번에 렌더링됩니다. 문제는, Flux가 직렬화되기 전에 종료 될 때까지 기다려야 하는가하는 것입니다. 그러면 차단 작업이 발생합니다. 정상적인 application / json을 반환 할 때 Flux를 사용하여 성능 및 확장성에 미치는 영향은 무엇입니까?

@RestController
public class ReactiveController {

    /* Note: In the browser this sits forever and never renders */
    @GetMapping(path = "/nonStreaming", produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<Person> getPeopleNonStreaming() {
        return Flux.interval(Duration.ofMillis(100))
                .map(tick -> new Person("Dude", "Dude", tick));
    }

    /* Note: This renders in the browser in chunks forever */
    @GetMapping(path = "/streaming", produces = MediaType.APPLICATION_STREAM_JSON_VALUE)
    public Flux<Person> getPeopleStreaming() {
        return Flux.interval(Duration.ofMillis(100))
                .map(tick -> new Person("Dude", "Dude", tick));
    }

    /* Note: This returns, but I can't tell if it is done in a non blocking manner. It
     * appears to gather everything before serializing. */
    @GetMapping(path = "/finiteFlux", produces = MediaType.APPLICATION_JSON_VALUE)
    public Flux<Person> finiteFlux() {
        return Flux.range(0, 100)
                .map(tick -> new Person("Dude", "Dude", tick));
    }
}

최신 정보:

아래에 추가 로깅 정보를 추가했습니다.

스트리밍은 두 개의 다른 스레드를 사용하는 것처럼 보입니다.

2019-02-13 16:53:07.363 DEBUG 3416 --- [ctor-http-nio-2] o.s.w.s.adapter.HttpWebHandlerAdapter    : [dac80fd4] HTTP GET "/streaming"
2019-02-13 16:53:07.384 DEBUG 3416 --- [ctor-http-nio-2] s.w.r.r.m.a.RequestMappingHandlerMapping : [dac80fd4] Mapped to public reactor.core.publisher.Flux<io.jkratz.reactivedemo.Person> io.jkratz.reactivedemo.ReactiveController.getPeopleStreaming()
2019-02-13 16:53:07.398 DEBUG 3416 --- [ctor-http-nio-2] o.s.w.r.r.m.a.ResponseBodyResultHandler  : Using 'application/stream+json;q=0.8' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [application/stream+json]
2019-02-13 16:53:07.398 DEBUG 3416 --- [ctor-http-nio-2] o.s.w.r.r.m.a.ResponseBodyResultHandler  : [dac80fd4] 0..N [io.jkratz.reactivedemo.Person]
2019-02-13 16:53:07.532 DEBUG 3416 --- [     parallel-1] o.s.http.codec.json.Jackson2JsonEncoder  : [dac80fd4] Encoding [io.jkratz.reactivedemo.Person@6b3e843d]
2019-02-13 16:53:07.566 DEBUG 3416 --- [ctor-http-nio-2] r.n.channel.ChannelOperationsHandler     : [id: 0xdac80fd4, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52398] Writing object DefaultHttpResponse(decodeResult: success, version: HTTP/1.1)
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/stream+json;q=0.8;charset=UTF-8
2019-02-13 16:53:07.591 DEBUG 3416 --- [ctor-http-nio-2] r.n.channel.ChannelOperationsHandler     : [id: 0xdac80fd4, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52398] Writing object 
2019-02-13 16:53:07.629 DEBUG 3416 --- [     parallel-1] o.s.http.codec.json.Jackson2JsonEncoder  : [dac80fd4] Encoding [io.jkratz.reactivedemo.Person@217d62db]
2019-02-13 16:53:07.630 DEBUG 3416 --- [ctor-http-nio-2] r.n.channel.ChannelOperationsHandler     : [id: 0xdac80fd4, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52398] Writing object 
2019-02-13 16:53:07.732 DEBUG 3416 --- [     parallel-1] o.s.http.codec.json.Jackson2JsonEncoder  : [dac80fd4] Encoding [io.jkratz.reactivedemo.Person@741c0c88]
2019-02-13 16:53:07.732 DEBUG 3416 --- [ctor-http-nio-2] r.n.channel.ChannelOperationsHandler     : [id: 0xdac80fd4, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52398] Writing object 
2019-02-13 16:53:07.832 DEBUG 3416 --- [     parallel-1] o.s.http.codec.json.Jackson2JsonEncoder  : [dac80fd4] Encoding [io.jkratz.reactivedemo.Person@7b8532e5]

JSON을 사용하면 유한 한 스레드가 하나의 스레드 만 사용합니다.

2019-02-13 16:55:34.431 DEBUG 3416 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [5b048f46] HTTP GET "/finiteFlux"
2019-02-13 16:55:34.432 DEBUG 3416 --- [ctor-http-nio-3] s.w.r.r.m.a.RequestMappingHandlerMapping : [5b048f46] Mapped to public reactor.core.publisher.Flux<io.jkratz.reactivedemo.Person> io.jkratz.reactivedemo.ReactiveController.finiteFlux()
2019-02-13 16:55:34.434 DEBUG 3416 --- [ctor-http-nio-3] o.s.w.r.r.m.a.ResponseBodyResultHandler  : Using 'application/json;q=0.8' given [text/html, application/xhtml+xml, image/webp, image/apng, application/xml;q=0.9, */*;q=0.8] and supported [application/json]
2019-02-13 16:55:34.435 DEBUG 3416 --- [ctor-http-nio-3] o.s.w.r.r.m.a.ResponseBodyResultHandler  : [5b048f46] 0..N [io.jkratz.reactivedemo.Person]
2019-02-13 16:55:34.439 DEBUG 3416 --- [ctor-http-nio-3] o.s.http.codec.json.Jackson2JsonEncoder  : [5b048f46] Encoding [[io.jkratz.reactivedemo.Person@425c8296, io.jkratz.reactivedemo.Person@22ae73df, io.jkratz.reactived (truncated)...]
2019-02-13 16:55:34.448 DEBUG 3416 --- [ctor-http-nio-3] r.n.channel.ChannelOperationsHandler     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] Writing object DefaultHttpResponse(decodeResult: success, version: HTTP/1.1)
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json;q=0.8;charset=UTF-8
2019-02-13 16:55:34.448 DEBUG 3416 --- [ctor-http-nio-3] r.n.channel.ChannelOperationsHandler     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] Writing object 
2019-02-13 16:55:34.450 DEBUG 3416 --- [ctor-http-nio-3] o.s.w.s.adapter.HttpWebHandlerAdapter    : [5b048f46] Completed 200 OK
2019-02-13 16:55:34.450 DEBUG 3416 --- [ctor-http-nio-3] r.n.http.server.HttpServerOperations     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] Last HTTP response frame
2019-02-13 16:55:34.450 DEBUG 3416 --- [ctor-http-nio-3] r.n.channel.ChannelOperationsHandler     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] Writing object EmptyLastHttpContent
2019-02-13 16:55:34.450 DEBUG 3416 --- [ctor-http-nio-3] r.n.http.server.HttpServerOperations     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] Decreasing pending responses, now 0
2019-02-13 16:55:34.451 DEBUG 3416 --- [ctor-http-nio-3] r.n.channel.ChannelOperationsHandler     : [id: 0x5b048f46, L:/0:0:0:0:0:0:0:1:8080 - R:/0:0:0:0:0:0:0:1:52991] No ChannelOperation attached. Dropping: EmptyLastHttpContent

해결법

  1. ==============================

    1.스트리밍 MIME 유형 (application / stream + json)의 경우, Spring WebFlux에서 기본적으로 구성된 JSON 코덱은 JSON으로 직렬화되고 Flux 입력의 각 요소를 네트워크에서 비 웁니다. 이 동작은 스트림이 무한하거나 사용 가능한 즉시 클라이언트에 정보를 푸시하려는 경우에 유용합니다. serializer를 호출하고 여러 번 플러시 할 때 리소스가 필요하므로 성능 비용이 발생합니다.

    스트리밍 MIME 유형 (application / stream + json)의 경우, Spring WebFlux에서 기본적으로 구성된 JSON 코덱은 JSON으로 직렬화되고 Flux 입력의 각 요소를 네트워크에서 비 웁니다. 이 동작은 스트림이 무한하거나 사용 가능한 즉시 클라이언트에 정보를 푸시하려는 경우에 유용합니다. serializer를 호출하고 여러 번 플러시 할 때 리소스가 필요하므로 성능 비용이 발생합니다.

    비 스트리밍 유형 (application / json)의 경우 Spring WebFlux에서 기본적으로 구성된 JSON 코덱은 JSON으로 직렬화되고 한 번에 네트워크로 플러시됩니다. Flux 를 메모리에 버퍼링하고 한 번에 직렬화합니다. 결과 Flux 가 네트워크에 반응 적으로 쓰여지기 때문에 작업이 블로킹 중임을 의미하지는 않습니다. 아무것도 여기에 차단되지 않습니다.

    이것은 단순히 "데이터 스트리밍과 더 많은 리소스 사용"대 "버퍼링과 리소스 사용을보다 효율적으로"단점입니다.

    스트리밍의 경우 작업 항목이 서로 다른 간격으로 사용 가능하기 때문에 상황이 다른 작업자 스레드에 의해 처리 될 가능성이 큽니다. 간단한 JSON 응답의 경우 하나 이상의 스레드로 처리 될 수 있습니다. 원격 클라이언트가 느리거나 그렇지 않은 경우 페이로드 크기에 따라 다릅니다.

  2. from https://stackoverflow.com/questions/54680001/spring-webflux-flux-behavior-with-non-streaming-application-json by cc-by-sa and MIT license