프로젝트 생성
컴포넌트 스캔 & 빈 객체 등록 어노테이션
@Component, @Service, @Repository
@Controller(@Component + @RequestMapping)
@Bean
Spring Cloud Routung 하위 항목인 Gateway를 선택.
설정용 파일은 yml로 바꾼다.
application.yml 파일에 설정을 추가한다.
routes 하위 항목에 대해 설명하자면
일반 프로젝트는 톰캣에 의해 작동한다.
스프링 클라우드 게이트웨이 서버는 Netty에 의해 작동된다.
둘을 조금 더 자세하게 비교하면,
Netty :
Tomcat :
Netty :
Tomcat :
Netty :
Tomcat :
Netty :
Tomcat :
Netty :
Tomcat :
Netty와 Tomcat은 각각 다른 목적과 특징을 가지고 있다. Netty는 비동기 네트워크 애플리케이션을 개발하는 데 적합하며, 뛰어난 성능과 확장성을 제공한다. 반면 Tomcat은 자바 웹 애플리케이션을 개발하고 실행하기 위한 서블릿 컨테이너로 주로 사용된다.
MicroService의 핵심은 Reverse-Proxy 즉, 마이크로 서비스 서버측이 단일 서버로 보이도록 해주는 것이 중요하다.
IntelliJ 에서 Spring Web 프로젝트 2개 생성(first-service, second-service)
두 마이크로서비스는 게이트웨이 서버에서 설정할 포트 번호가 8001, 8002로 연결할 예정이기 때문에
실행 포트번호를 아래와 같이 설정한다.
접속 가능 여부를 테스트 하기 위해 HelloController 생성 및, RestController 어노테이션을 사용하여 접근이 가능하도록 한다.
그 다음으로 게이트웨이, first-service, second-service 각 서버를 실행 해준다.
실행 후 개별 서버로 접속하여 접속 가능 여부를 확인한다.
그 다음으로 할일은 게이트웨이 서버 하나를 통해서 두 마이크로서비스 서버로 접근이 되도록 application.yml 파일 내 설정을 바꿔준다.
게이트웨이 서버쪽에 application.yml 파일에 localhost:8001 과 8002 관련 주소에 대한 설정을 추가해준다.
위 설정을 보면, first-service 라는 아이디의 route는 /first-service/** 라는 요청이 오게되면, 요청에 맞는 uri(컴퓨터 내 자원 주소)인 http://localhost:8001/로 연결해 주겠다는 의미이다.
위처럼 설정을 맞춘 뒤 테스트 시 404 오류가 발생함.
위처럼 수정 후 게이트웨이 서버의 포트 번호인 8000을 경유하여 접속해도 마이크로서비스 서버로 잘 접속이된다.
아래부터 마이크로서비스 서버 접속에 대한 필터 설정과 자바 config 파일로 설정하는 방법에 대해 알아보겠다.
라우팅 정보를 yml이 아닌 자바 config 클래스로 옮기는 작업을 먼저 해보겠다.
우선, yml 파일에 정의한 내요을 주석을 해준다.
그 다음 필터 설정이 들어갈 패키지 및 클래스를 생성.
그 다음으로 아래와 같은 양식으로 작성한다.
@Configuration이 붙은 클래스는 빈 컨테이너에 내부에 있는 메서드가 리턴하는 요소들을 적재하는데 @Bean 어노테이션이 붙은 메서드가 리턴하는 객체들을 전부 적재해준다.
설정은 스프링 시큐리티처럼 람다형식으로 설정하면 되는데
path() 에는 yml파일에서 Path에 적었던 패턴을 적으면 되고
uri()에는 path()에 적힌 패턴에 걸리면 어떤 서버주소로 포워딩 할 지 정의
filters()에는 이 과정에서 활용할 필터들을 등록하면 된다.
필터는 프록시 형태로 요청 전, 후에 필요한 로직을 추가할 때 사용한다.
필터는 크게 pre-filter와 post-filter로 나뉜다.
pre-fiter : request 처리 전에 실행되는 필터
post-filter : response 처리 전에 실행되는 필터
필요한 로직이 있는 경우 filters에 필터들을 등록해야 한다.
위처럼 람다형식으로 필터 메서드를 add 해주고, 해당 내용을 내부에 작성해준다.
2개 이상의 라우터 등록은 위처럼 체이닝 형식으로 route() 메서드를 연결하면 된다.
필터를 거치는 요청과 응답 과정
위에 그림을 통해 알 수 있듯이, 마이크로서비스 서버 측에서 요청 데이터를 받기 전에 게이트 웨이 서버를 거친 뒤 필터를 거쳐 요청이 들어온다.
이 때, 필터를 거치면서 헤더에 'fsreqh' 라는 키와 'f-req-v'라는 값이 추가된다.
마이크로서비스에 header를 확인하기 위한 메서드를 추가한다.
위와 같이 헤더의 값을 잘 출력하는 것을 볼 수 있다.
application.yml 파일에서 필터를 적용하는 방법
게이트웨이 서버측에 자바 config 파일에 @Bean 어노테이션을 비활성화 해준다.
이러면 빈 컨테이너에 해당 메서드가 반환하는 객체가 빈으로 등록되지 않는다. 따라서 설정도 초기화 된다.
그리고 yml 파일의 주석을 풀어준다.
그 다음으로 filters라는 항목을 추가해준다.
filters에 추가할 항목은 아래와 같다.
추후 커스텀 헤더를 쓸 예정이지만, 우선 디폴트 헤더드들을 이용해보는것이며
위와 같이 작성하면, pre-filter 와 post-filter로 정상 작동하다.
웹 브라우저로 하셔도 좋고, postman으로 진행이 가능하다.
커스텀 필터는 AbstractGatewayFilterFactory를 상속한 필터 클래스를 @Component 어노테이션을 이용하여 등록해주면 동작.
생성자에서 super()를 사용하여 apply 라는 메서드 하나를 구현해주면 동작함.
netty 서버에서는 tomcat 서버와는 달리
ServerHttpRequest와 ServletHttpResponse를 사용하고
등록된 필터는 yml 파일에 이름만 기입하면 적용된다.
실습을 진행해보면, AbstractGatewayFilterFactory를 생성해준다. CustomFilter의 이너클래스인 Config를 생성해준다.
해당 이너클래스 안에는 추후 설정 내역을 작성하게 된다.
굳이 이너클래스로 작성한 이유는 코드의 응집도를 높이기 위함이다.
그리고 생성자를 이용해 이너클래스 내역을 super()로 보내준다.
그 다음으로 apply(Config config)를 구현해야 한다.
그리고 비동기 서버에서 사용하는 ServerHttpRequest, ServerHttpResponse를 import 한다.
여기서 임포트 구문이 reactive가 포함된 패키지인지 확인해줘야 한다.
pre-filter는 그냥 내부에 작성하면 돌아가는데 반해
post-filter는 아래와 같이 return 구문 안에chain.filter().then(Mono.formRunnable(내부 람다));
를 이용해서 설정할 수 있다.
Mono는 스프링 에서 추가되었는데 비동기 방식 서버에서 단일값 전달할 때 사용하는 양식이다.
이제 여기까지 완성시켰다면, yml 파일에 등록해보자.
이미 컴포넌트 어노테이션을 통해 등록된 필터 클래스이므로 이름만 적어주면 된다.
참고로 OrderedGatewayFilter를 원래 구현해서 만들어야 하는데 이를 람다로 처리한것이므로
내부 구조가 궁금하다면 OrderedGatewayFilter를 살펴보시는것도 좋습니다.
전역 필터는 위의 AbstractGatewayFilterFactory를 사용하되 등록을 게이트웨이 서버측 자체에 해준다는 차이가 있다.
yml 파일에서의 등록을 gateway 측에 직접 걸어주면 된다.
먼저 GlobalFilter를 새로 작성한다.
어차피 실행하는 기본적인 코드나 원리는 CustomFilter와 비슷하고
단지 차이점은 개별 마이크로서비스에만 적용하는지 gateway 서버에 전역적으로 적용하는지의 차이만 있다.
GlobalFilter 라는 클래스를 생성해준다.
다음 설정파일에서 넘겨주는 인자값을 받을 수 있도록 Config 이너 클래스에 멤버 필드들을 선언한다. 그리고 lombok의 @Getter, @Setter를 추가해준다.
그리고 해당 정보를 이용해서 글로벌 필터 내부 코드를 작성한다.
위 코드에서 상기하고 있어야 할 내용은 pre-filter는 그냥 작성해주면 되고, post-filter는 return 구문 속에 코드를 작성해서 만든다는 점이다.
이후 yml 파일에서 인자 제공 및 필터 등록을 해준다.
위처럼 routes 와 같은 depth 내에 name에는 등록할 전역 필터명을 args 내부에는 Cofnig 이너클래스 멤버변수에서 전달할 값을 입력해주면 된다.
호출 우선순위는
글로벌 pre -> custom pre -> custom post -> 글로벌 post
순으로 마치 스택처럼 호출 및 종료가 이뤄진다.
추후 config 서버를 구성해서 해당 서버만 세팅을 바꿔주고 마이크로서비스 서버는 설정을 바꾸지 않아도 되게 구성할 수 있다.
default-filters:
- name: GlobalFilter
args:
message:Global Filter Default Message Test
pre: true
post: true
- name: GlobalFilter2
args:
message:Global Filter Default Message Test2
pre: false
post: false
위처럼 전역 필터를 여러개 나열하는 방법이다.
로깅에 좀 더 적합하게 필터를 개선하는 방법
println() 하는 것은 콘솔에 출력만 해주고 실제 로거에 저장 되지는 않는다.
@Slf4j 어노테이션을 추가하여 로거에 로깅을 해준다.
별도의 매개변수 없이 필터를 복수로 등록할 경우 아래처럼 나열하면 된다.
매개변수를 제공하는 경우 아래와 같이 필터별로 - name으로 구분하여 나열해준다.