24.01.14

서린·2024년 1월 14일

혼자개발

목록 보기
17/82

open api 사용방법 찾아보기

webclient 사용방법
1. webClient 개발
1) api 작성
webclientController 라는 이름으로 api 작성

@RestController
public class WebClientController {

    @GetMapping("/test")
    public Mono<String> doTest() {
        WebClient client = WebClient.create();
        return client.get()
            .uri("http://localhost:5011/webclient/test-create")
            .retrieve()
            .bodyToMono(String.class);
    }
}

가장 기본적인 webclient 사용 예제
webClient.create()는 빠르게 테스트 할 때 사용,
실제로는 webClient.builder()를 이용하여 여러가지 옵션을 부여해 사용

1개의 값을 리턴할 때는 bodyToMono 사용
복수의 값을 리턴 할 때는 bodyToFlux 사용

2) 실행, 테스트
Terminal을 열고 webserver 프로젝트 디렉토리로 이동

/mvnw clean package -DskipTests && java -jar target/webclient.jar

이 명령어로 실행jar를 생성 후 실행
curl 명령으로 webserver를 호출 해 결과가 정상적으로 나오는지 확인

❯ curl -i http://localhost:5001/test
HTTP/1.1 200 OK
Content-Type: text/plain;charset=UTF-8
Content-Length: 39

test-create => Working successfully !!!
  1. webClient 활용 매뉴얼
    1) timeout 지정
    HttpClient를 생성하면서 timeout을 지정하고, webClient.clientConnector를 이용하여 적용
@GetMapping("/test2")
    public Mono<String> doTest2() {
        HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(
                client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) //miliseconds
                    .doOnConnected(
                        conn -> conn.addHandlerLast(new ReadTimeoutHandler(5))  //sec
                            .addHandlerLast(new WriteTimeoutHandler(60)) //sec
                    )
            );    

        WebClient client = WebClient.builder()
            .baseUrl("http://localhost:5011")
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .build();
        
        return client.get() 
            .uri("/webclient/test-builder")
            .retrieve()
            .bodyToMono(String.class);          
    }

2) baseUrl
위 예시처럼 baseUrl에 Host를 지정하고, uri에는 Path만 지정해 사용할 수 있음

3) filter
filter를 이용하여 Request, Response를 컨트롤 할 수 있음
doTest2 메소드에 filter를 추가하기
첫번째 필터에는 Request Header를 추가하고,
두번째 필터에는 Request Header를 로깅하고,
세번째 필터에는 Response Header를 로깅한다

@RestController
public class WebClientController {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    
	...
    @GetMapping("/test2")
    public Mono<String> doTest2() {
		...
        
        WebClient client = WebClient.builder()
            .baseUrl("http://localhost:5011")
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .filter(
                (req, next) -> next.exchange(
                    ClientRequest.from(req).header("from", "webclient").build()
                )
            )
            .filter(
                ExchangeFilterFunction.ofRequestProcessor(
                    clientRequest -> {
                        log.info(">>>>>>>>>> REQUEST <<<<<<<<<<");
                        log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
                        clientRequest.headers().forEach(
                            (name, values) -> values.forEach(value -> log.info("{} : {}", name, value))
                        );
                        return Mono.just(clientRequest);
                    }
                )
            )
            .filter(
                ExchangeFilterFunction.ofResponseProcessor(
                    clientResponse -> {
                        log.info(">>>>>>>>>> RESPONSE <<<<<<<<<<");
                        clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{} : {}", name, value)));
                        return Mono.just(clientResponse);
                    }
                )
            )
            .build();
		
        ...
    }    

}

webClient 콘솔 로그를 확인하기
webServer의 콘솔 로그 확인, Request Header에 'from'이 있는지 확인하기

4) buff memory 늘리기
Request Data를 버퍼링 하기 위한 메모리의 기본값은 256KB 인데,

org.springframework.core.io.buffer.DataBufferLimitException: Exceeded limit on max bytes to buffer

만약 이런 에러가 발생한다면 버퍼 메모리를 늘려주어야 한다

ExchangeStrategies로 메모리를 지정하고 webClient.exchangeStragegies로 적용

