인프런의 "Spring Cloud로 개발하는 마이크로서비스" 강의를 보고 작성되었습니다.
https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81-%ED%81%B4%EB%9D%BC%EC%9A%B0%EB%93%9C-%EB%A7%88%EC%9D%B4%ED%81%AC%EB%A1%9C%EC%84%9C%EB%B9%84%EC%8A%A4
마이크로소프트에서 클라우드 아키텍처를 보여주는 그림으로, 클라이언트에서 직접 Microservices를 호출하는 모습입니다.
이렇게 직접 호출을 하게되면 microservice가 추가 혹은 변경 사항이 생기면 클라이언트쪽에서도 엔드포인트에 대해 수정하고 배포할 필요가 생깁니다.
API Gateway Service는 사용자가 설정한 라우터의 엔드포인트로 클라이언트를 대신해 요청한 후 받은 응답을 클라이언트에 전달해주는 프록시 역할을 합니다.
그림과 같이 API Gateway에서 microservice로 요청되는 모든 정보를 일괄처리하게 되어 시스템의 내부 구조를 숨기고, 외부 요청에 대해 적절한 형태로 가공해 응답이 가능하게 됩니다.
1) RestTemplate
서버 주소와 포트, 전달할 파라미터, 메소드의 방식을 외부에 전달하는 형태입니다.
2) Feign Client
특정 인터페이스를 생성해서 외부 서비스의 이름을 등록합니다. 등록된 microservice의 이름으로 호출이 가능하게 됩니다.
초기 Spring Cloud에서는 Client Side Load Balancer인 Ribbon을 제공했습니다.
Ribbon은 서비스의 이름으로 호출이 가능하지만, 비동기 처리가 잘 되지 않는 문제가 있어 최근 사용이 되지 않아 Spring Boot 2.4에서 Maintenance 상태입니다.
Load Balancer 역할을 하지만, 역시 비동기 처리가 잘 되지 않기 때문에 현재는 Maintenance 상태입니다.
위의 두 Load Balancer는 비동기 방식 처리의 어려움으로 비동기 처리가 가능한 Spring Cloud Gateway를 사용해보려 합니다.
DevTools, Eureka Discovery Client, Gateway 를 추가해 주었습니다.
gateway는 다음과 같이 gateway-mvc가 아닌 의존성을 추가해주어야 Tomcat 서버가 아닌 비동기 방식을 사용하는 Netty 서버로 연결이 됩니다.
cloud.gateway.routes : 리스트 형태로 라우트 객체를 등록합니다.
predicates : 라우터의 조건을 설정합니다.
클라이언트가 조건에 맞는 요청을 하게 되면 uri로 라우팅을 합니다.
이때, Path가 그대로 uri에 붙기 때문에 컨트롤러에서 매핑을 등록할 필요가 있습니다.
이와 같이 RouterLocator Bean을 등록하게 되면 application.yml에 등록한 라우터를 자바 코드를 통해 작성 가능합니다.
여기서는 람다식을 통해 인자의 path를 확인 후 request, response 헤더를 설정해 uri로 이동시킵니다.
localhost:8000/first-service/message 호출 시 로그 확인 가능합니다.
위의 자바 코드로 작성한 필터 설정을 yml 파일에서 filters를 통해 등록할 수 있습니다.
Custom Filter를 등록해 사용자 정의의 필터로 로그, 인증 등의 역할을 수행할 수 있습니다.
request id와 response status code를 로그로 남기는 필터입니다.
위와 같이 cloud.gateway.routes의 filters에 CustomFilter를 등록합니다.
각 라우터가 실행될 때 공통적으로 시행될 수 있는 필터로, 과정상 모든 필터의 가장 먼저 시작되고 마지막에 종료됩니다.
gateway에 default-filters로 설정합니다.
현재는 yml 파일이 microservice에 내장되는 데이터로, 변경이 일어나면 다시 빌드하고 배포하는 과정을 거쳐야하는데, 이후 진행하는 강의에서 외부 데이터를 가져와 반영합니다.
postman을 통해 진행한 테스트를 보면 컨트롤러가 정상 동작하고, CustomFilter와 GlobalFilter의 로그가 정상적으로 찍히는 모습을 볼 수 있습니다.
GlobalFilter를 응용해서 로그를 출력하는 Filter를 구현해보았습니다.
총 3개의 Filter가 적용된 프로젝트의 전체적인 흐름은 다음과 같습니다.
GlobalFilter와 동일하게 apply 메소드를 정의하여 구현하였지만, 반환 값인 GatewayFilter를 인터페이스이기 때문에 인스턴스 생성이 불가하므로 new OrderedGatewayFilter를 사용해 구현했습니다.
OrderdedGatewayFilter 내부의 filter라는 메소드가 재정의 됨으로 필터가 해야할 역할을 정의할 수 있고, filter 메소드는 OrderdedGatewayFilter의 생성자의 GatewayFilter의 filter를 구현하는 것입니다.
GatewayFilter의 filter메소드의 argument로 exchange와 chain을 받고 있어 위의 람다식에서 exchange와 chain을 통해 filter 메소드를 구현한 것으로 생각합니다.
filters에 추가적으로 전달할 파라미터가 생기면 각 필터에 name 옵션을 추가해 주어야 합니다.
위에서 구현한 Spring Cloud Gateway와 이전 포스팅에서 만든 Eureka를 연동시켜 Load Balancer의 역할을 하도록 만들었습니다.
uri 부분을 lb://(discovery service에 등록된 이름) 으로 변경해 포워딩 시킬 수 있습니다.
이 부분은 앞서 포스팅에서 설명한 부분으로 간단하게 설명하면
port를 0으로 설정해 랜덤 포트로 변경한 후 intance-id를 추가해 microservice가 구분되도록 설정합니다.
Controller에 서비스에 랜덤으로 설정된 포트를 확인하는 로직을 구현하였습니다.
이후 postman으로 호출하게 되면 다음과 같이 라운드로빈 방식으로 게이트웨이가 서비스를 호출하게 됩니다.