스프링 클라우드 어노테이션
https://javatechonline.com/spring-cloud-annotations-with-examples/
위의 문서를 번역한 내용이다.
@EnableEurekaServer
여러분이 스프링 부트 & 스프링 클라우드를 사용하여 마이크로서비스 기반 프로젝트를 개발한다면, 유레카(Eureka) 서버를 만들기 위해서 마이크로서비스의 메인 클래스에 적용하게 되는 첫 번째 어노테이션이 될 것이다. 이것은 이름에서 알 수 있듯이 유레카(Eureka) 서버를 가능하게 하는 것이다. 이 어노테이션을 서비스 레지스트리(Service Registry) & 디스커버리(Discovery)의 문맥(Context)에서 이 어노테이션을 사용할 것이다. 서비스 레지스트리(Service Registry) 개념에서 모든 마이크로서비스는 유레카(Eureka) 서버에 자신을 등록한다. 반면 서비스 디스커버리는 하나의 마이크로서비스가 유레카(Eureka) 서버를 통해서 다른 마이크로서비스를 찾는 개념이다. 유레카(Eureka)에 대해서 더 알고 싶으면 Netflix Eureka Service Registry & Discovery(https://javatechonline.com/how-to-register-discover-microservices-using-netflix-eureka/)를 방문해 보길 바란다.
더욱이, 스프링 클라우드 어노테이션인 @EnableEurekaServer를 사용할 때, 다른 마이크로서비스는 여기에 등록하고 서비스 디스커버리를 통해 서로 통신한다. 예를 들어 여러분의 애플리케이션/마이크로서비스를 유레카(Eureka) 서버처럼 동작하게 하고 싶으면, 아래와 같이 애플리케이션 메인 클래스에 @EnableEurekaServer를 적용하면 된다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@EnableEurekaServer
@SpringBootApplication
public class SpringCloudEurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudEurekaServerApplication.class, args);
}
}
@EnableEurekaClient
이 어노테이션은 마이크로서비스에서 서비스 디스커버리 개념과 관련이 있다. 서비스 디스커버리를 사용할 때, 하나의 마이크로서비스는 Eureka 서버를 경유하여 서로 통신한다. 그러므로 Eureka 서버에 등록하고 Eureka 서버에서 다른 서비스를 찾고자 하는 마이크로서비스는 이 어노테이션의 대상이 된다.
여러분의 애플리케이션/마이크로서비스를 Eureka 디스커버리 클라이언트로 동작하게 하기 위해서 여러분은 아래와 같이 애플리케이션의 메인 클래스에 @EnableEurekaClient를 적용해야 한다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@EnableEurekaClient
@SpringBootApplication
public class SpringCloudPaymentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudCartServiceApplication.class, args);
}
}
@EnableFeignClients
마이크로서비스에서 여러 서비스 간 통신은 프로듀서-컨슈머 개념으로 동작한다. 게다가 컨슈머(consumer) 서비스는 프로듀서(producer) 서비스에서 발행된 서비스를 소비(consume)할 것이다. 컨슈머쪽에 이 어노테이션을 적용한다. Feign은 선언적인 REST 클라이언트이다. 웹 서비스 클라이언트를 더 쉽게 작성할 수 있게 한다.
OpenFeign의 특징을 가지기 위해서 우리는 아래처럼 메인 클래스에 @EnableFeignClients를 추가적으로 적용할 것이다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
public class SpringCloudFeignStudentServiceApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFeignStudentServiceApplication.class, args);
}
}
@FeignClient(name="ApplicationName")
이 어노테이션은 @EnableFeignClients 어노테이션과 같이 사용된다. 컨슈머의 인터페이스 수준에서, @FeignClient 어노테이션을 적용하고 프로듀서 서비스/애플리케이션의 이름을 명시한다. 예를 들면, 아래 코드가 그 개념을 나타낸다.
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import com.dev.springcloud.feign.model.Book;
@FeignClient(name="BOOK-SERVICE")
public interface BookRestConsumer {
@GetMapping("/book/data")
public String getBookData();
@GetMapping("/book/{id}")
public Book getBookById(@PathVariable Integer id);
@GetMapping("/book/all")
public List<Book> getAllBooks();
@GetMapping("/book/entity")
public ResponseEntity<String> getEntityData();
}
더욱이, Book 서비스에서 데이터를 수신하기 위해서는, 필요한 곳에 BookRestConsumer를 auto-wire할 필요가 있다. 추가적으로 Feign 클라이언트와 관련된 더 많은 정보를 위해서 아래 글을 방문해라.
How To Implement Feign Client in Spring Boot Microservices? (https://javatechonline.com/how-to-implement-feign-client-in-spring-boot-microservices/)
EnableConfigServer
컨피그(Config) 서버는 연결되어 있는 각 마이크로서비스의 구성정보를 제공하는 중앙집중화된 구성 서버이다. 마이크로서비스가 컨피그(Config) 서버로 동작하기 위해서는 마이크로서비스 메인 클래스에 @EnableConfigServer 어노테이션을 적용해야 한다. 예를 들면, 아래 코드가 @EnableConfigServer 어노테이션의 사용방법을 설명한다.
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableConfigServer
public class SpringCloudConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringCloudFeignBookServiceApplication.class, args);
}
}
더욱이, 스프링 클라우드 컨피그(Config) 클라이언트는 스프링 클라우드 컨피그 서버에서 제공되는 서비스들을 사용하는 마이크로서비스들이다. How To Implement Spring Cloud Config Server In Microservices?(https://javatechonline.com/how-to-implement-spring-cloud-config-server-in-microservices/)를 방문해라.
Resilience4j의 Fault Tolerance 어노테이션
마이크로서비스 컨텍스트(Context)에서 Fault Tolerance는 장애에 내성이 있는 기술이다. 장애에 내성이 있는 마이크로서비스는 Fault Tolerant로 알려져 있다. 게다가, 마이크로서비스는 전체 애플리케이션이 비교적 잘 동작하도록 하는 장애 허용의 방법이어야 한다. 마이크로서비스 기반 애플리케이션을 운영할 때 다양한 종류의 장애가 발생할 가능성이 있다. 이 기술을 구현하기 위해서 Resilience4j는 여러 장애 유형에 기반한 다양한 모듈을 제공한다. 여기서 다양한 유형의 장애를 위한 어노테이션을 알아볼 것이다. Resilience4j API의 상세한 내용을 보고 싶다면 How To Implement Fault Tolerance In Microservices Using Resilience4j?(https://javatechonline.com/how-to-implement-fault-tolerance-in-microservices-using-resilience4j/) 글을 방문하라.
@RateLimiter
Rate Limiter는 특정 기간 동안의 요청 수를 제한한다. 스패머(spammers)로 부터 리소스를 보호하거나 SLA(service level agreement)를 만족하는 등, API가 처리할 수 있는 요청 수를 제한하는 다양한 이유가 있다. 우리는 명시적인 코드를 작성하지 않고 Resilience4j에서 제공되는 @RateLimiter 어노테이션을 통해서 이 기능을 구현할 수 있다. 예를 들어, 아래 코드는 메소드에 적용된 @RateLimiter의 기능을 나타낸다.
@GetMapping("/getMessage")
@RateLimiter(name = "getMessageRateLimit", fallbackMethod = "getMessageFallBack")
public ResponseEntity<String> getMessage(@RequestParam(value="name", defaultValue = "Hello") String name){
return ResponseEntity.ok().body("Message from getMessage() :" +name);
}
@Retry
마이크로서비스 'A'가 다른 마이크로서비스 'B'를 의존한다고 해보자. 마이크로서비스 'B'는 장애가 있는 서비스이고 성공 비율이 5060%밖에 안된다고 해보자. 하지만, 장애는 서비스가 다운되거나, 때로는 응답 값이 잘못될 수도 있고 간헐적인 네트워크 장애 등, 어떤 이유에서도 발생할 수 있다. 하지만 이런 경우에 마이크로서비스 'A'는 23번의 요청을 보낸다면 응답을 받을 가능성은 높아진다. 코드를 작성하지 않고 Resilience4j에서 제공되는 @Retry 어노테이션으로 이 기능을 구현할 수 있다. 예를 들어 아래 코드는 메소드에 적용된 @RateLimiter 기능을 나타낸다.
@GetMapping("/getInvoice")
@Retry(name = "getInvoiceRetry", fallbackMethod = "getInvoiceFallback")
public String getInvoice() {
logger.info("getInvoice() call starts here");
ResponseEntity<String> entity= restTemplate.getForEntity("http://localhost:8080/invoice/rest/find/2", String.class);
logger.info("Response :" + entity.getStatusCode());
return entity.getBody();
}
@CircuitBreaker
이름에서 알 수 있듯이 '서킷을 차단하는 것'. 마이크로서비스 'A'는 내부적으로 다른 마이크로서비스 'B'를 호출하고 있고 'B'는 장애가 있다. 말할 필요도 없이 마이크로서비스 아키텍처 'A'는 다른 마이크로서비스를 의존하고 마이크로서비스 'B'의 경우도 동일하다. 여러 마이크로서비스에서 연속적인 장애가 발생하지 않도록 하기 위해서, 마이크로서비스 'B'를 호출하지 않게 한다. 대신에 'Fallback 메소드'라고 불리는 더미(dummy) 메소드를 호출한다. 그래서 실제 서비스 대신 폴백(fallback) 메소드를 호출하는 것을 서킷을 차단(breaking the circuit)하는 것이라고 한다.
Circuit Breaker를 사용하여 장애 징후를 제거할 수 있다. 우리는 특정 코드를 작성하지 않고 @CircuitBreaker 어노테이션으로 쉽게 이 기능을 구현할 수 있다. 예를 들어 아래 코드는 메소드에 적용된 @CircuitBreaker의 개념을 나타낸다.
@GetMapping("/getInvoice")
@CircuitBreaker(name = "getInvoiceCB", fallbackMethod = "getInvoiceFallback")
public String getInvoice() {
logger.info("getInvoice() call starts here");
ResponseEntity<String> entity= restTemplate.getForEntity("http://localhost:8080/invoice/rest/find/2", String.class);
logger.info("Response :" + entity.getStatusCode());
return entity.getBody();
}
@Bulkhead
장애 허용(Fault Tolerance) 매커니즘 컨텍스트에서, 동시 요청 수를 제한하고 싶다면, Bulkhead를 사용할 수 있다. Bulkhead를 사용하여 특정 기간 안의 동시 요청 수를 제한할 수 있다. 코드를 작성하지 않고 @Bulkhead 어노테이션으로 쉽게 이 기능을 구현할 수 있다. 예를 들어 아래 코드는 메소드에 적용된 @Bulkhead 어노테이션의 사용 방법을 나타낸다.
@GetMapping("/getMessage")
@Bulkhead(name = "getMessageBH", fallbackMethod = "getMessageFallBack")
public ResponseEntity<String> getMessage(@RequestParam(value="name", defaultValue = "Hello") String name){
return ResponseEntity.ok().body("Message from getMessage() :" +name);
}
@Timelimiter
시간을 제한하는 것은 마이크로서비스가 응답할 시간을 제한하는 것이다. 마이크로서비스 'A'가 마이크로서비스 'B'에 요청을 보내고 마이크로서비스 'B'에 응답 시간 제한을 설정했다고 해보자. 만일 마이크로서비스 'B'가 제한된 시간 내에 응답하지 않는다면 장애가 발생했다고 판단될 것이다. 우리는 코드를 작성하지 않고 @Timelimiter 어노테이션을 통해 쉽게 이 기능을 구현할 수 있다. 예를 들면 아래 코드가 @Timelimiter의 사용법을 나타낸다.
@GetMapping("/getMessageTL")
@TimeLimiter(name = "getMessageTL")
public CompletableFuture<String> getMessage() {
return CompletableFuture.supplyAsync(this::getResponse);
}
장애 허용 어노테이션의 더 많은 예제는 How To Implement Fault Tolerance In Microservices Using Resilience4j?(https://javatechonline.com/how-to-implement-fault-tolerance-in-microservices-using-resilience4j/)의 글을 방문하라.