이번에는 API Gateway가 무엇인지, 사용하기 위해서는 어떻게 해야하는지, 간단하게 Spring Cloud Gateway를 사용한 프로젝트를 생성해보겠다.
api gateway service는 사용자가 설정한 라우팅 설정에 따라서 각각의 endpoint로 클라이언트 대신 요청해주고, 해당 응답을 받으면 클라이언트에게 전달해주는 일종의 proxy 역할을 해준다.
API Gateway를 사용하면 다음과 같은게 가능하다.
Spring cloud에서 MSA간의 통신은 RestTemplate or Feign Client 방법이 있다.
RestTemplate restTemplate = new RestTemplate();
restTemplate.getForObject("http://localhost:8080/), User.class, 200);
@FeignClient("stores")
public interface StoreClient {
@RequestMapping(method=RequestMethod.GET, value="/stores")
List<Store> getStores();
}
근데 문제는 LB를 어디에 구축해야 하는가이다.
요 문제를 해결하기 위해서 사용할 수 있는 것이 Ribbon
이다. Ribbon
은 클라이언트 사이드 로드 밸런서로 서비스 이름으로 호출이 가능하고 헬스 체크도 가능하다.
하지만 비동기화방식이 제대로 되지 않기 때문에 요즘에는 잘 사용되지 않는다고 한다.
API Gateway이자 Http Reverse Proxy이다. 하지만 얘도 Maintenance 상태라고 한다...
요기 들어가면 어떤게 Maintenace인지 확인할 수 있다.
https://spring.io/blog/2018/12/12/spring-cloud-greenwich-rc1-available-now#spring-cloud-netflix-projects-entering-maintenance-mode
우리가 사용할 Gateway이다! 아래에서 자세하게 알아보자.
Spring Cloud Gateway는 Tomcat이 아닌 Netty를 사용한다. API GATEWAY는 모든 요청이 통과하는 곳이기 때문에 성능적인 측면이 매우 중요하며 기존의 1THREAD / 1REQUEST 방식인 SPRING MVC를 사용할 경우 성능적인 이슈가 발생할 수 있다.
Netty는 비동기 WAS이고 1THREAD / MANY REQUESTS 방식이기 때문에 기존 방식보다 더 많은 요청을 처리할 수 있다고 한다.
Spring Cloud Gateway는 크게 3가지 구성요소가 존재한다.
고유 ID, 목적지 URI, Predicate, Filter로 구성된 요소로, GATEWAY로 요청된 URI의 조건이 참일 경우 매핑된 해당 경로로 매칭을 시켜준다.
주어진 요청이 주어진 조건을 충족하는지 테스트하는 구성요소이다.
각 요청 경로에 대해 충족하게 되는 경우 하나 이상의 조건자를 정의할 수 있다. 만약 Predicate에 매칭되지 않는다면 HTTP 404 Not Found를 응답한다.
Gateway 기준으로 들어오는 요청 및 나가는 응답에 대해 수정을 가능하게 해주는 구성요소이다.
Spring cloud gateway를 사용해보자.
먼저 각각의 서비스를 생성해준다.
@RestController
@RequestMapping("/")
public class FirstServiceController {
@GetMapping("/welcome")
public String welcome() {
return "welcome to the first service";
}
}
@EnableDiscoveryClient
를 추가해 유레카 서버에 등록됨을 명시해준다.@SpringBootApplication
@EnableDiscoveryClient
public class XXXApplication {
...
}
server:
port: 8080
spring:
application:
name: my-first-service
eureka:
instance:
instance-id: ${spring.cloud.client.hostname}:${spring.application.instance_id:${random.value}}
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
@RestController
@RequestMapping("/")
public class SecondServiceController {
@GetMapping("/welcome")
public String welcome() {
return "welcome to the second service";
}
}
server:
port: 8081
spring:
application:
name: my-second-service
eureka:
instance:
instance-id: ${spring.cloud.client.hostname}:${spring.application.instance_id:${random.value}}
client:
service-url:
defaultZone: http://127.0.0.1:8761/eureka
새로운 프로젝트를 생성한다. 필요한 dependency는 DevTools
, Eureka Discovery Client
, Gateway
이다.
server:
port: 8000
eureka:
client:
register-with-eureka: false
fetch-registry: false
service-url:
defautlZone: http://localhost:8761/eureka
spring:
application:
name: apigateway-service
cloud:
gateway:
routes:
- id: first-service
uri: http://localhost:8081/
predicates:
- Path=/first-service/**
- id: second-service
uri: http://localhost:8082/
predicates:
- Path=/second-service/**
이제 first-service
, second-service
, gateway-service
를 모두 실행시킨 후 확인해보자.
단순하게 생각했을 때 http://localhost:8000/first-service/welcome
를 들어가면 8080 포트로 바뀐 http://localhost:8081/welcome
이 나와야하는데 정작 뜨는건 404 페이지이다.
현재는 위처럼 가는게 아닌, 사용자가 요청한 first-service
도 같이 붙고 있기 때문에
http://localhost:8000/first-service/welcome
-> http://localhost:8081/first-service/welcome
으로 변경되어 404가 떴던 것이다.
해당 문제는 Filter를 사용해서 해결할 수 있다.
이 부분은 다음 글에 정리하겠다!🔥
참고
SPRING CLOUD GATEWAY를 이용한 API GATEWAY 구축기
Spring Cloud로 개발하는 마이크로서비스 애플리케이션(MSA)