토비의 봄 TV 스프링 리액티브 프로그래밍 를 보고 정리한 글
RestTemplate -> sync
- AsyncRestTemplate
- WebClient(WebFlux)
@SpringBootApplication
@RestController
@Slf4j
@EnableAsync
public class WebfluxPrcApplication {
static final String URL1 = "http://localhost:8081/service?req={req}";
static final String URL2 = "http://localhost:8081/service2?req={req}";
@Autowired MyService myService;
WebClient client = WebClient.create(); // AsyncRestTemplate과 유사
@GetMapping("/rest")
public Mono<String> rest(int idx) {
return Mono.just("Hello"); // 이미 값이 있는걸 리턴하고 싶다 -> just
}
public static void main(String[] args) {
SpringApplication.run(WebfluxPrcApplication.class, args);
}
}
- "Hello"가
Mono
라는 타입 안에 담겨 리턴됨 Mono
: 컨테이너라고 생각하면됨List<String>
처럼 컨테이너가 제공하는 기능을 사용할 수 있음
@GetMapping("/rest")
public Mono<String> rest(int idx) {
Mono<ClientResponse> res = client.get().uri(URL1, idx).exchange(); // (1)
return Mono.just("Hello"); // (2)
}
(1)
- WebClient -> builder 스타일로 호출
- URL1에 해당하는 api를 호출하고 응답값을 가져오는 역할
- ClientResponse -> ResponseEntity 와 비슷
(2)
- 이렇게 하면 호출 되는가?
- Pub Sub 개념으로 subscribe 를 해야만 호출
- res.subscribe()를 해야
- Mono를 리턴하는 것만으로 호출되지 않음
@GetMapping("/rest")
public Mono<String> rest(int idx){
Mono<ClientResponse> res=client.get().uri(URL1,idx).exchange();
Mono<Mono<String>>body=res.map(clientResponse->clientResponse.bodyToMono(String.class)); // (1)
Mono<String> body2=res.flatMap(clientResponse->clientResponse.bodyToMono(String.class)); // (2)
return body2;
// (리액티브 스타일 코드는)선언만 하는 것으로는 동작하지 않는다.
}
(1)
- clientResponse 라는 타입을 받아서 String으로 리턴하는게 아니라 Mono 로 리턴 : 다시 Mono로 감쌈
(2)
- (1)에서 이중으로 감싸졌기 때문에
flat
해준다!! - body만 꺼내서 Mono 타입으로 리턴해줘야함
/* 간결하게 */
@GetMapping("/rest")
public Mono<String> rest(int idx) {
return client.get().uri(URL1, idx).exchange()
.flatMap(c -> c.bodyToMono(String.class));
}
/* +2번째 API를 호출 */
@GetMapping("/rest")
public Mono<String> rest(int idx) {
return client.get().uri(URL1, idx).exchange() // Mono<ClientResponse>
.flatMap(cr -> cr.bodyToMono(String.class)) // 위에서 ClientResponse를 받아서 Mono<String> 리턴
.flatMap((String res1) -> client.get().uri(URL2, res1).exchange()) // 위에 String을 받아서 2번째 api를 호출
.flatMap(c -> c.bodyToMono(String.class))
.map(res2 -> myService.work(res2)); // 위 결과로 work를 실행 (1)
}
@Service
public static class MyService {
public String work(String req) {
return req + "/asyncwork";
}
}
(1)
- map? flatMap?
- map!
- 위가 Mono 이기 때문에 map 을 걸어서 String만 꺼내주면됨
Mono<String>
/* +2번째 API를 호출 */
@GetMapping("/rest")
public Mono<String> rest(int idx) {
return client.get().uri(URL1, idx).exchange()
.flatMap(cr -> cr.bodyToMono(String.class))
.flatMap((String res1) -> client.get().uri(URL2, res1).exchange())
.flatMap(c -> c.bodyToMono(String.class))
.flatMap(res2 -> Mono.fromCompletionStage(myService.work(res2))); // 비동기적으로 서비스 메소드 호출 1번
}
@Service
public static class MyService {
@Async
public CompletableFuture<String> work(String req) {
return CompletableFuture.completedFuture(req + "/asyncwork");
}
}