Gateway Logging 구현
모든 마이크로서비스에 대한 요청과 응답에 대한 JSON 데이터를 Gateway Server 로그에 출력하는 로직을 추가한다.
로그를 남기기 위해 사용하는 Slf4j 라이브러리를 사용할 수 있는 lombok 라이브러리를 gateway-server build.gradle에 추가한다.
gateway-server에 filter 패키지 추가 후 LoggingFilter 클래스 추가
package org.msa.gatewayserver.filter;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import java.nio.charset.StandardCharsets;
import org.reactivestreams.Publisher;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferFactory;
import org.springframework.core.io.buffer.DefaultDataBuffer;
import org.springframework.core.io.buffer.DefaultDataBufferFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import reactor.core.scheduler.Schedulers;
@Component
@Slf4j
public class LoggingFilter implements WebFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
ServerHttpResponse response = exchange.getResponse();
ServerHttpRequest request = exchange.getRequest();
DataBufferFactory dataBufferFactory = response.bufferFactory();
// log the request body
ServerHttpRequest decoratedRequest = getDecoratedRequest(request);
// log the response body
ServerHttpResponseDecorator decoratedResponse = getDecoratedResponse(response, request, dataBufferFactory);
return chain.filter(exchange.mutate().request(decoratedRequest).response(decoratedResponse).build());
}
private ServerHttpResponseDecorator getDecoratedResponse(ServerHttpResponse response, ServerHttpRequest request, DataBufferFactory dataBufferFactory) {
return new ServerHttpResponseDecorator(response){
@Override
public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
if (body instanceof Flux){
Flux<? extends DataBuffer> fluxBody = (Flux<? extends DataBuffer>) body;
return super.writeWith(fluxBody.buffer().map(dataBuffers -> {
DefaultDataBuffer joinedBuffers = new DefaultDataBufferFactory().join(dataBuffers);
byte[] content = new byte[joinedBuffers.readableByteCount()];
joinedBuffers.read(content);
String responseBody = new String(content, StandardCharsets.UTF_8);
log.info("request.id {}, method: {}, url: {}, \nresponse body: {}", request.getId(), request.getMethod(), request.getURI(), responseBody);
return dataBufferFactory.wrap(responseBody.getBytes());
}).switchIfEmpty(Flux.defer(() -> {
System.out.println("If empry");
return Flux.just();
}))
).onErrorResume(err -> {
log.error("error while decorating Response: {}", err.getMessage());
return Mono.empty();
});
} else {
System.out.println("Not Flux");
}
return super.writeWith(body);
}
};
}
private ServerHttpRequest getDecoratedRequest(ServerHttpRequest request){
return new ServerHttpRequestDecorator(request){
@Override
public Flux<DataBuffer> getBody() {
log.info("request.id: {}, method: {}, url: {}", request.getId(), request.getMethod(), request.getURI());
return super.getBody().publishOn(Schedulers.boundedElastic()).doOnNext(dataBuffer -> {
try(ByteArrayOutputStream baos = new ByteArrayOutputStream()){
Channels.newChannel(baos).write(dataBuffer.asByteBuffer().asReadOnlyBuffer());
String body = baos.toString(StandardCharsets.UTF_8);
log.info("request.id: {}, request body: {}", request.getId(), body);
}catch (Exception e){
log.error(e.getMessage());
}
});
}
};
}
}
LoggingFilter는 요청과 응답에 대한 Json 값을 출력하는 로직을 가지고 있다.
구현한 WebFilter 인터페이스는 Spring에서 제공하는 인터셉터 기능을 제공하는 Filter로 Http 요청이 Controller에 전달되기 이전, 이후에 작업을 실행하게 해주는 역할로 각종 로깅, 로그인체크, 권한체크 등을 수행할 수 있다.
filter 메서드의 반환 객체 Mono는 데이터 전송처리를 위해 사용되는 클래스이다.
파라미터로 전달받는 ServerWebExchange 객체는 HTTP 요청과 응답에 대한 액세스를 제공하는 기능을 가지고 있다.
WebFilterChain은 다음 필터에 정보를 전달하기위해 사용하는 객체이다.
메서드 구현을 보면 response, request 객체를 exchange 객체를 통해 가져오고 decoratedRequest 객체는 request 객체에 존재하는 요청 데이터를 스트리밍 하기위해 ServerHttpRequest 구현체인 ServerHttpRequestDecorator 클래스를 사용하여 기능 재정의를 하였다.
decoratedResponse 객체도 response 객체에 존재하는 응답 데이터를 읽어오기 위해 ServerHttpResponse 구현체인 ServerHttpResponseDecorator 클래스를 사용하여 기능 재정의를 하였다.
return 으로 chain 객체를 통해 filter 메서드를 진행하면 다음에 오는 filter 또는 마이크로 서비스로 데이터를 전달하는데 파라미터로 넘기는 decoratedRequest, decoratedResponse 객체는 실제로 데이터가 전달처리되는 시점에 재정의 된 기능이 수행하게 된다. request로 들어온 json 데이터를 출력하는 부분을 decoratedRequest가 재정의 된 기능에 개발해야 하고 response json 데이터 출력 기능은 decoratedResponse가 재정의 된 기능에 개발해야 한다.
getDecoratedRequest 메서드에 ServerHttpRequestDecorator 클래스를 통해 getBody 함수가 재정의 되어 request 데이터를 스트리밍 하기 위해 구현된 부분이다.
super.getBody() 부분이 실제 요청이 온 파라미터 정보이고 이 부분을 스트리밍 하기 위해 dataBuffer 객체로 뽑았다.
실제 log.info로 개발된 부분이 request body를 log로 출력한 부분이다.
WebFlux
Spring WebFlux는 Servlet API와 Servlet 컨테이너로 이루어졌던 스프링 프레임워크에서 5 버전에 새로 추가된 모듈로 클라이언트, 서버에서 reative 스타일의 어플리케이션을 도와주는 모듈이다.
WebFlux는 reactive-stack web framework로 non-blocking에 reative stream을 지원하고 Web MVC나 WebFlux 둘 다 사용이 가능하다.
동시성을 핸들링하고 함수형 프로그래밍을 사용하게 되며 비동기 프로그래밍을 보완하기위해 탄생되었다.
https://heeyeah.github.io/spring/2020-02-29-web-flux/
[Spring] WebFlux란?
WebFlux? Spring Framwork5에서 새롭게 추가된 모듈이다. web-flux는 client, server에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈이라고 한다. 스프링 공식문서에 있는, Spring WebFlux에 대한 소개와
heeyeah.github.io
https://devuna.tistory.com/108
[Spring] WebFlux의 개념 / Spring MVC와 간단비교
💡 WebFlux란? Spring WebFlux는 Spring 5에서 새롭게 추가된 모듈입니다. WebFlux는 클라이언트, 서버에서 reactive 스타일의 어플리케이션 개발을 도와주는 모듈이며, reactive-stack web framework이며 non-blocking에
devuna.tistory.com
https://jie0025.tistory.com/541
[Spring Webflux] 스프링 웹플럭스 (MVC와 비교, 내부 동작 원리, Netty)
황정식 - 스프링으로 시작하는 리액티브 프로그래밍 Spring WebFlux를 이용한 Non-Blocking 애플리케이션 구현 책을 통해 공부하면서 개인적으로 정리한 내용입니다. 목차 Spring Webflux란? Spring Webflux 탄
jie0025.tistory.com
서버실행
- config-server
- eureka-server
- gateway-server
- item-service
postman으로 물품등록 API 요청
gateway-server 콘솔에 요청과 응답에 대한 json 데이터가 출력된다.