위의 코드는 stream()을 통해 함수형 프로그래밍을 위한 Stream 객체를 생성하고 있고,
map()을 통해 Stream 객체의 단어들을 prefix로 변형시키고 있다.
그리고 foreach를 통해서 prefix를 보고 map에 값을 추가하고 있다.
마이크로 서비스 아키텍처는 메시지 기반 통신을 선호한다.
리액티브 프로그래밍의 중요한 특징은 이벤트(또는 메시지)를 중심으로 애플리케이션을 구축하는 것이다.
몇 년 전부터 대부분의 애플리케이션에는 다음과 같은 특징이 있다.
수 초 단위의 응답 시간
여러 시간의 오프라인 유지 관리
소량의 데이터
시대가 바뀌면서 오늘 날에는 다음과 같은 특징이 있다.
밀리초 단위의 응답 시간
100% 가용성
데이터 볼륨이 기하 급수적으로 증가
지난 몇 년 동안 이러한 새로운 도전 과제를 해결하기 위해 다양한 접근 방식이 등장했다.
리액티브 프로그래밍은 실제로 새로운 현상은 아니짐나 문제를 성공적으로 해결한 접근법 중 하나다.
리액티브 방식으로 구축된 시스템은 보다 유연하고 느슨하게 연결되며 확장 가능하므로 개발하거나 변경이 쉽다.
이들은 장애에 훨씬 더 관대해 장애가 발생하면 큰 장애를 일으키지 않고 간결하게 대처한다.
리액티브 시스템은 반응이 뛰어나 사용자에게 효과적인 대화식 피드백을 제공한다.
리액티브 시스템의 특성
back pressure
전통적인 접근 방식
전통적인 접근 방식은 조사해서 주가가 변경됐는지 여부를 확인한다.
페이지가 랜더링되면 일정한 간격으로 최신 가격을 확인하기 위해 AJAX 요청을 주가 서비스에 보낸다.
웹 페이지에 주가 변동 정보가 없으므로 주가가 변경됐는지 여부에 관계없이 이러한 호출을 수행해야 한다.
리액티브 접근 방식
리액티브 접근 방식은 발생하는 이벤트에 반응할 수 있도록 관련된 여러 구성요소를 연결하는 것을 포함한다.
주가 웹 페이지가 로드되면 웹 페이지는 주가 서비스의 이벤트를 등록한다.
주가 변동 이벤트가 발생하면 이벤트가 트리거 돼 최신 주가는 웹 페이지에서 업데이트 된다.
리액티브 방식은 일반적으로 세 단계로 이뤄진다.
이벤트 구독
이벤트 발생
가입 취소
주가 웹 페이지가 처음 로드되면 주가 변동 이벤트를 구독한다.
특정 주식에 주가 변동 이벤트가 발생하면 이벤트의 모든 구독자에 대해 새 이벤트가 트리거된다.
웹 페이지가 닫히거나 새로 고쳐지면 구독자가 가입 취소 요청을 보낸다.
전통적인 접근 방식과 리액티브 방식의 비교
전통적인 접근 방식에서는 변경사항을 조사한다.
즉 주가 변동 여부에 관계없이 전체 시퀀스가 매분(또는 지정된 간격)마다 트리거된다.
리액티브 방식에서는 일단 이벤트에 등록하면 주가가 변경될 때만 시퀀스가 트리거된다.
구현
리액티브 스트림
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
public interface Subscription {
public void request(long n);
public void cancel();
}
Subscriber가 Publisher에게 subscribe하면 Publisher가 데이터 또는 시퀀스 전달.
전달하기 전에 Publisher는 Subscribe에 정의된 onSubscribe()를 호출하고, Subscriber는 request(n)를 호출하여 몇 개의 데이터를 보내달라고 요청.
이때 Subscription을 사용하는데, Request(n)를 호출하여 데이터 전송 요청을 하게 되면 Publisher에서는 0에서 N개의 데이터 또는 시퀀스를 Subscriber에 전달.
이 과정에서 에러가 발생하면 onError()를 호출, 데이터(시퀀스)전달이 완료가 되면 onComplete()호출.
Subscriber가 Publisher에 Request하는 과정을 보통 Back-Pressure라고 표현하는데, Push하는 데이터(시퀀스)의 흐름을 제어할 수 있다. Request(1)을 호출하면 1개만 보내도록�요청할 수 있고, Request(MAX)를 호출하면 최대값에 해당하는 데이터를 요청.
Publisher
끊임없이 data를 생성한다. Mono/FLux가 Spring Reactor가 구현한 Publisher의 역할로 이해할 수 있다.
Subscriber
data를 요청해서 받아간다. reactive programming의 핵심 중 하나는 back pressure이다.
data를 소비하는 쪽에서 충분히 여유가 있을 때 요청하여 받아가는 형태로 이해할 수 있다.
Subscription
publisher와 subscriber사이에 위치하여 이벤트를 통하여 data를 전달해 주는 역할로 이해할 수 있다.
리액터 프레임워크
Mono : 0~1개의 결과만을 처리하기 위한 Reactor의 객체
Mono<String> noData = Mono.empty();
Mono<String> data = Mono.just("foo");
어플리케이션이 RestTemplate를 생성하고 URI, HTTP 메소드 등의 헤더를 담아 요청한다.
RestTemplate은 HttpMessageConverter를 사용하여 requestEntity를 요청메시지로 변환한다.
RestTemplate은 ClientHtttpRequestFactory로 부터 ClientHttpRequest를 가져와서 요청을 보낸다.
ClientHttpRequest는 요청메시지를 만들어 HTTP 프로토콜을 통해 서버와 통신한다.
RestTemplate은 ResponseErrorHandler로 오류를 확인하고 있다면 처리로직을 태운다.
ResponseErrorHandler는 오류가 있다면 ClientHttpResponse에서 응답데이터를 가져와서 처리한다.
RestTemplate은 HttpMessageConverter를 이용해서 응답메시지를 java object로 변환한다.
어플리케이션에 반환된다.
RestTemplate 사용
@Configuration
public class RestTemplateClient {
@Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
RestTemplate을 생성할 때는 builder를 통하여 생성해 줄 수 있다.
builder를 통해 생성하고 스프링 빈으로 사용할 수 있도록 설정해준다.
public class RestTemplateTestClass {
@Autowired
RestTemplate restTemplate;
public TestClass(RestTemplate restTemplate){
this.restTemplate = restTemplate;
}
public String getSthFromServer(){
return restTemplate.getForObject("https://example.com",String.class);
}
}
RestTemplate을 사용하기 위해서는 restTemplate.메소드명() 을 사용하면 된다.
// RestTemplate의 처리방식 예제 코드
// 요청 당 20개의 RestTemplate client를 만들고, 최대 50개까지 증가할 수 있다.
@Configuration
public class RestTemplateConfig {
public RestTemplate getRestTemplate(int defaultMaxPerRoute, int maxTotal) {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setDefaultMaxPerRoute(defaultMaxPerRoute);
connManager.setMaxTotal(maxTotal);
HttpClient client = HttpClientBuilder.create().setConnectionManager(connManager).build();
HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory(client);
factory.setConnectTimeout(3000);
factory.setReadTimeout(3000);
return new RestTemplate(factory);
}
@Bean
public RestTemplate coffeeRestTemplate() {
return getRestTemplate(20, 50);
}
}
요청을 처리할 쓰레드가 있으면 아무런 문제가 없지만, 쓰레드가 다 차는 경우 이후의 요청은 Queue에 대기하게 된다.
대부분의 문제는 네트워킹이나 DB와의 통신에서 생기는데 이런 문제가 여러 쓰레드에서 발생하면
가용한 쓰레듯수가 현저하게 줄어들게 되고, 결국 전체 서비스는 매우 느려지게 된다.
WebClient
Web Client란?WebClient는 스프링 5.0에서 추가된 인터페이스다.
스프링 5.0 이전에는 비동기 클라이언트로 AsyncRestTemplate를 사용했었다.
하지만 스프링 5.0 이후부터는 Deprecated 되었다.
스프링 5.0 이후는 WebClient 사용을 권장한다.
WebClient는 다음과 같은 특징이 있다.
WebClient 특징
싱글 스레드 방식을 사용
Non-Blocking 방식을 사용
JSON, XML을 쉽게 응답받는다.
각 요청은 Event Loop내에 Job으로 등록이 된다.
Event Loop는 각 Job을 제공자에게 요청한 후, 결과를 기다리지 않고 다른 Job을 처리한다.
Event Loop는 제공자로부터 CallBack으로 응답이 오면, 그 결과를 요청자에게 제공한다.
WebClient는 이렇게 이벤트에 반응형으로 동작하도록 설계됐다.
그래서 반응성, 탄력성, 가용성, 비동기성을 보장하는 Spring React 프레임워크를 사용한다.
또한, React Web 프레임워크인 Spring WebFlux에서 Http Client로 사용된다.
댓글