Software Architecture

Reactive Programming #1 (관련 개념정리)

멋진그이름 2018. 10. 17. 18:00

최근 Reactive Programing이라는 개념이 많이 사용되고 있어서 관련하여 개념들을 정리를 해보려고 한다.

1. Event Driven

Reactive를 알기 위해서 먼저 Event Driven을 알아볼 필요가 있다.
Event Driven은 말 그대로 프로그램을 만들때 Event를 기반으로 동작하도록 하는 것이다.
가장 대표적으로 많이 사용되는 형태가 Event Bus 를 활용하는 것이다.

Apache Kafka가 마이크로 서비스 구축에서 대표적으로 Event Bus의 역할을 하고 있다고 볼 수 있다.
간단히 설명하면 다음과 같다.

<그림-1>

UI의 mouse clickm, key in등을 포함하여 (타시스템이 오는 Event가 될 수도 있다.) 어떤 Event가 발생했을 때 담당 Application은 해당 요청을 직접 보내고 응답을 기다리는 것이 아니라 Event를 생성하여 저장만 한다. 이후 그 Event에 대한 처리는 다른 Application에서 가져가서 처리하게 된다. 기본적으로 비동기식의 처리흐름을 갖게 된다.


2. Actor Model (Akka)

 위와 같은 환경에서 결국 Event Bus(혹은 큐)를 기준으로 Event생성자와 소비자는 분리되어 동작하며 불필요한 대기현상을 없애는 것이 핵심. 그리고 이때 병렬프로그래밍을 활용하게 되는데 동시성제어를 용이하게 만든 컨셉으로 Actor Model을 사용하기도 한다. Akka는 이를 이용하여 프로그래밍을 쉽게 할 수 있도록 한 프레임워크이다.

Actor Model의 핵심은 다음과 같다.

- 메시지를 보내고 잊는다.

- 메세지를 받는다.

기존의 동기식 처리도 가능하지만 비동기처리가 일반적으로 권장된다.


<장점>

- Scale-out 이 쉽다.

- lock, synchronized 등을 사용하지 않아도 된다. 

- 단순하다. 메시지를 보낸다/받는다


<단점> 

- 전체적인 로직의 파악이 어렵다. (코드 추적이 어렵다)

- 시스템 사용량이 많다. (쓰레드를 많이 생성한다.)

- 상태를 갖지 않는다. (장점이자 단점)

- 여전히 DeadLock이 발생할 여지가 있다.   


3. 동기 vs 비동기

 동기식 : 호출자는 상대방이 결과값 혹은 Exception을 줄때까지 이후 작업을 진행하지 않고 대기한다.

 비동기식 : 호출자는 상대방의 응답에 관계없이 호출 후 작업을 진행한다.

                 응답처리는 callback, Future 등을 통해서 처리한다.


간혹 동기 vs 비동기, Non-blocking vs Blocking, 싱글쓰레드 vs 멀티쓰레드의 개념을 헷갈리거나 혼재해서 사용하는 경우가 있는데 반드시 분리가 필요하다. (Non-blocking IO 는 또다른 주제이다.)


위와 같이 비동기 방식을 사용하여 Event 혹은 메시지를 생성/소비 하며 Applicatino이 동작하는 것이 Event Driven이라고 볼 수 있으며 현재 Kafka, Akka를 사용하여 많이 구성되고 있다.

Kafka를 제외하고 Akka로만 구성하는 경우도 있는데 내부에 Message Queue, MailBox등을 가지고 있기 때문에 가능하다. (권장하진 않는다)


4. Reactive programming

wikipedia의 정의에 따르면 다음과 같다.

reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change

핵심은 모든 것을 Data streams로 간주하여 처리하는데 이 흐름이 비동기로 처리된다는 것이다.

여기까지 보면 Event Driven 과 유사해보이는데 좀 더 자세히 살펴보면 반응형이라는 것이 추가된다.

검색창에 타이핑을 할 때 자동완성 검색어가 표시되거나 웹 페이지상의 표현내용이 새로고침없이 실시간으로 바뀌는 것이다.


<그림-2>

이를 구현하는 핵심은 비동기와 Observer  패턴이다.

- 어떠한 정보를 주고 받을때 비동기적으로 DataStream의 형태로 처리를 하여 메인작업에 영향을 주지 않아야 하며

- 값에 대해서 즉각 반응을 하기 위해서 Observer패턴으로 값을 관찰하여 연산을 수행한다.


쉽게 사용할 수 있도록 여러가지 라이브러리가 존재한다. (Rx... )


사실 이것보다 더 중요한 차이가 있다고 생각하는데

Reactive는 데이터의 흐름을 중심으로 프로그래밍을 한다는 것이다. 

과거에 Control,제어의 흐름을 중심으로 프로그래밍을 해왔다면 최근에는 Data의 흐름을 중심으로 프로그래밍하는 것으로 추세가 변하고 있다.

객체가 주고받는 메시지에 집중하고, 함수에 들어가는 In,Out에 집중하고, 데이터를 어떻게 변화시킬 지 map, filter, aggregation등에 집중한다.

