관리 메뉴

Mini

[스프링 msa] RestTemplate로 서비스간 통신하기, restTemplate 404 해결 본문

Java/Spring-Cloud

[스프링 msa] RestTemplate로 서비스간 통신하기, restTemplate 404 해결

Mini_96 2025. 2. 12. 03:20
  • 먼저 서비스를 키려면
    • config-server를 켜야 yml파일을 읽어올수 있음
    • docker의 rabbit mq 도 켜야함
  • user-service에서 orders를 order_service에 요청해서 받아오기

user-service.yml

@Override
    public UserDto getUserByUserId(String userId) {
        UserEntity userEntity = userRepository.findByUserId(userId);

        if(userEntity==null){
            throw new UsernameNotFoundException("User not found");
        }

        UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);
//        ArrayList<ResponseOrder> orders = new ArrayList<>();

        /* Using RestTemplate */
        String orderUrl = String.format(env.getProperty("order_service.url"), userId);
        ResponseEntity<List<ResponseOrder>> orderResponse =
                restTemplate.exchange(orderUrl, GET, null, new ParameterizedTypeReference<List<ResponseOrder>>() {
                });

        List<ResponseOrder> orderList = orderResponse.getBody();
        userDto.setOrders(orderList);

        return userDto;
    }
  • rest template bean을 등록해야함

결과

* 단점

  • 해결 : 유레카 서비스에 동록되어있는 서비스이름으로 바꾸면 됨
  • port변경, ip 변경에도 대비 가능함

RestTemplate과 Eureka Server의 작동 원리를 설명해드리겠습니다:

1. Eureka 서비스 디스커버리 작동 원리

[User Service] <----> [Eureka Server] <----> [Order Service]
      ↑                                            ↑
      |                                           |
   등록/조회                                      등록/조회
  1. 서비스 등록 과정:
  2. // 각 서비스는 시작 시 Eureka Server에 자신의 정보를 등록 OrderService -> Eureka Server: "나는 order-service이고 localhost:8000에서 실행 중" UserService -> Eureka Server: "나는 user-service이고 localhost:8001에서 실행 중"
  3. 서비스 조회 과정:
  4. // 서비스가 다른 서비스를 호출할 때 UserService -> Eureka Server: "order-service의 위치를 알려주세요" Eureka Server -> UserService: "order-service는 localhost:8000입니다"

2. @LoadBalanced RestTemplate 작동 원리

@LoadBalanced
RestTemplate restTemplate;

// http://ORDER-SERVICE/order-service/%s/orders 호출 시
1. "ORDER-SERVICE" 를 실제 호스트로 변환 필요
2. Eureka Server에서 서비스 위치 조회
3. 실제 호출: http://localhost:8000/order-service/%s/orders

3. 전체 통신 흐름

1. User Service 시작
   - Eureka Server에 등록
   - 서비스 이름: user-service
   - 위치: localhost:8001

2. Order Service 시작
   - Eureka Server에 등록
   - 서비스 이름: order-service
   - 위치: localhost:8000

3. RestTemplate 호출 시
   UserService -> EurekaServer: "ORDER-SERVICE 위치 요청"
   EurekaServer -> UserService: "localhost:8000 응답"
   UserService -> OrderService: "localhost:8000으로 실제 API 호출"

4. 로드밸런싱

// 여러 인스턴스가 있을 경우
OrderService1: localhost:8000
OrderService2: localhost:8001
OrderService3: localhost:8002

@LoadBalanced RestTemplate은 자동으로 부하 분산:
- Round Robin (기본)
- 가중치 기반
- 최소 연결 수

5. 실제 코드에서의 동작

// 1. 서비스 등록 (application.yml)
spring:
  application:
    name: order-service  // Eureka에 등록될 이름
  eureka:
    client:
      serviceUrl:
        defaultZone: http://localhost:8761/eureka/

// 2. 서비스 호출 (UserServiceImpl.java)
String orderUrl = String.format(env.getProperty("order_service.url"), userId);
// ORDER-SERVICE는 Eureka를 통해 실제 호스트:포트로 변환됨
// http://ORDER-SERVICE/order-service/123/orders
// -> http://localhost:8000/order-service/123/orders

RestTemplate restTemplate;
ResponseEntity<List<ResponseOrder>> response = 
    restTemplate.exchange(orderUrl, HttpMethod.GET, null, 
        new ParameterizedTypeReference<List<ResponseOrder>>() {});

6. 고가용성 지원

// 여러 서비스 인스턴스 실행 시
OrderService (instance 1) - port 8000
OrderService (instance 2) - port 8001

// @LoadBalanced RestTemplate이 자동으로:
1. 서비스 디스커버리
2. 로드 밸런싱
3. 장애 극복

이해하기 쉽게 정리하면:

  1. 모든 서비스는 Eureka Server에 자신을 등록
  2. 서비스 간 통신 시 서비스 이름 사용
  3. @LoadBalanced RestTemplate이 서비스 이름을 실제 주소로 변환
  4. 자동 로드밸런싱으로 여러 인스턴스에 요청 분산

* 오류

  • restTemplate에서 404 에러가뜸
  • 의심 : ORDER-SERVICE를 localhost로 못바꾸는거 아닌가? -> 아님
  • url 패턴을 아래와같이 수정시 잘 작동함
String testUrl = "http://ORDER-SERVICE/health_check";  // URL 패턴 수정
  • 원인분석

API Gateway의 라우팅 설정에는 보통 다음과 같이 /order-service/** 패턴이 포함되어 있을 것입니다:

spring:
  cloud:
    gateway:
      routes:
        - id: order-service
          uri: lb://ORDER-SERVICE
          predicates:
            - Path=/order-service/**

서비스 간 직접 통신(RestTemplate)과 API Gateway를 통한 통신의 차이:

  1. API Gateway 통해서 호출할 때:
  2. Client -> http://gateway:8000/order-service/{userId}/orders
  • Gateway가 /order-service를 보고 ORDER-SERVICE로 라우팅
  1. 서비스 간 직접 통신할 때:
  2. UserService -> http://ORDER-SERVICE/{userId}/orders
  • OrderController의 매핑 구조를 직접 따라야 함
  • Gateway의 라우팅 규칙이 적용되지 않음

그래서 직접 통신할 때는 Gateway의 라우팅 규칙이 아닌, 실제 OrderController의 매핑 구조를 따라야 합니다.

네, 맞습니다. 마이크로서비스 간 내부 통신에서 RestTemplate을 사용할 때는 API Gateway를 거치지 않고 서비스를 직접 호출합니다.

작동 흐름을 보면:

  1. 외부 요청 (API Gateway 경유)
  2. Client -> API Gateway (8000) -> 각 서비스 http://gateway:8000/order-service/** -> Order Service
  3. 내부 서비스 간 통신 (API Gateway 미경유)
  4. User Service -> Order Service (직접 통신) http://ORDER-SERVICE/** -> Order Service

이렇게 설계된 이유:

  1. 성능 최적화 - 불필요한 게이트웨이 홉(hop) 제거
  2. 내부 통신의 단순화
  3. 불필요한 오버헤드 방지

그래서 RestTemplate로 내부 호출 시에는 OrderController의 실제 엔드포인트 매핑을 따라야 하고, API Gateway의 라우팅 규칙은 고려하지 않아도 됩니다.

 

* 결론

이게 정답

  • 이게 http:localhost:order-service포트번호 로 변환된다.

'Java > Spring-Cloud' 카테고리의 다른 글

[스프링 msa] 대칭키를 이용한 암호화  (0) 2025.02.11
[Spring] spring cloud bus 써보기  (0) 2025.02.04