차단 응용프로그램 설계에서 Spring Webflux의 WebClient를 사용하면 RestTemplate보다 리소스를 더 많이 사용합니까?
저는 요청당 스레드의 전통적인 패턴을 가진 몇 가지 스프링 부트 애플리케이션을 작업하고 있습니다.Spring-boot-webflux를 사용하여 응용프로그램 간의 RESTful 통합을 수행하기 위해 WebClient를 획득하고 있습니다.따라서 우리의 애플리케이션 설계는 응답을 받은 후 바로 게시자를 차단해야 합니다.
최근에는 블록 애플리케이션 설계에서 반응형 모듈을 사용하여 리소스를 불필요하게 지출하는 것이 아닌지에 대해 논의하고 있습니다.제가 알기로는 WebClient는 이벤트 루프에서 반응 작업을 수행할 작업자 스레드를 할당하여 이벤트 루프를 활용합니다. 웹 를 사용여트하를과 함께 사용하는 것..block()
http-request를 모드로 합니다.다른 RestTemplate와 비교하여 WebClient는 이벤트 루프를 사용하여 추가 리소스를 사용할 것으로 보입니다.
이러한 방식으로 스프링-웹 플럭스를 부분적으로 도입하면 단일 스레드와 동시에 성능에 긍정적인 기여를 하지 않으면서 추가적인 사용 리소스가 발생한다는 것이 맞습니까?우리는 현재 스택을 완전히 사후 대응적으로 업그레이드하지 않을 것으로 예상하기 때문에 점진적으로 업그레이드해야 한다는 주장은 적용되지 않습니다.
이 프레젠테이션에서 Rossen Stoyanchev
Spring
팀은 이러한 점들 중 몇 가지를 설명합니다.
WebClient
스레드를 합니다 - 총 제된수코사개 (용어당스 2드)12 threads
로컬 컴퓨터에서 - 응용프로그램의 모든 요청 및 응답을 처리합니다.그래서 만약 당신의 지원서가 수신된다면,100 requests
외부 한 합니다.WebClient
에서는 이러한 를 러한스사 이것처것입 다할니로 합니다.non-blocking
/asynchronous
예의 범절
물론, 당이언것처, 이신전하면럼, 를당화급한신,block
의 원래 + 총 래개스레드차로개의총, 원 100 스레드 + 12 스레드입니다.112 threads
요청을 처리할 수 있습니다.그러나 이 12개의 스레드는 요청이 많아질수록 크기가 커지지 않으며 I/O가 많이 발생하지 않기 때문에 이와 유사하지 않습니다.WebClient
요청을 실제로 수행하기 위해 스레드를 생성하거나 요청별 스레드 방식으로 해당 스레드를 계속 사용합니다.
언제쯤 실타래가 잡힐지 모르겠습니다.block
출을할때동동작게다니합일하와단▁▁call▁blocking▁the다▁it▁▁a▁when▁making동을 통해 차단 전화를 걸 때와 동일하게 동작합니다.RestTemplate
내가 보기에 전자의 실은 다음과 같아야 합니다.inactive
대기 중인NIO
complete, 되어야 .I/O
그래서 아마 거기에는 차이가 있을 것입니다.
사용하기 시작하면 흥미로워집니다.reactor
예를 들어, 서로 의존하는 요청을 처리하거나 많은 요청을 병렬로 처리할 수 있습니다.그리고나서WebClient
요청당 스레드를 사용하는 대신 동일한 12개 스레드를 사용하여 모든 동시 작업을 수행하므로 확실히 우위에 있습니다.
이 애플리케이션을 예로 들어 보겠습니다.
@SpringBootApplication
public class SO72300024 {
private static final Logger logger = LoggerFactory.getLogger(SO72300024.class);
public static void main(String[] args) {
SpringApplication.run(SO72300024.class, args);
}
@RestController
@RequestMapping("/blocking")
static class BlockingController {
@GetMapping("/{id}")
String blockingEndpoint(@PathVariable String id) throws Exception {
logger.info("Got request for {}", id);
Thread.sleep(1000);
return "This is the response for " + id;
}
@GetMapping("/{id}/nested")
String nestedBlockingEndpoint(@PathVariable String id) throws Exception {
logger.info("Got nested request for {}", id);
Thread.sleep(1000);
return "This is the nested response for " + id;
}
}
@Bean
ApplicationRunner run() {
return args -> {
Flux.just(callApi(), callApi(), callApi())
.flatMap(responseMono -> responseMono)
.collectList()
.block()
.stream()
.flatMap(Collection::stream)
.forEach(logger::info);
logger.info("Finished");
};
}
private Mono<List<String>> callApi() {
WebClient webClient = WebClient.create("http://localhost:8080");
logger.info("Starting");
return Flux.range(1, 10).flatMap(i ->
webClient
.get().uri("/blocking/{id}", i)
.retrieve()
.bodyToMono(String.class)
.doOnNext(resp -> logger.info("Received response {} - {}", I, resp))
.flatMap(resp -> webClient.get().uri("/blocking/{id}/nested", i)
.retrieve()
.bodyToMono(String.class)
.doOnNext(nestedResp -> logger.info("Received nested response {} - {}", I, nestedResp))))
.collectList();
}
}
이 앱을 실행하면 30개의 모든 요청이 (내 컴퓨터에서) 동일한 12개 스레드에 의해 즉시 병렬로 처리되는 것을 볼 수 있습니다. Neat!
만약 당신이 당신의 논리에서 그런 종류의 평행성으로부터 이익을 얻을 수 있다고 생각한다면, 그것은 아마도 줄 가치가 있을 것입니다.WebClient
주사 한 대
위의 할 때 실제로 자원 지출, 는 전체를 합니다.reactor/webflux
이것에 - 수화물 에도, 하고 디버그하는 더 .RestTemplate
리고그고.thread-per-request
모범이 되는
물론 다른 사람들이 언급했듯이 적절한 메트릭을 얻으려면 로드 테스트를 실행해야 합니다.
RestTemplate에 대한 공식 Spring 문서에 따르면 현재 유지 보수 모드이며 향후 버전에서는 지원되지 않을 것이라고 합니다.
이 클래스는 모드에 , 및 에 대한 만 앞으로됩니다.5.0 기준이유모수지있드으에며보, 변경항사요진다니앞됩행로으청만한소대한버그및에사는래스으로클▁as▁is▁to,5진▁and다▁requests▁for▁class니▁minor됩행▁in▁main▁going로▁mode▁changestenance▁accepted▁forward앞.다음을 사용하는 것을 고려해 보십시오.
org.springframework.web.reactive.client.WebClient
인 API를 동기,, 를 지원합니다.
시스템 리소스의 경우 사용 사례에 따라 다르므로 몇 가지 성능 테스트를 실행하는 것이 좋습니다. 하지만 차단 클라이언트를 사용하는 낮은 워크로드의 경우 연결당 전용 스레드를 소유하는 것이 더 나은 성능을 발휘할 수 있을 것으로 보입니다.부하가 증가함에 따라 NIO 클라이언트의 성능이 향상되는 경향이 있습니다.
업데이트 - 반응형 API 대 Http 클라이언트
반응형 API(Project Reactor)와 http 클라이언트의 차이점을 이해하는 것이 중요합니다.비록 ~일지라도WebClient
합니다. 반형를 API 용다음같 연응명 은산를 사시로으때않용다습과 같은 flatMap
또는delay
다른 스레드 풀에서 실행을 예약할 수 있습니다.우리가 그냥 사용하면.
webClient
.get()
.uri("<endpoint>")
.retrieve()
.bodyToMono(String.class)
.block()
클라이언트를 차단하는 것과 동일한 코드가 호출자 스레드에서 실행됩니다.
이 코드에 대한 디버그 로깅을 활성화하면 다음과 같이 됩니다.WebClient
은 " " " " " " " " " " " " " " " " 로 됩니다.reactor-http-nio-...
가장 큰 차이점은 내부적으로WebClient
에서는 NIO(Non-Blocking IO) 기반의 비동기 클라이언트를 사용합니다.이러한 클라이언트는 Reactor 패턴(이벤트 루프)을 사용하여 많은 수의 동시 연결을 처리할 수 있는 별도의 스레드 풀을 유지 관리합니다.
I/O 원자로의 목적은 I/O 이벤트에 대응하고 개별 I/O 세션에 이벤트 알림을 발송하는 것입니다.I/O 반응기 패턴의 주요 아이디어는 기존 차단 I/O 모델에 의해 부과되는 연결 모델당 하나의 스레드에서 벗어나는 것입니다.
기본적으로 Reactor Netty가 사용되지만 필요한 어댑터를 생성하는 경우 Jetty Directive Http Client, Apache Http Components(비동기화) 또는 AWS CRT(공통 런타임) Http Client도 고려할 수 있습니다(이미 존재하는지는 확실하지 않음).
일반적으로 비동기 I/O(NIO)는 부하가 높은 애플리케이션에서 리소스 효율성이 높기 때문에 업계 전반에서 비동기 I/O를 사용하는 추세를 볼 수 있습니다.
또한 리소스를 효율적으로 처리하려면 전체 흐름이 비동기식이어야 합니다.을 사용하여block()
NIO의 이점을 대부분 제거하는 연결당 스레드 접근 방식을 암시적으로 재도입하고 있습니다.에 동에사용을 사용합니다.WebClient
와 함께block()
완전 반응형 애플리케이션으로의 마이그레이션을 위한 첫 번째 단계로 간주할 수 있습니다.
좋은 질문입니다.
지난주에 우리는 resttemplate에서 webclient로 마이그레이션을 고려했습니다.이번 주에는 차단 웹 클라이언트와 나머지 템플릿 간의 성능 테스트를 시작했는데, 놀랍게도 응답 페이로드가 큰 시나리오에서 나머지 템플릿의 성능이 더 우수했습니다.나머지 템플릿이 응답하는 데 걸리는 시간이 절반 미만이고 리소스를 더 적게 사용하는 등 차이가 상당히 컸습니다.
저는 아직 성능 테스트를 진행하고 있으며, 지금은 더 넓은 범위의 사용자들에게 요청하여 테스트를 시작했습니다.
애플리케이션은 mvc이며 스프링 5.13.19 및 스프링 부트 2.6.7을 사용합니다.
성능 테스트를 위해 jmeter를 사용하고 있으며 상태를 위해 visual vm/jconsole을 확인합니다.
언급URL : https://stackoverflow.com/questions/72300024/does-the-use-of-spring-webfluxs-webclient-in-a-blocking-application-design-caus
'sourcecode' 카테고리의 다른 글
mongodb는 뚜렷한 기록을 얻습니다. (0) | 2023.07.02 |
---|---|
Gitstash 팝업 - 필요 병합, 인덱스를 새로 고칠 수 없습니다. (0) | 2023.07.02 |
스토어 fake api 배열에서 새 항목 이동 해제 (0) | 2023.07.02 |
행렬 또는 데이터 프레임의 모든 행에 함수 적용 (0) | 2023.07.02 |
유니온에서 현재 어떤 유형이 사용되고 있는지 어떻게 확인합니까? (0) | 2023.07.02 |