일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- EC2
- 파이썬 #python #docstring
- PostgreSQL
- jsonb
- 파이썬 #python #class #클래스 #상속
- redis
- 파이썬 #python #Comprehension
- 파이썬 #python #filter #map #reduce
- 파이썬 #python #lambda #람다
- 파이썬 #python #enumerate
- 약수 수하기
- 파이썬 #python #가변매개변수 #키워드가변매개변수 #args #kwargs
- docker
- Git
- 파이썬 #python #os #os.path #glob
- 파이썬 #python #예외처리 #exception
- 배포
- 파이썬기본문법 #파이썬 #python
- 파이썬 #python #전역변수 #지역변수 #eval
- spring boot
- 연산자메서드
- 사용자정의예외
- 파이썬 #python #지역함수
- 파이썬 #python #함수 #function
- 민감 정보 관리
- 파이썬 #python #모듈 #module #import #random #time #calendar #sys
- aw3
- aws
- 파이썬 #python #file #i/o #input #output
- 프로그래머스
- Today
- Total
Yeonnnnny
[TIL] MSA - API GateWay 본문
처음 MSA 에 대한 개념을 접했을 때,
개발 경험이 적은 나에게는 그 구조가 굉장히 복잡하고 어렵게 느껴졌다.
물론 지금도 그렇지만,,
그치만 세부적인 것들을 배워가면서 재미를 느꼈다.
특히, MSA 를 다루는 회사에서 일하는 모습을 상상해보니까 두근두근 거렸다. 허허허
이 글에서는 MSA의 기본 개념들과 API Gateway에 대해 살~짝 공부해보고자 한다.
먼저, MSA (Microservices Architecture)에 대해 알아보자.
💡MSA는 확장성과 독립성을 제공하지만, 운영 복잡성이 증가한다.
애플리케이션을 여러 개의 독립적인 마이크로서비스로 나누어 개발하고 운영하는 아키텍쳐이다.
각 서비스는 개별적으로 배포 및 운영되어 API를 통해 서로 통신한다.
1. 서비스별 독립 배포 가능
2. 각 서비스는 개별 데이터베이스 사용 가능
3. 다양한 기술 스택 적용 가능
4. 탄력적인 확장
💡MSA의 장점
✅ 독립적인 개발 및 배포 → 특정 서비스만 개별 업데이트할 수 있다.
✅다양한 기술 선택 가능 → 각 서비스에 맞는 언어/DB 선택할 수 있다.
✅ 확장성 우수 → 필요한 서비스만 확장 가능하다.
✅ 높은 장애 격리성 → 한 서비스 장애가 전체 서비스에 영향을 주지 않는다.
💡MSA의 단점
⚠️ 서비스 간 통신 비용 발생 → API 요청이 많아지면 성능이 저하될 수 있다.
⚠️ 데이터 일관성 유지 어려움 → 분산 트랜잭션 관리가 많이 필요하다.
⚠️ 운영 복잡성 증가 → 서비스 개수가 많아질수록 DevOps가 필요하다.
MSA에서 주로 사용하는 기술 중에 API Gateway에 대해 알아보자.
내가 생각하는 MSA는 여러 개의 서비스들이 독립적인 애플리케이션으로 운영되는 구조이다.
예를 들어, 쇼핑몰 사이트라고 가정을 했을 때, 회원, 구매, 물품 등은 각각의 독립적인 서비스로 작동을 하게되는 것이다. 이때, 사용자가 구매 서비스를 요청했을 때, 사용자 즉, 클라이언트가 구매 서비스와 직접 연결되는 것이 아니라, 서비스 사이에서 API Gateway가 중간 역할을 해준다.
API Gateway는 클라이언트와 마이크로서비스 간의 중간 역할을 하는 서비스로, 다양한 API 여청을 단일 진입점에서 받아 내부 마이크로 서비스로 라우팅, 인증, 로드 밸런싱, 요청 변환 등의 기능을 수행한다.
즉, 클라이언트가 여러 개의 마이크로서비스에 직접 요청을 하는 대신, API Gateway를 거쳐 요청을 처리함으로써 보안 강화, 트래픽 제어, 부하 분산 등의 기능을 제공할 수 있다.
📍API Gateway의 주요 기능
- 라우팅
: 클라이언트 요청을 적절한 마이크로 서비스로 전달 - 인증 및 권한 관리
: OAuth2, JWT(Json Web Token), API key 등을 활용해 사용자 인증 및 접근 제어 - 로드 밸런싱
: 동일한 마이크로 서비스의 여러 인스턴스 간 부하를 분산
: 예) product-service의 인스턴스 3개 중 하나로 요청을 라운드 로빈 방식으로 분배 - 캐싱
: 자주 사용되는 API 응답을 저장하여 성능 개선 - 요청/응답 변환
: 클라이언트 요청을 내부 API 형식으로 변환하거나, 내부 API 응답을 클라이언트가 원하는 형식으로 변환 - 로깅 및 모니터링
: API 요청 및 응답을 기록하고, 서비스 성능을 모니터링 - 레이트 리미팅
: 특정 클라이언트의 요청 수를 제한하여 서버 과부하 방지
💡Spring Cloud Gateway ?
Spring Cloud Gateway는 Spring 프로젝트의 일환으로 개발된 API 게이트웨이로, 클라이언트 요청을 적절한 서비스로 라우팅하고 다양한 필터링 기능을 제공한다.
Spring Cloud Netflix 패키지의 일부로, 마이크로 서비스 아키텍쳐에서 널리 사용된다. 그리고 Eureka와 쉽게 통합할 수 있다. Eureka를 통해 동적으로 서비스 인스턴스를 조회하여 로드 밸런싱과 라우팅을 수행할 수 있다.
기본 설정을 위한 의존성 추가가 필요하다.
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client'
}
필터링도 필요하다.
Global Filter : 모든 요청에 대해 작동하는 필터
Gateway Filter : 특정 라우트에만 적용되는 필터
필터 구현 시, GlobalFilter 또는 GatewayFilter 인터페이스를 구현하고, filter 메서드를 오버라이드 해야한다.
[필터의 주요 객체]
✅ Mono : 리액티브 프로그래밍에서 0또는 1개의 데이터를 비동기적으로 처리
✅ServerWebExchange : HTTP 요청과 응답을 캡슐화한 객체
✅GatewayFilterChain : 여러 필터를 체인처럼 연결. chain.filter(exchange)로 요청을 다음 필터로 전달
[시점에 따른 필터]
▶ Pre Filter
: 요청이 처리되기 전에 실행된다. 요청을 가로채고 필요한 작업을 수행한 다음, 체인의 다음 필터로 요청을 전달
▶ Pro Filter
: 요청이 처리된 후, 응답이 반환되기 전에 실행된다. Post 필터에서는 체인의 다음 필터가 완료된 후에 실행되어야 하는 추가적인 작업을 수행해야 한다. 따라서 chain.filter(exchange) (다음 필터로 요청을 전달하는 함수)를 호출하여 다음 필터를 실행한 후, then 메서드를 사용해 응답이 완료된 후에 실행할 작업을 정의 한다.
이제 실제로 Gateway를 구현해보자 !!!
✅ Server
(Eureka Server 의존성 추가해야 함)
applicaion.properties
spring.application.name=server
server.port=19090
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false
eureka.instance.appname=localhost
eureka.client.service-url.defaultZone=http://localhost:19090/eureka/
ServerApplication.java(main함수가 있는 클래스에 Eureka Server관련 어노테이션 추가)
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class ServerApplication {
public static void main(String[] args) {
SpringApplication.run(ServerApplication.class, args);
}
}
✅ 주문 애플리케이션
(Eureka Client 의존성 추가해야 함)
OrderController
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class OrderController {
@GetMapping("/order")
public String getOrder() {
return "Order details";
}
}
application.yml
server:
port: 19092
spring:
application:
name: order-service
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka/
실행
✅ 상품 애플리케이션
(Eureka Client 의존성 추가해야 함)
ProductController
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductController {
// application.yml에서 지정한 실행 포트 번호 가져오기
@Value("${server.port}")
private String serverPort;
@GetMapping("/product")
public String getProduct() {
return "Product info! From port : " + serverPort;
}
}
application.yml
server:
port: 19093
spring:
application:
name: product-service
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka
실행 (* 로드 밸런싱 확인을 위해 2개의 포트에 Product 실행)
✅ Gateway
CustomPreFilter (then 함수 없음 !!!!)
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.logging.Logger;
@Component
public class CustomPreFilter implements GlobalFilter, Ordered {
private static final Logger logger = Logger.getLogger(CustomPreFilter.class.getName());
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
ServerHttpRequest response = exchange.getRequest();
logger.info("Pre Filter: Request URI is " + response.getURI());
return chain.filter(exchange);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
CustomPostFilter (then 함수 있음 !!! )
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.util.logging.Logger;
@Component
public class CustomPostFilter implements GlobalFilter, Ordered {
private static final Logger logger = Logger.getLogger(CustomPostFilter.class.getName());
@Override
public Mono<Void> filter(ServerWebExchange exchange, org.springframework.cloud.gateway.filter.GatewayFilterChain chain) {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
logger.info("Post Filter: Response status code is " + response.getStatusCode());
}));
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
application.yml
server:
port: 19091 # 게이트웨이 서비스가 실행될 포트 번호
spring:
main:
web-application-type: reactive # Spring 애플리케이션이 리액티브 웹 애플리케이션으로 설정됨
application:
name: gateway-service # 애플리케이션 이름을 'gateway-service'로 설정
cloud:
gateway:
routes: # Spring Cloud Gateway의 라우팅 설정
- id: order-service # 라우트 식별자
uri: lb://order-service # 'order-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/order/** # /order/** 경로로 들어오는 요청을 이 라우트로 처리
- id: product-service # 라우트 식별자
uri: lb://product-service # 'product-service'라는 이름으로 로드 밸런싱된 서비스로 라우팅
predicates:
- Path=/product/** # /product/** 경로로 들어오는 요청을 이 라우트로 처리
discovery:
locator:
enabled: true # 서비스 디스커버리를 통해 동적으로 라우트를 생성하도록 설정
eureka:
client:
service-url:
defaultZone: http://localhost:19090/eureka/ # Eureka 서버의 URL을 지정
👩💻 RUN
모두 구동 !!!!!!!
유레카 서버 => Gateway => Order => Product 1, Product 2 순으로 실행
localhost:19090 (server) 에 접속해서 각 인스턴스 확인
order 애플리케이션의 포트 번호 19092로 직접 접속하지 않고, Gateway에서 order 서비스를 호출할 수 있다.
product 애플리케이션의 포트 번호 19093, 19094로 직접 접속하지 않고, Gateway에서 product 서비스를 호출할 수 있다.
여러 번 호출을 하면서 포트가 달라지는 것을 확인할 수 있다. 이를 통해 로드밸런싱이 잘 동작함을 알 수 있었다 !! 참고로 로드밸런싱 알고리즘은 라운드 로빈이다 !! (Round Robbin : 각 요청을 순차적으로 서버에 분배하는 알고리즘)
'TIL' 카테고리의 다른 글
[TIL] 브랜치 변경 (0) | 2025.02.17 |
---|---|
[TIL] JSONB 타입의 변수를 가진 엔티티 (Spring Boot - PostgreSQL) (0) | 2025.02.15 |
[TIL] PostgreSQL JSONB 타입으로 리스트 저장하기 (0) | 2025.02.14 |
[TIL] Git - 민감 정보 관리 (0) | 2025.02.13 |
[TIL] 스프링 시큐리티 개념 및 흐름 (0) | 2025.02.12 |