3 분 소요

개요

Communication Types

동기 & 비동기 방식

스프링 클라우드로 개발 된 마이크로 서비스들 간에는 동기 방식과 비동기 방식으로 서로 통신을 할 수 있다.

  • Synchronous HTTP communication
    - 하나의 클라이언트 요청이 들어왔을 때, 해당 작업이 끝날 때까지 다른 작업을 할 수 없다.
  • Asynchronous communication over AMQP
    - AMQP라는 프로토콜을 이용해 각 마이크로 서비스 간 비동기 통신이 가능

스크린샷 2022-10-07 오후 1 25 38
예를 들어, 쇼핑몰 어플리케이션에서 주문이 많이 늘어나서, order-service가 두 개 기동되고 있다고 가정해보자.
클라이언트로부터 user-service에 요청이 들어왔을 때, user-serviceorder-service로 부터 데이터를 가지고 와야하는 상황이다.

그러기 위해서, 먼저 유저 서비스는 유레카한테 오더 서비스의 존재를 물어본다. (어떤 서비스를 사용하면 되는지, 어디에 서비스가 있는지)
그리고 유저 서비스는 유레카로부터 오더 서비스의 정보를 받아서 직접 해당하는 오더 서비스를 호출하는 방식이다.

유저 서비스가 유레카에게 오더 서비스의 정보를 물어볼 때, 유레카는 round-robin 방식으로, 첫 번째 오더 서비스와 두 번째 오더 서비스의 정보를 번갈아가며 알려 줄 것이다.

Rest Template

두 번째로, 마이크로 서비스 간 데이터를 호출할 수 있는 방법 중에서, 전통적으로 많이 사용되었던 Rest Template 라는 API를 사용할 수도 있다.
Rest Template은 마이크로 서비스 혹은 스프링 클라우드라는 기술에 도입된 것이 아니라,
기존에 java로 만들어진 웹 어플리케이션 간에서 HTTP 프로토콜을 이용해 GET 방식이라던가 POST 방식으로써 또 다른 서비스 혹은 API를 호출하기 위해서 사용되었던 방식이다.

Rest Template이라는 인스턴스를 생성한 뒤, 해당하는 인스턴스에 GET 메서드 혹은 POST 메서드를 정의하고,
파라미터가 필요하다면 파라미터도 전달하고, 반환값도 받아오는 게 Rest Template의 주 목적이었다.

스크린샷 2022-10-07 오후 1 39 37
앞선 예제와 같이 두 개의 오더 서비스가 있을 때, 유저 서비스가 다이렉트로 유레카 서비스 혹은 오더 서비스를 사용하는게 아니라,
Rest Template이라는 객체를 이용해서 일종의 서비스를 대신 처리할 수 있는 대리자 역할을 하게 한다.

만약, 클라이언트가 http://localhost:8000/user-service/users/{userId}로 사용자의 정보를 요청했을 때,
유저 서비스는 Rest Template을 이용해서 유레카 서비스에서 얻어온 오더 서비스의 정보를 가지고 호출하게된다.

