[MSA] 서비스 간 통신 - OpenFeign

yejin·2024년 11월 28일

MSA

목록 보기
31/36

서비스 간 통신

📌 개요

  • 통신 방식
    1. 동기 방식(Synchronous HTTP Communication)
    ➡ 클라이언트 요청 시, 해당 요청이 끝날 때까지 다른 클라이언트의 요청 처리 불가능
    2. 비동기 방식(Asynchronous communication over AMQP)
    ➡ AMQP 프로토콜 이용하여 마이크로서비스 비동기 통신 지원하여 연결되어 있는 모든 마이크로서비스에 변경 사항 전달
  • 통신 방법
    1. Rest Template
    2. OpenFeign ✅

💡 OpenFeign 란?

  • 개념
    Rest Call을 추상화 한 Spring Cloud Netflix 라이브러리
    선언적인 HTTP 클라이언트를 제공하는 라이브러리
  • 사용방법
    1. 호출하려는 HTTP Endpoint에 대한 interface 생성
    2. @FeignClient 선언
    ➡ Spring Boot 애플리케이션에서 Feign 클라이언트를 활성화하는 어노테이션
  • Load Balanced 지원
  • 장점
    1. 코드의 간결성
    2. 유지보수성 향상
    3. 효율적인 통합
    4. MSA에 적합
    서비스 등록/검색(Eureka), 로드 밸런싱(Ribbon), 장애처리(Hystrix)와 잘 통합되기 때문에 마이크로서비스 환경에서 다른 서비스와의 통신을 손쉽게 설정하고 관리 가능

📌 소스코드

  • User-service pom.xml 수정
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
  • EUserServiceApplication 수정
package com.example.euserservice;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.web.client.RestTemplate;

@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients //추가
public class EUserServiceApplication {

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

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

//Rest Template이 아닌 OpenFeign을 사용할 것이기 때문에 더 이상 사용하지 않음
    @Bean
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
//여기까지

}
  • 인터페이스 OrderServiceClient.java 생성
package com.example.euserservice.client;

import com.example.euserservice.vo.ResponseOrder;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

import java.util.List;

@FeignClient(name="order-service")
public interface OrderServiceClient {

    @GetMapping("/order-service/{userId}/orders")
    List<ResponseOrder> getOrders(@PathVariable String userId);
}

참고
interface는 모두 public으로 사용해야 하기 때문에 굳이 public을 명시하지 않아도 됨

💡 추가 설명
@FeignClient 어노테이션을 사용한 인터페이스는 order-service라는 이름의 서비스에서 GET /order-service/{userId}/orders 요청을 처리하는 클라이언트 역할

  • UserServiceImpl.java 수정
//추가
OrderServiceClient orderServiceClient;

//수정
@Autowired
public UserServiceImpl(UserRepository userRepository, BCryptPasswordEncoder passwordEncoder, Environment env, RestTemplate restTemplate, OrderServiceClient orderServiceClient) {
    this.userRepository = userRepository;
    this.passwordEncoder = passwordEncoder;
    this.env = env;
    this.restTemplate = restTemplate;
    this.orderServiceClient = orderServiceClient;
}

//변경
@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);

//        List<ResponseOrder> orders = new ArrayList<>();
        /* Using as rest template */
//        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();
        /* End */

        /* Using as FeignClient */
        List<ResponseOrder> orderList = orderServiceClient.getOrders(userId);
        /* End */
        userDto.setOrders(orderList);

        return userDto;
    }

참고
Rest Template을 사용하기 위해 추가했던 EnvironmnetrestTemplate는 불필요(=미사용)

📌 실행결과

  • 주문

  • 주문내역 확인

profile
새싹 개발자

0개의 댓글