@GetMapping("/test2")
    public Mono<String> doTest2() {
		...
        
        //Memory 조정: 2M (default 256KB)
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
            .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2*1024*1024)) 
            .build();

        WebClient client = WebClient.builder()
			...
			.exchangeStrategies(exchangeStrategies)
			.build();

		...
    }

5) Spring Bean class 이용(defaultHeader, defaultCookie 포함)
webClient를 만드는데에 시간이 걸리니까 미리 만들어놓고 사용하는 방법을 사용하기도 한다
이 webClient를 사용하는 모든 수행에서 공통 HttpRequest Header 와 공통 Cookie가 있다면 defaultHeader와 defaultCookie를 이용

@Configuration
public class WebClientConfig {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    
    @Bean
    public WebClient webClient() {
        HttpClient httpClient = HttpClient.create()
            .tcpConfiguration(
                client -> client.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 3000) //miliseconds
                    .doOnConnected(
                        conn -> conn.addHandlerLast(new ReadTimeoutHandler(5))  //sec
                            .addHandlerLast(new WriteTimeoutHandler(60)) //sec
                    )
            );

        //Memory 조정: 2M (default 256KB)
        ExchangeStrategies exchangeStrategies = ExchangeStrategies.builder()
            .codecs(configurer -> configurer.defaultCodecs().maxInMemorySize(2*1024*1024)) 
            .build();

        return WebClient.builder()
            .baseUrl("http://localhost:5011")
            .clientConnector(new ReactorClientHttpConnector(httpClient))
            .filter(
                (req, next) -> next.exchange(
                    ClientRequest.from(req).header("from", "webclient").build()
                )
            )
            .filter(
                ExchangeFilterFunction.ofRequestProcessor(
                    clientRequest -> {
                        log.info(">>>>>>>>>> REQUEST <<<<<<<<<<");
                        log.info("Request: {} {}", clientRequest.method(), clientRequest.url());
                        clientRequest.headers().forEach(
                            (name, values) -> values.forEach(value -> log.info("{} : {}", name, value))
                        );
                        return Mono.just(clientRequest);
                    }
                )
            )
            .filter(
                ExchangeFilterFunction.ofResponseProcessor(
                    clientResponse -> {
                        log.info(">>>>>>>>>> RESPONSE <<<<<<<<<<");
                        clientResponse.headers().asHttpHeaders().forEach((name, values) -> values.forEach(value -> log.info("{} : {}", name, value)));
                        return Mono.just(clientResponse);
                    }
                )
            )
            .exchangeStrategies(exchangeStrategies)            
            .defaultHeader("user-agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.87 Safari/537.3")
            .defaultCookie("httpclient-type", "webclient")
            .build();
    }
}

그 다음 webClientController.java를 아래처럼 단순하게 만들고
@Autowired 주석을 지정하고 webClient를 정의하면 미리 만들어진 webClient를 사용할 수 있다

@RestController
public class WebClientController {
    private final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(getClass());
    
    @Autowired
    private WebClient webClient;
    
    @GetMapping("/test")
    public Mono<String> doTest() {
        WebClient client = WebClient.create();
        return client.get()
            .uri("http://localhost:5011/webclient/test-create")
            .retrieve()
            .bodyToMono(String.class);
    }

    @GetMapping("/test2")
    public Mono<String> doTest2() {
        return webClient.get()
                .uri("/webclient/test-builder")
                .retrieve()
                .bodyToMono(String.class);
    }
}

그 다음 webClient를 빌드, 실행, 테스트 하고
webClient와 webServer의 콘솔 확인

6) mutate
webClientConfig에서 지정되어있는 baseUrl(Target host)이 다를 경우 사용할 수 없다는 점을 보완하고자 사용하는 것이 mutate

webConfigController에 메소드 추가

@GetMapping("/test3")
    public Mono<String> doTest3() {
        return webClient
                .mutate() 
                .baseUrl("http://localhost:5011")
                .build()
                .get()
                .uri("/webclient/test-mutate")
                .retrieve()
                .bodyToMono(String.class);
    }

WebClient와 webServer의 콘솔에서 bean으로 생성한 webClient에 적용한 모든 옵션이 동작하는 것을 확인 할 수 있음

0개의 댓글