우리가 필요한 것은, 사용자 정보를 보여줌과 동시에 사용자의 주문 이력을 보여주고 싶다고 하자.
이 작업을 하기 위해서 Rest Template을 이용해 오더 서비스가 가지고 있는 API(http://localhost:8000/order-service/users/{userId}/orders)를 호출할건데,
이 때, {userId} 값은 사용자의 정보를 얻기 위해 제일 처음에 클라이언트가 요청한 {userId} 값이다.
이를 오더 서비스의 파라미터로 전달하게되면 해당하는 사용자의 주문 이력을 받아올 수 있다.

이제부터 RestTemplate을 이용해서 유저 서비스에서 오더 서비스로 데이터를 요청하고 받아오는 과정에 대해 알아보자.

Rest Template 사용

user-service

UserServiceApplication.java

@SpringBootApplication
@EnableEurekaClient
public class UserServiceApplication {

    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    @Bean
    public RestTemplate getRestTemplate() {
        return new RestTemplate(); // 🌟 추가
    }
}

이렇게 빈으로 등록된 애들은 다른 쪽에 있는 컨트롤러, 서비스 혹은 레포지토리에서 주입받아서 사용할 수 있다.

UserServiceImpl.java

코드에서는 생략했지만, RestTemplate는 생성자 주입을 통해 주입받았다.

@Service
public class UserServiceImpl implements UserService {
    ...
    @Override
    public UserDto getUserByUserId(String userId) {

        UserEntity userEntity = userRepository.findByUserId(userId);
        UserDto userDto = new ModelMapper().map(userEntity, UserDto.class);

        // 주문 생성
        /* 🌟 Using RestTemplate */
        String orderUrl = String.format(env.getProperty("order-service.url"), userId);
        ResponseEntity<List<ResponseOrder>> orderListResponse =
            restTemplate.exchange(orderUrl, HttpMethod.GET, null,
                                  new ParameterizedTypeReference<List<ResponseOrder>>() {});

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

        return userDto;
    }
}

native-file-repo/user-service.yml

구성 정보는 이전에 생성했던 native-file-repo 안에 있는 user-service.yml에 따로 적는다.

order-service:
  url: http://127.0.0.1:8000/order-service/%s/orders

테스트

유레카 서버 -> config-service -> gateway-service -> user-service -> order-service 순으로 기동하자.

상품 주문

아래와 같이, 로그인 후 생성된 userId를 파라미터로 넣어서 요청한다.
스크린샷 2022-10-07 오후 2 17 25

사용자 정보 확인

중요한건, 이때 사용자 정보에 주문 목록도 같이 가져와야 한다는 것이다!
상품 주문과 마찬가지로 로그인 후 생성된 userId를 파라미터로 넣고,
user-service에는 인증 필터도 걸려있기 때문에 Authorizationtoken 값도 집어넣는다.

user-service에 break point를 걸어놓고, 디버그 모드로 실행히보면 아래와 같이 order-service에서 주문 정보를 가져왔음을 확인할 수 있다.
스크린샷 2022-10-07 오후 2 23 42

resume 버튼을 클릭하게 되면 “사용자 단건 조회” 시에도 사용자의 “주문 정보”까지 가져왔음을 확인할 수 있다.
스크린샷 2022-10-07 오후 2 26 19

주문을 하나 더 한 뒤, 다시 사용자 단건 조회를 해보자
스크린샷 2022-10-07 오후 2 30 59
이번에도 주문 정보를 리스트로 잘 가져온다!

UserServiceApplication.java

user-service에서 Rest Template을 이용해 order-service를 호출하기 위해서,
user-service.yml에 호출하고자하는 order-service의 주소를 명시했다.
이때 사용되는 order-service의 주소를 조금 더 간단히 해보자.

스크린샷 2022-10-07 오후 2 39 24
위 사진처럼 사용되었던 주소값 대신 (유레카에 등록된) 마이크로 서비스의 이름으로 변경하기 위해 아래처럼 변경하자.

@Bean
@LoadBalanced // 🌟 추가
public RestTemplate getRestTemplate() {
    return new RestTemplate();
}

UserServiceApplication에 빈으로 등록한 RestTemplate@LoadBalanced만 붙여주면 된다.

native-file-repo/user-service.yml

이제 user-service.yml에 등록한 order-service의 url을 변경하자.

order-service:
  url: http://ORDER-SERVICE/order-service/%s/orders

참고
이 때, order-service의 Microservice name은 유레카 서버에서 확인할 수 있다.
스크린샷 2022-10-07 오후 2 46 22

busrefresh

구성 정보가 변경되었을 때, user-service를 재기동해도 되지만 그것보다는 Spring Cloud Bus를 이용하자.
🌟 rabbitmq-server를 먼저 기동하자!
스크린샷 2022-10-07 오후 2 50 13

http://localhost:8000/user-service/actuator/busrefresh를 통해 busrefresh를 하면 Spring Cloud Bus에 연결된 모든 마이크로 서비스들의 구성 정보가 일괄적으로 업데이트 된다.
스크린샷 2022-10-07 오후 2 51 01
그리고 아까와 같이 상품 주문 -> 사용자 단건 조회를 해보면 정상적으로 작동함을 확인할 수 있다.
스크린샷 2022-10-07 오후 3 05 24



💛 개인 공부 기록용 블로그입니다. 👻

맨 위로 이동하기