Ribbon이란?
- 클라이언트 사이드 load balancer
- 로드밸런싱 룰을 커스터마이징 가능
@LoadBalanced
RestTemplate, WebClient가 Bean으로 선언된 것에 한해서 @LoadBalanced 어노테이션을 붙이면 Intercept를 통해서 LoadBalancer를 끼어넣게된다. 따라서 알아서 로드 밸런싱을 하게된다.
이 때, RestTemplate, WebClient에 주는 주소는 IP, Port를 지우고 호출할 서버 군의 이름을 적으면 군에 해당하는 목록을 알아서 LoadBalancer에서 호출한다.
서버쪽 설정
서버에 들어올 경우 어디로 들어왔는지 콘솔에 로그를 남기도록 간단하게 구현했다.
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
package com.example.circuitbreakerbookstore;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
@RestController
@SpringBootApplication
public class CircuitBreakerBookstoreApplication {
private static Logger logger = LoggerFactory.getLogger(CircuitBreakerBookstoreApplication.class);
@RequestMapping(value = "/recommended")
public String readingList(){
logger.info("Access /recommended");
Random random = new Random();
List<String> bookList = Arrays.asList("Spring in ACtion (Manning)", "Cloud Native Java", "Learning Spring Boot");
int randomNum = random.nextInt(bookList.size());
return bookList.get(randomNum);
}
@RequestMapping(value = "/")
public String home(){
logger.info("Access home");
return "Welcome home";
}
public static void main(String[] args){
SpringApplication.run(CircuitBreakerBookstoreApplication.class, args);
}
}
클라이언트 쪽 설정
pom.xml
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Greenwich.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
@EnableCircuitBreaker
@RestController
@SpringBootApplication
//다른 자바 설정파일 처럼 @Configuration을 사용하면 안된다. @ComponentScan으로 해당 설정이 로딩될 경우 에러갈 발생한다.
@RibbonClient(name="say-book", configuration = SayBookConfiguration.class) //RibbonClient Annotaion은 Controller에 넣어야한다.
public class CircuitBreakerReadingApplication {
@Autowired
private BookService bookService;
@Bean
@LoadBalanced
public WebClient.Builder WebClient(){ //공홈에서는 RestTemplate를 썻지만 그냥 Webflux를 써봤다.
return WebClient.builder();
}
@RequestMapping("/to-read")
public String toRead(@RequestParam(value="name", defaultValue = "Aroon") String name)
{
return String.format("%s, %s",name,bookService.readingList());
}
public static void main(String[] args) {
SpringApplication.run(CircuitBreakerReadingApplication.class, args);
}
}
public class SayBookConfiguration {
@Autowired
IClientConfig ribbonClientConfig;
@Bean // 첫번째 Parameter가 True일 경우 https false일 경우 http. AppendString은 Ping 보낼 URL
public IPing ribbonPing(IClientConfig config){
return new PingUrl(false, "/");
}
@Bean //LoadBalancing Rule을 설정하는 것
public IRule ribbonRule(IClientConfig config){
return new AvailabilityFilteringRule();
}
}
@Service
public class BookService {
@Autowired
WebClient.Builder webClientBuilder;
/*
Hystrix Command 설정
commandKey 별로 Circuit Breaker 생성
fallbackMethod : 실패시 해당 메소드 실행
*/
@HystrixCommand(commandKey = "EX", fallbackMethod = "reliable")
public String readingList(){
return webClientBuilder.baseUrl("http://say-book").build().get() //get 방식
.uri("recommended") //endpoint
.retrieve() // body를 가지고 온다는 뜻
.bodyToMono(String.class)
.block()
;
}
public String reliable(){
return "Success Fallback";
}
}
Eureka와 연동하지 않을 것이므로 false를 하고 Server 군의 목록을 지정해준다.
ServerListRefreshInterval마다 IPing이 실행되며 서버 상태를 확인한다.
server.port= 8888
say-book.ribbon.eureka.enabled=false #유레카를 사용하지 않겠다.
say-book.ribbon.listOfServers=localhost:8090,localhost:9092 #서버군 목록
say-book.ribbon.ServerListRefreshInterval= 15000 #서버리스트 refresh 간격 ms
실행방법 :
1. 서버를 아래 mvn 명령어도 두개를 실행한다.
SERVER_PORT=9092 mvn spring-boot:run
SERVER_PORT=8090 mvn spring-boot:run
*만약 Maven이 설치되어 있지 않은 경우 homebrew insall maven 명령어로 설치해준다.
2. Client를 실행 후 접속해서 확인해본다.
참고
https://gunju-ko.github.io/spring-cloud/netflixoss/2018/12/14/Ribbon.html
https://spring.io/guides/gs/client-side-load-balancing/
https://piotrminkowski.com/2018/05/04/reactive-microservices-with-spring-webflux-and-spring-cloud/
'Spring' 카테고리의 다른 글
SpringBoot Hystrix 적용하기 (0) | 2020.03.01 |
---|---|
Hystrix 란 (0) | 2020.03.01 |