빅데이터 분석에 반드시 필요한 데이터 수집부, 전처리부에서도 이러한 개념이 많이 필요하고, 시스템 자원의 효율화를 이끌어내기 위해서도 이러한 개념이 필요하여 최근 비슷한 컨셉이 각광받는 다고 생각한다. 

<그림-3>

이 자료를 보면 중간에 패러다임의 변화에 대해서 설명이 잘 되어 있다.(https://www.slideshare.net/jongwookkim/ndc14-rx-functional-reactive-programming)

위 자료도 몇 년전에 만들어졌고, 본인이 2015년경부터 데이터수집, DataFlow처리를 위해서 많은 OpenSource들(Nifi, Storm, Spark, Kafka, Flume)을 활용해 왔는데 프로그래밍 패러다임이 변하고 있다는 것을 막연히 느낄 수 있었다.

(http://icthuman.tistory.com/entry/Data-%EC%88%98%EC%A7%91-%EC%95%84%ED%82%A4%ED%85%8D%EC%B2%98-%EC%82%AC%EB%A1%80%EA%B2%80%ED%86%A0?category=541260)


Reactive와 유사한 흐름으로 Flow-based Programming 패러다임도 있다.


5. Flow-based Programming

위에서 언급했던 Flow-based Programming에 대해서 잠깐 살펴보면 위키피디아에 아래와 같이 정리가 되어있다.

In computer programmingflow-based programming (FBP) is a programming paradigm that defines applications as networks of "black box" processes, which exchange data across predefined connections by message passing, where the connections are specified externally to the processes

간단히 설명하면 어플리케이션을 블랙박스 프로세스들의 네트워크로 정의한다. 각 데이터들은 connection을 통해서 교환되며 블랙박스 프로세스들을 연결한다.

이러한 컨셉을 사용한 오픈소스가 Apache Nifi 이다. 최초 미국NSA에 의해서 개발되었으며 오픈소스로 공개되어 지금은 호튼웍스의 Data Flow 제품에 포함되어 있다.
거의 10년도 넘는기간 동안 NSA에 의해서 만들어졌다. 그만큼 컴포넌트나 지원하는 프로토콜도 많고 안정성,확장성이 뛰어나다. (그런데 오픈된 버전은 뭔가 좀 부족하다. 잘 죽는다..)

<그림-4>

직접 사용하지 않더라도 시간이 된다면 소스를 보고 분석하는 것을 추천한다.

구조가 잘 짜여있고 유연하게 설계가 되어있다.


6. Functional Programming , Immutable

Reactive Programming 을 보면 반드시 나오는 개념이 함수형 프로그래밍이다.

이 개념은 이미 많은 곳에서 사용되고 있다. (Scala, Spark, Akka 등)

그럴 수 밖에 없는 것이 최근 함수형 프로그래밍의 선두주자가 Scala인데 Spark, Akka, Kafka 등 왠만큼 병렬처리로 유명한 오픈소스들은 Scala로 개발되어 있기 때문이다.


함수형 프로그래밍은 결국 우리가 수학시간에 배웠던 함수를 프로그래밍으로 만드는 개념이다.

y=f(x) 

즉, 어떠한 입력에 대해서 내부계산에 의한 출력값을 주는 함수이다. 숨겨져있는 입/출력이 없고, 상태를 가지고 있지 않다. 

이러한 특징이 병렬처리에서 큰 장점을 가져온다.


- 함수내에서 입력을 변화시키는 것이 원칙적으로 불가능하기 때문에 immutable이 보장

- 매번 같은 수행결과를 보장할 수 있고 

- 복구/재처리가 쉽게 가능하다. 이는 원본을 수정하지 않기 때문인데 Spark를 사용해보면 filter, map등의 메서드등을 호출해도 원본에는 변화가 없으며 새로운 결과객체를 생성하여 돌려준다.

- Akka actor도 이와 유사한 개념으로 받아온 메세지에 수정을 가할 수 없으며, 다른 actor에게 메세지를 보낼 때는 새로운 메세지를 생성한다. 즉 immutable 하다.

- Scala에서 var 와 val의 차이를 찾아보면 좀 더 이해가 쉽다.

- Java도 primitive type이 immutable이긴 하지만 reflection 을 사용하면 객체레벨의 변경이 가능하기 때문에 완벽하진 않다.


7. Reactive vs Event Driven

에서 보면 수많은 사람들이 각각의 방법으로 설명을 하고 있다.

현재까지 이해한바로는 결국 여기에도 개념과 용어의 혼재가 있는 것으로 보인다.
Reactive programming 은 프로그래밍의 하나의 패러다임으로 보는 것이 바람직하고
Event Driven은 시스템을 구현하는 방법, 아키텍처에 가까워 보인다. (Event Driven Architecture라고 많이 이야기한다.)

Event도 결국 DataStream의 일종으로 처리한다면 비슷한 개념으로 볼 수 있으며 Reactive에서는 특정 구성요소등의 언급이 없는 것으로 볼때 맞지 않나 싶다.. (혼자만의 생각?)


참조