티스토리 뷰
스프링 REST 클라이언트
스프링 부트가 REST 클라이언트 관련해서 직접적으로 기능을 제공하는 것은 아니다. REST 클라이언트는 Spring Framework에서 제공하는 것이고, 부트는 그걸 쉽게 사용할 수 있도록 빈을 등록해준다.
RestTemplate과 WebClient
REST 클라이언트를 사용하는데에 두가지 선택사항이 있다. 차이점은 아래와 같다.
RestTemplate
Blocking I/O 기반의 Synchronous API
Blocking I/O 기반의 Synchronous 개념을 이해하기 위한 예제 코드
RestRunner에서 hi API를 호출한 뒤, Blocking 되므로 해당 메서드가 끝나기 전까지 다음 라인이 실행되지 않는다.
따라서 두개의 API를 호출하고 시간을 찍어보면 약 8초가 나온다.
public class RestClientController {
"/hi") (
public String hi() throws InterruptedException {
Thread.sleep(5000L);
return "hi";
}
"/bye") (
public String bye() throws InterruptedException {
Thread.sleep(3000L);
return "bye";
}
}
public class RestRunner implements ApplicationRunner {
RestTemplateBuilder restTemplateBuilder;
public void run(ApplicationArguments args) throws Exception {
RestTemplate restTemplate = restTemplateBuilder.build();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
String hiResult = restTemplate.getForObject("http://localhost:8080/hi", String.class);
log.info("/hi API Result : " + hiResult);
String byeResult = restTemplate.getForObject("http://localhost:8080/bye", String.class);
log.info("/bye API Result : " + byeResult);
stopWatch.stop();
log.info(stopWatch.prettyPrint());
}
}
2019-06-30 17:17:52.271 INFO 37526 --- [ main] i.n.s.restclient.RestRunner : /hi API Result : hi
2019-06-30 17:17:55.280 INFO 37526 --- [ main] i.n.s.restclient.RestRunner : /bye API Result : bye
2019-06-30 17:17:55.281 INFO 37526 --- [ main] i.n.s.restclient.RestRunner : StopWatch '': running time (millis) = 8425
-----------------------------------------
ms % Task name
-----------------------------------------
08425 100%
RestTemplateAutoConfiguration.class 설정 클래스 내부
프로젝트에 spring-web 모듈이 있다면 RestTemplateBuilder를 빈으로 등록해준다.
HttpMessageConvertersAutoConfiguration.class) (
RestTemplate.class) (
public class RestTemplateAutoConfiguration {
private final ObjectProvider<HttpMessageConverters> messageConverters;
private final ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers;
public RestTemplateAutoConfiguration(
ObjectProvider<HttpMessageConverters> messageConverters,
ObjectProvider<RestTemplateCustomizer> restTemplateCustomizers) {
this.messageConverters = messageConverters;
this.restTemplateCustomizers = restTemplateCustomizers;
}
public RestTemplateBuilder restTemplateBuilder() {
RestTemplateBuilder builder = new RestTemplateBuilder();
HttpMessageConverters converters = this.messageConverters.getIfUnique();
if (converters != null) {
builder = builder.messageConverters(converters.getConverters());
}
List<RestTemplateCustomizer> customizers = this.restTemplateCustomizers
.orderedStream().collect(Collectors.toList());
if (!CollectionUtils.isEmpty(customizers)) {
builder = builder.customizers(customizers);
}
return builder;
}
}
WebClient
WebClientAutoConfiguration
프로젝트에 spring-webflux 모듈이 있다면 WebClient.Builder를 빈으로 등록해준다.
가장 쉬운 방법은 spring-boot-starter-webflux를 의존성에 추가하는 방법이다.
Non-Blocking I/O 기반의 Asynchronous API
개념을 이해하기 위한 예제 코드(컨트롤러는 위와 동일)
WebClient로 부터 Mono를 만드는 라인은 Blocking되지 않는다. 실제로 subscribe가 일어날 때 요청해서 String객체를 파싱하는 동작을 한다. 이 동작도 Non-Blocking으로 동작한다. 단지, Asynchronous하게 응답이 오면 subscribe의 콜백함수가 동작을 하게 된다. 호출만 되고 밑라인을 수행한다.
그렇기 때문에, 늦게 호출한 /bye 에 대한 동작이 3초밖에 소요되지 않으므로 먼저 끝나서 로그로 찍히고, 뒤에 5초가 소요되는 /hi 의 로그가 찍힌다.
총 소요시간은 5초밖에 되지 않는다.
public class RestRunner implements ApplicationRunner {
WebClient.Builder builder;
public void run(ApplicationArguments args) throws Exception {
WebClient webClient = builder.build();
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 이 라인이 실행되도 아무런 동작을 하지 않는다.
Mono<String> hiMono = webClient.get().uri("http://localhost:8080/hi")
.retrieve()
.bodyToMono(String.class);
// subscribe를 해줘야 스트리밍이 일어난다.
hiMono.subscribe(hiResult -> {
log.info("/hi API Result : " + hiResult);
// Asynchronus 이므로 어느것이 먼저 끝날지 모른다. 스탑워치가 돌고있으면 멈추고,
if (stopWatch.isRunning()) {
stopWatch.stop();
}
// 로그를 찍고, 스탑워치를 다시 돌려줘야 한다. Asynchronus 이므로 순서보장이 안되기 때문에.
log.info(stopWatch.prettyPrint());
stopWatch.start();
});
Mono<String> byeMono = webClient.get().uri("http://localhost:8080/bye")
.retrieve()
.bodyToMono(String.class);
byeMono.subscribe(byeResult -> {
log.info("/bye API Result : " + byeResult);
if (stopWatch.isRunning()) {
stopWatch.stop();
}
log.info(stopWatch.prettyPrint());
stopWatch.start();
});
}
}2019-06-30 17:39:27.124 INFO 39877 --- [ctor-http-nio-2] i.n.s.restclient.RestRunner : /bye API Result : bye
2019-06-30 17:39:27.125 INFO 39877 --- [ctor-http-nio-2] i.n.s.restclient.RestRunner : StopWatch '': running time (millis) = 3375
-----------------------------------------
ms % Task name
-----------------------------------------
03375 100%
2019-06-30 17:39:29.064 INFO 39877 --- [ctor-http-nio-4] i.n.s.restclient.RestRunner : /hi API Result : hi
2019-06-30 17:39:29.065 INFO 39877 --- [ctor-http-nio-4] i.n.s.restclient.RestRunner : StopWatch '': running time (millis) = 5314
-----------------------------------------
ms % Task name
-----------------------------------------
03375 064%
01939 036%
REST 클라이언트 커스터마이징
RestTemplate
기본으로 java.net.HttpURLConnection 사용
커스터마이징
로컬 커스터마이징
로컬에서 정의 후 안에서만 사용
글로벌 커스터마이징
RestTemplate이 기본적으로 사용하는 java.net.HttpURLConnection를 HttpClient로 사용해야 하는 상황이 있을 수 있다.
먼저 HttpClient를 빈으로 만들기 위해서 의존성에 추가한다.
그리고 RestTemplateCustomizer를 빈으로 등록해서 전역 사용한다.
커스터마이저 안에서 restTemplate.setRequestFactory()로 맞는 구현체를 등록한다.(이것 역시 Spring의 PSA(Portable Service Abstraction)가 잘 적용 되어있다.)
이렇게 되면 더이상 기본 커넥션이 아닌, apache에서 제공하는 org.apache.http.client.HttpClient를 RestTemplate이 사용하게 된다.
public RestTemplateCustomizer restTemplateCustomizer() {
return new RestTemplateCustomizer() {
public void customize(RestTemplate restTemplate) {
restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory());
}
};
}빈 재정의
WebClient
기본으로 Reactor Netty의 HTTP 클라이언트 사용
커스터마이징
로컬 커스터마이징
WebClient webClient = builder
.baseUrl("http://localhost:8080")
.build();
...
Mono<String> hiMono = webClient.get().uri("/hi")
.retrieve()
.bodyToMono(String.class);
...
Mono<String> byeMono = webClient.get().uri("/bye")
.retrieve()
.bodyToMono(String.class);
글로벌 커스터마이징
WebClientCustomizer를 빈으로 등록해서 전역 사용
빈으로 등록해 놓고, 사용하는 쪽에서는 build()만 해서 사용
public WebClientCustomizer webClientCustomizer() {
return webClientBuilder -> webClientBuilder.baseUrl("http://localhost:8080");
}
또는 빈 재정의 해서 사용해도 됨.
Reference
'ICT Eng > Spring' 카테고리의 다른 글
[Spring]Jackson json deserialize시 snake_case to camelCase (0) | 2020.06.24 |
---|---|
[Spring Framework] IoC Container, ApplicationContext와 Bean (0) | 2019.08.01 |
[Spring Boot] Spring Security 자동 설정과 커스터마이징 (0) | 2019.07.05 |
[Spring Boot] Spring MVC CORS 활용, ajax로 검증 (0) | 2019.01.25 |
[Spring Boot] Spring HATEOAS 활용 (0) | 2019.01.25 |
- Total
- Today
- Yesterday
- ORM
- 스프링부트
- github
- Algorithm
- AWS
- 자바
- Vue.js
- 무선통신소프트웨어연구실
- vuex
- Wisoft
- springboot
- 한밭대학교
- 순환
- JPA
- 젠킨스
- 시간복잡도
- 한밭이글스
- 알고리즘
- RBT
- 정렬
- 라즈베리파이
- Spring Boot
- 인프런
- Java
- vuejs
- Raspberry Pi
- IT융합인력양성사업단
- 레드블랙트리
- Recursion
- Spring
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |