Reactive Stream 간단 사용

Reactive Stream 을 사용하기 위해서는 다음과 같은 것을 구현해줘야 한다.

  • Publisher
  • Subscriber
  • Subscription

한가지 문제가 있다. 이것을 구현하기가 쉽지가 않다. 특히 Publisher 의 경우에는 Reactive Stream 의 기본 아이디어만 가지고 간단하게 구현할 수가 없다. 어떤 것을 Publisher 할지도 영향을 주는것도 문제지만 Reactive 의 Spec 을 마춰짜야 하는데 이게 쉽지가 안다.

Publisher 를 간단하게 구현 방법이 없을까? SubmissionPublisher 를 이용하면 간단하게 사용해 볼 수 있다.

이 문서는 SubmissionPublisher 를 이용해 어떻게 Reactive Stream 을 구현하는지를 설명한다.

SubmissionPublisher Class

이 클래스를 간단히 살펴보자.

이 클래스는 Publisher 인터페이스를 구현한 구현체 클래스 이다. 하지만 아주 간단하지만은 않다.

SubmissionPublisher 클래스가 가지는 메소드가 꽤 있다. 자세히 보면 get 혹은 is 로 시작하는 메소드가 많음을 알 수 있다. 이 클래스들은 Publisher 의 상태 정보를 체크하기 위한 것들이다. 반대로 set 으로 시작하는, 그러니까 뭔가를 지정하는 메소드는 많지가 않다.

이 클래스에 설명은 다음과 같이 시작한다.

Flow.Publisher that asynchronously issues submitted (non-null) items to current subscribers until it is closed. Each current subscriber receives newly submitted items in the same order unless drops or exceptions are encountered. Using a SubmissionPublisher allows item generators to act as compliant reactive-streams Publishers relying on drop handling and/or blocking for flow control.

Flow.Publisher 는 Null 이 아닌 제출된 아이템들을(item) 현재 구독자에게 닫힐때까지 비동기적으로 발행한다. 각각의 현재 구독자들은 삭제 또는 예외가 발생하지 않는한 새롭게 제출된 아이템들을 같은 순서로 받는다. SubmissionPublisher 를 사용하는 것은 아이템 생성기를 흐름제어를 위해 삭제 핸들링과 블럭킹을 필요로하는 reactive-streams Publisher 와 호환되는 것처럼 행동하게 해준다.

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/concurrent/SubmissionPublisher.html

어찌되었든 간에 이것을 이용하면 Reactive Streams Publisher 처럼 만들어 준다 것에 주목해야 한다.

SubmissionPublisher 사용하기

사용방법은 아주 간단하다.

SubmissionPublisher 의 객체를 하나 생성한다. 그리고 Reactive Stream 이 Publisher 에 요구하는 subscribe 메소드를 호출해 준다.

하지만 subscribe() 메소드는 인자값으로 Subscriber 클래스를 요구한다. Subscriber 클래스는 Interface 클래스여서 구현체 클래서를 넣어야 한다. 다양한 방법이 있지만 여기서는 다음과 같이 작성해 본다.

Reactive Stream 이 요구하는 Subscriber 에 요구 메소드들이(onNext, onError, onComplete) 모두 나온다. 다음과 같이 모두 구현해 준다.

위와같이 구현을 해주면 된다. 여기서 중요한 것이 존재하는데, Subscription 객체 변수를 반드시 써야 한다는 것이다. onSubscribe, onNext 메소드에서 공통으로 보이는 subscription.request(1) 이 바로 그것인데 onNext 메소드에 이를 사용하기 위해서 Subscriber 구현체에서 멤버변수를 만들어두고 onSubscribe 시에 주입된 객체변수를 할당해 준다.

이제 다음과 같이 publisher 의 submit 메소드를 사용해 아이템을 넣어준다.

submit 메소드는 아이템을 제출해 비동기적으로 Subscriber 에게 생성해준다. SubmissionPublisher 의 비동기는 Thread 를 이용한다. 그래서 모든 Thread 가 완료되기를 Main Thread 를 몇초간 중지줘야해서 Thread.sleep(100) 코드가 필요하다.

모든 비동기 제출이 완료되면 제출자를 닫아준다. publisher.close() 다.

이렇게 완료된 전체 코드는 다음과 같다.

Reactive Stream 을 어떻게하면 간단하게 사용해볼까. Reactive Stream 이 요구하는 최소사항을 어떻게하면 직접 사용해볼 수 있을까? 그렇다면 SubmissionPublisher 클래스를 활용하라.

자바 Reactive Stream 구현체

자바(Java) 에서 Reactive Stream 구현체를 살펴본다.

이전 글에서 Reative Stream 의 기본 아이디어를 설명했었다.

  • Flow control
  • Publisher – Subscriber pattern

또한 이 아이디어를 위한 포맷은 Publisher – Subscriber 이다. Reactive Stream 에서 이 모델은 1) Subscriber 가 Publisher 에게 가입을 요청하면 2) Publisher 는 Subscriber 와 통신을 위한 채널인 Subscription 을 생성하고 이를 통보한다. 이는 다음 그림과 같이 묘사할 수 있다.

출처:https://dzone.com/articles/reactive-streams-in-java-9

여기서 주목해야할 것이 Subscriber 는 Subcription 을 통해서 데이터를 요청하고 받게 된다는데 있다.

java.util.concurrent.Flow

자바에서 구현은 위 클래스에 구현 되어 있다. 그리고 이 클래스 첫줄에 다음과 같이 설명이 시작된다.

Interrelated interfaces and static methods for establishing flow-controlled components in which Publishers produce items consumed by one or more Subscribers, each managed by a Subscription

흐름 제어 컴포넌트를 설정하기 위한 (밀접하게) 상호연관된 인터페이스(Interface) 와 스태틱 메소드(Static method)

java.util.concurrent.Flow

주석에 내용에 등장하는 컴포넌트 Publisher, Subscriber, Subscription 이다. Flow.class 는 다음과 같이 구성되어 있다.

Publisher

FunctionalInterface 인게 눈에 뛴다. 이것은 오직 한개의 메소드만을 가진다는 것과 Lambda 식을 지원하게 된다. subscribe 메소드는 인자로 Subscriber 를 받는다.

중간에 주석에 주목할만한 내용이 나온다.

Subscribers may enable receiving items by invoking the request method of this Subscription, and may unsubscribe by invoking its cancel method.

Subscriber 는 이 Subcription 의 request 메소드를 호출함으로써 아이템들을 수신할 수 있으며 cancel 메소드를 호출함으로써 가입이 해제된다.

Subcriber 는 Subscription 의 request 메소드를 호출함으로써 데이터(아이템)을 수신한다는 것이다.

Subscriber

4개의 메소드를 가지고 있다. 이 메소드들이 다루는 것이 무엇인지를 아는것이 제일 중요하다. 각 메소드의 주석에는 Subscription 이 항상 나온다.

Method invoked with a Subscription’s next item. If this method throws an exception, resulting behavior is not guaranteed, but may cause the Subscription to be cancelled

Subscription’s 의 다음 아이템을 호출하는 메소드. 만약 이 메소드가 예외를 발생시키면, 최종 결과를 보장하지 않지만, Subcription 은 취소될 수 있다.

onNext

Subscriber 가 다음 아이템을 호출하는데 Publisher 를 대상으로 하지 않고 Subscription 을 대상으로 한다는 것을 꼭 기억해야 한다.

Subscription

Publisher 와 Subscriber 를 연결하는 메시지 제어를 하는게 목적이다 .request 메소드는 Subcription 에 대해 현재 처리되지 않은 요청을 위한 아이템의 개수를 추가하게 된다. 몇개의 아이템을 요청할 것인지를 요청하는 것이다.

SubmissionPublisher

SubmissionPublisher 는 non-null 아이템을 생성해주는 Publisher 이다. 생성을 해준다고 해서 자동으로 생성해주는 것이 아니라 아이템을 제출해야 한다. 이 제출된 아이템은 순차적으로 Publish 해준다.

submit

이 메소드는 비동기적으로 Subscriber의 onNext 메소드를 호출함으로써 인수로 받은 아이템을 Subscriber 에 아이템을 발행한다.

Reactive Stream Family

Reactive Stream 에는 다양한 프레임워크, 라이브러리들이 존재한다. 이것들은 각자의 고유한 이름과 특징을 가지고 있다. 어떤 것들이 있는지 간단하게 정리해 본다.

Reactive Streams

저수준의 규약으로 주로 자바 인터페이스(Interface) 로 구현 되어 있다. 명시적인 back-pressure 으로 Publisher 와 Subscriber 의 기본 빌딩 블록을 표현한다. Java 9 에서 java.util.concurrent.Flow 로 구현되어 있다.

RxJava

이것은 Reactive Extension 이다. Neflix 에서 개발해 오픈소스로 전화하면서 세상에 알려졌다. ReactiveX 라고도 불린다. 브릿지 Reactive Stream 이라고 말하기도 하는데, Reactive Stream 을 위한 타입 전환을 지원한다.

Reactor

자바 프레임워크 이다. Spring 배포로 유명한 피보탈(Pivotal) 에서 제작해 오픈소스로 배포하고 있다.

Spring Framework

HTTP 서버/클라이언트을 툴을 비롯해 리액트 기능을 가지고 있다. Spring 의 특징은 어노테이션을 이용한 기능 주입등을 사용할 수 있다. 비동기 HTTP 서버에서 구동가능하다. (Netty, Undertow, Tomcat 8 이상)

Akka Stream

Actor Model 을 기반으로 애플리케이션 개발을 위한 Java, Scala 를 위한 툴킷이다.

이것을 정리하는 이유가 있다. Reactive Stream 은 스펙이기 때문이다. 이 스펙을 만족하면 Reactive Stream 이라고 할 수 있다. 물론 자바의 저수준 인터페이스 이름도 Reactive Stream 이라 헷깔릴 수 있지만 Reactive Stream 스펙을 만족하는 다양하고 라이브러리, 프레임워크, 툴킷들이 있다.

Reactive Stream 기본 아이디어

자바 9에서 소개된 Reactive Stream 은 비동기(Asynchronously), 논 블럭킹(Non-Blocking)이 특징이다.

그렇다면 왜 이것이 특징이되어야만 했나. 이 특징에 반대되는 개념은 블럭킹(Blocking) 이며 블럭킹을 발생시키는 요인은 동시식 요청인 것이다. 결과적으로 이 둘을 해결해야만 하는 과제를 안고 있었는데 Reactive Stream 이 이 문제를 해결했다고 볼수 있다.

이 글에서 Reactive Stream 에서 핵심 특징의 아이디어를 아주 가볍게 고찰해 본다.

일반적인 데이터 처리 흐름.

데이터를 처리를 어떻게 할까? 좀 더 정확하게 표현을 한다면 데이터 처리를 어떠한 형태를 가지고 할까?

대부분 위와 같은 형태를 가진다. 첫째로 데이터 처리를 요청하는 무언가가 있다. 이를 ‘데이터 처리 요청자’라고 하자. 둘째로 데이터를 처리하는 무언가가 있다. 이를 ‘데이터 처리자’라고 하자. 데이터 처리 요청자는 데이터 처리를 담당하는 데이터 처리자에게 데이터를 던진다. 그러면 데이터 처리자는 요청이 들어온 순서대로 데이터를 처리하게 된다.

그런데, 데이터 처리 요청자가 갑자기 한꺼번에 많은 데이터를 던지면 어떻게 될까? 아니면 데이터 처리 요청자 여러개가 하나의 데이터 처리자에게 데이터를 던지면 어떻게 될까?

데이터 처리자는 일을 처리하는데 필요한 자원이 한정되어 있다. 순차적으로 일을 처리하는 데이터 처리자는 한꺼번에 밀려드는 데이터를 쌓아두게 된다. 또, 데이터 처리 요청자는 요청한 데이터가 모두 처리되어 돌아올때까지 기달려야 한다.

이렇게 되면 전체적으로 시스템의 성능이 느려지게 된다. 모든 데이터처리가 동기화되어서 작동되는 방식이며 데이터 처리자가 모든 것을 처리할때까지 요청자는 다른 요청을 모두 차단(Block) 하게 된다.

Reactive Stream 은 이러한 문제를 해결하고자 하는게 핵심 포인트라고 보면 된다.

기본 아이디어 – 데이터 흐름을 뒤집다.

데이터 처리자의 자원은 한정되어 있다. 자원이 할당되어져야 데이터 처리자는 데이터를 처리하게 된다.

그렇다면 데이터 처리자가 자원이 할당 되었을때에 데이터 처리자가 데이터 요청자에 데이터를 가지고 가면 될 것이 아닌가? 이렇게 하기 위해서는 데이터 처리자가 데이터 요청자를 알고 있어야 한다.

이러한 아이디어는 Reactive Stream 에서는 다음과 같은 것이 핵심적인 키 포인트로 정리가 된다.

  • Flow Control
  • Publish-Subscribe pattern

기존의 데이터 흐름 제어를 완전히 뒤집은 새로운 아이디어다.

Reactive Stream 작동 방법

Reactive Stream 에서 구현은 기본 아이디어를 바탕으로 하지만 약간 다르다.

Reactive Stream 에서는 Subcriber 가 Publisher 에게 직접적으로 데이터를 요청하는 것이 아니라 어느정도 데이터 처리가 가능한지에 대한 정보를 알려준다. 그렇게되면 Publisher 는 처리가 가능한 정도만 데이터를 보내게 된다.

Publisher 와 Subscriber 사이에 정보를 교환하기 위한 일종의 채널이 필요하게 된다. 이러한 채널을 Subscription 이라고 한다. 이러한 Subscription 은 Subscriber 가 Publisher 에 가입하는 순간 생성된다.

Subscription 은 Publisher 와 Subcriber 간의 통신 채널이다. Subcriber 는 Subscription 을 통해 몇개나 받을 수 있는지에 대한 정보를 통지한다. 그러면 Publisher 는 다음과 같은 정보를 Subcriber 에게 전송한다.

  • Subscribed with subscription
  • error
  • complete
  • cancel

Publisher 가 complete 나 cancel 을 통지하면 채널은 해제되고 Publisher 와 Subscriber 관계는 끝이 난다.

Back-Pressure

역압이라고 번역하던데, 의미를 파악하기 어려운 용어들이다. 이 용어에 대한 정의는 다음에서 찾을 수 있다 .

This back-pressure is an important feedback mechanism that allows systems to gracefully respond to load rather than collapse under it

Back-Pressure 는 중요한 피드백 매커니즘인데, 대량으로 데이터를 수신한 Subscriber 가 과부하에 응답을 못하거나 시스템이 다운되지 않고 정상적으로 응답하게 해준다.

https://www.reactivemanifesto.org/glossary#Back-Pressure

Back-Pressure 는 매커니즘인데, 앞에서 설명한 어쩔땐 pull-based 혹은 push-based 로 작동되게하는 것이 바로 Back-Pressure 다.

여기서 한가지 중요한 포인트가 있다. Back-Pressure 는 성능을 보장하지 않는다는 것이다. 시스템을 보호해 어떻게든 응답을 보장하는게 목적인 것이지 Subscriber 가 응답을 빠르게 해야한다는 것은 아니다.

Gradle 설치

이 문서는 Gradle 설치에 관한 것이다. 설치할 시스템은 Linux 이다.

설치(Installation)

Gradle 공식 페이지에서 다운로드가 가능하다.

다음과 같이 압축을 해제하면 기본적으로 설치는 끝난다.

환경변수 설정

쉘 환경변수는 두가지로 다음과 같이 설정을 해줘야 한다. 쉘 환경변수는 계정에만 적용할 수도 있고 전체 시스템에 적용할 수도 있다.

GRADLE_HOME

GRADLE_HOME 은 Gradle 설치한 홈 디렉토리를 지정하면 된다.

GRADLE_USER_HOME

이 디렉토리는 다운로드한 리소스, 컴파일된 빌드 스크립트들을 캐쉬하는데 사용된다. 의존성 라이브러리를 다운로드 하는 디렉토리이기도 하다. 다음과 같이 디렉토리를 생성해 준다.

이제 이것들을 모두 쉘 환경변수로 등록해 준다.

배민의 배신??

배달의 민족이 딜리버리 히어로에 매각(?) 됐다. 국내 배달앱 1위를 달리는 배달의 민족을 인수한 딜리버리 히어로는 이미 요기요 배달앱을 인수해 한국 시장에 진출한 상황이다. 이런 상황에 배달의 민족을 인수한 딜리버리 히어로는 국내시장에서 사실상 독점적 지위에 올랐다라고 밖에 말할 수 밖에 없다.

그런데, 논란은 배달의 민족이 독일계 딜리버리 히어로에 매각한 것을 두고 ‘배신’ 이라는 성토가 이어지고 있다는데 있다. 국내에 자영업자들의 고열을 빨아 성정한 배달의 민족… ‘민족’ 이라는 어찌보면 ‘국내용’ 간판을 단것도 사람들의 마음을 아프게 한것도 있을지도 모른다.

이런 논란을 뒤로하고 이것을 바라보는 내 시각을 정리해 본다.

전세계 배달앱 시장에서의 한국.

한국 만큼 24시간 잠을 안자는 민족도 없을 것이다. 유튜브에 유행하는 한국 거주 외국인들의 말을 들어봐도 그렇다. 영미, 유럽등 다른 나라에서는 안전상의 이유도 있지만 대부분 밤9시나 10시쯤이면 가게가 문을 닫는다. 하지만 한국은 24시 편의점이 존재만으로도 그것을 입증한다.

그것뿐만이 아니라 야식이라는 문화도 한몫한다. 새벽까지 잠을 안자는 습성,, 그 중간에 뭔가를 먹어야겠다는 욕망.. 한국의 종특이라고 할수도 있다. 그런 문화를 기반으로 하는 배달 문화는 당연하다.

실제로 딜리버리 히어로에서의 한국 시장에서 직위는 어마어마하다. 딜리버리 히어로는 전세계를 기반으로 배달시장에 진출했지만 한국 시장에서 벌어들이는 수익은 다른 시장에서의 수익을 훨씬 높다.

한마디로 다른 세계시장에서 한국 시장에서의 수익은 단연 원탑이다.

자영업자들의 고열을 빨아 외국 자본에 배만 불린….

내가 봤을때 자영업자의 고열을 빤것은 맞는 말이기도 하다. 내가 자주가는 중화요리집이 있어 그 분들과 자주 이야기를 하는데 배달앱을 별로 좋아하지 않았다. 문제는 수수료. 시장의 독점적 지위를 이용해 수수료를 강제하는 행위는 명백한 독과점 행위다.

문제는 외국자본이라는 건데, 외국자본뿐만 아니라 국내자본등이 이번 거래로 인해서 어마어마한 돈을 벌었다는 것인데 이것에 대한 비판도 있다.

이 부분에 대해서 배민을 욕할 수 있나… 내가 사장이며 사업을 넓히긴 바란다면 당연히 돈이 필요할 것이며 투자자금이 절실하게 된다. 배민 대표도 당연히 사업을 확장하면서 이런 고민을 했을게 분명하다. 사업을 하는 대표라면 이런 고민을 다하게 된다.

이런때에 외국자본이 배달의 민족을 먼저 알아보고 투자금을 내준 것일 뿐이다. 먼저 알아보기도 했거니와 배달의 민족 대표가 투자 요청을 공개적으로 했을 수도 있지만 어쨌든 결정은 투자자가 하게된다. 외국자본은 시장의 흐름을 분석해 배달의 민족에 선 투자를 했을 뿐이다.

“외국 자본의 배만 불린..” 기사는 그래서 내게는 좀 다른 생각을 갖게 한다. 그렇다면 한국 자본은 뭘했다는 건가…. 아무리 생각을 해봐도 한국자본은 배달의 민족에 투자를 결정할 능력이 없었다는 거다.

배달의 민족 대표…

내가 배민의 대표였다면 이런 결정을 할 것인가…. 이런 질문에 대부분의 사람들은 도덕주의적인 관점에서만 바라 보게 된다. 남읠 비판하기에 가장 좋은 도구는 도덕성일테니까.

민족을 배신한 대표…. 글쎄다…. 일단 돈을 받은것도 아닌 딜리버리 히어로에 한 자리를 받았을 뿐이다. 물론 지분이 있기 때문에 지분을 처분하면 현찰이 되겠지만, 어쨌든 지분을 가지고 있는 상태… 그리고 딜리버리 히어로에서 경영자로서 한 자리..

내가봤을때 내가 그 대표였다면 이렇게 하지 않았을 것이다. 그것 확실하다. 그 이유는 아마도 국가, 지역사회, 커뮤니티와 지역적이 고유한 미덕, 문화등을 무시한 자본적인 사고를 별로 좋아라하지 않기 때문이다.

현실을 살아가는데 자본은 중요하다. 부정할 수 없는 현실성이 아니겠나. 그렇다고 그것이 내가 살아가는데 중요한 가치까지 넘어설수는 없다고 생각한다.

배민의 대표는 자신의 경영자로서 딜리버리 히어로로 한 자리를 얻었겠지만 그런 판단에는 그동안에 그런 회사를 키워준 사회에 대한 고려가 과연 있었겠나 하는 생각…. 그런 생각을 했을까….

독과점…

신자유주의 시장이라는 미국. 다른걸 제외하고 자본을 운영하는 미국을 보고 있노라면 무섭기도 하다. ‘반독점법안’ 이 강력함은 말을 하지 않아도 될 정도고 문제는 자본을 어지럽힌 자는 같은 자본으로서 벌을 내리는 제도를 운영하는 미국…

미국에서 이런 일이 벌어졌다면 어떻게 될까… 미국 정보는 이번 거래를 승인할까? 미국 뿐만 아니라 유럽에서 일이 벌어졌다면 과연 유럽은 이 거래를 승인할까?

굳이 전문가가 아니라고 하더라도 내가 본 몇년간 배달앱 시장은 독점적 시장으로 발전해가고 있었다. 1위와 2위가 이미 시장의 70% 이상을 넘어서고 있는 것이 증명한다.

이런 상황에서 국가인 한국정부는 뭐했냐고 하고 싶지만 지금의 자유시장, 그것도 한국의 자유시장에서 국가의 힘은 그렇게 크지 않고 어찌할 수도 없었을 것이다.

방법은 그져 한국자본이 배달의 민족과 같은 회사에 조기 투자를 하는 수밖에.. 아니면 지분을 가지던가….

이래저래 생각을 해봐도 어렵구만..

스트림(Stream)

자바(Java) 세계에서 언제부터인지 스트림(Stream) 이라는 단어를 목격하게 되었다. 내 기억으로는 Java 8 에서부터 시작된 것 같은데 난데없는 이 단어가 왜 그렇게 핵심이 되었는지가 의문이였다. 도대체 왜 스트림(Stream) 이냐 하는 질문에 대한 대답을 듣기도 어려웠던 시절이기도 하다. 그져 사용하는 방법을 익히는데에 몰두하는 모습만 목격됐을 뿐이다.

java.util.stream

스트림(Stream) 에 대한 정의는 다양하다.

데이터 소스(Array, List) 로부터 흐름을 가지는 데이터의 집합체이며 통합연산을(bulk processing) 통해 데이터를 변형시키고 최종적으로 소비자가 그 데이터를 소비하도록 한다.

스트림을 다루게 되면 항상 다음과 같은 데이터 소스들을 만나게 된다. 모두 데이터의 집합체들이다.

  • Array
  • List

하필 왜 데이터 집합체들일까

컴퓨터 알고리즘 필요성과 유사한 스트림(Stream)

난데 없이 컴퓨터 알고리즘을 꺼내온 이유가 있다. 컴퓨터 알고리즘을 공부할때에 가장 먼저 만나는 것이 정렬(sort)문제이다. 그런데, 이런 질문을 하게된다.

왜 하필 정렬부터 인가?

이에 대한 대답은 간단다.

Compute 연산과 Memory 공간을 절약하기 위해서..

컴퓨터가 중복된 데이터를 어떻게 찾아낼까? 정렬을하면 쉽게 해결된다. 정렬된 데이터가 아니라면 모든 데이터를 비교해야 하지만 정렬할 경우에 같은 위상을 같은 데이터 값이 나오게 되는데 이를 하나만 남기고 지우면 간단해 진다.

이렇게 함으로써 Memory 공간도 절약하게 되고 이렇게 중복되지 않은 데이터를 가지고 Compute 연산을 할 경우에 당연히 그에 들어가는 비용도 줄게 된다.

자바에서 스트림도 이와 유사하다.

자바에서 데이터를 다루는 방법은 다양한다. 이는 데이터 소스를 통해서 다루어지는데, 이 데이터 소스를 간단하게 타입(Type) 이라고 생각해보자. 정수형, 문자열 등은 가장 단순한 타입이다.

이런 타입들은 단 하나의 데이터만 저장하고 있을 뿐 “데이터들” 을 가지고 있지 않다. Compute 연산 알고리즘에서는 여러 데이터들의 집합을 다룬다. 컴퓨터가 가지고 있는 데이터들이란 집합을 이야기 한다. 따라서 데이터 소스라고하면 “데이터들” 을 지칭하며 자바에서 이런형태의 데이터 타입은 Array, List 가 대표적이다.

그럼 이런 생각을 하게된다. 데이터 집합체들을 어떻게 하면 빠르게 중복을 제거하고 연산을 하게 만들 것인가? 과거에 For loop 문과 같은 것을 이용해서 조건식을 붙이면서 사용을 할 수도 있다.

람다(Lambda)

연속된 데이터들을 다루기만 할 거라면 단순하게 For loop 문을 이용하면 된다. 만일 이런 생각을 하게 된다.

연속된 데이터를 처리할때에 병렬을 이용해서 처리보자.

For loop 문에서 병렬처리는 쉬운게 아니다. Thread 를 이용할 수도 있지만 이건 동시성 프로그래밍이지 병렬은 아니다.

이를 위해서 자바 8 에서는 람다(Lambda) 를 도입했다. 이것에 대한 정의를 보면 함수형 프로그래밍(Funtional Programming) 이라는 말을 자주 접하게 되는데 병렬연산을 가능하게 하는 부분이다.

자바 8 스트림은 이 람다를 기반으로 한다. 결국에 스트림은 벌크 프로세싱(Bulk Processing) 을 람다를 사용해 구현하여 빠른 고속 데이터 처리가 가능하다.

스트림 – 흐른다.

스트림의 중요한 특징은 흐름이다. 프로그래밍에서 데이터를 다룰때 흐름 없이 다루는 경우도 많다. 앞에서 컴퓨터가 다루는 데이터는 “데이터들” 이라고 했는데, 이것들을 흐름을 가지고 연산을 수행하는게 스트림이다.

“흐른다” 라는 말을 수도관을 떠올리게 한다. 왼쪽에 물을 흘려보내면 오른쪽으로 물이 나온다. 데이터를 왼쪽에서 흘려보내면 오른쪽으로 물이 나온다. 만일 이 물이 설탕물로 만들고 싶다면 중간에 설탕을 뿌리면된다. 이물질을 제거하고 싶다면 이물질 제거기를 설치하면 된다.

이렇게 보면 누군가 데이터를 흘려보내는 놈이 필요하고 데이터를 받아 마시는 놈이 필요하게 된다. 이것을 Producer 와 Comsumer 관계라고 부른다.

리액티브 와 무슨 관계?

자바 스트림과 Reactive 관계보다 차이가 존재한다.

스트림(Stream) 은 데이터를 생산하면 즉각 소비가 발생한다. 하지만 리액티브 는 그렇지 않다. 리액티브 은 시간이 지남에 따라서 생산과 소비가 발생한다. 생산과 소비가 즉각적이지 않다.

이말을 잘 생각해 볼 필요가 있다. 스트림은 데이터를 다루는 영역에서 매우 유용할 수 있다. 프로그래밍 연산을 할 경우에 적합하게 사용되어질 수 있다. 하지만 Reactive 는 프로그래밍 연산보다 네트워크를 통한 데이터 요청과 리턴에 접합한 모델이라고 할 수 있다.

차이는 또 있다. 리액티브 에서 생산자는 반드시 흐름 데이터만 만들지 않는다. 대표적으로 웹에서 클릭(Click) 조차도 리액티브 에서 생산자가 될 수 있다. 그래서 연속된 데이터 흐름이 없다보니 뭔가 생산하는 개념이 아닌것이여서 생산자(Producer) 라는 말을 쓰지 않는다.

“즉각적으로 소비가 발생하지 않는다” 라는 말도 중요하다. 비동기적으로 데이터 리턴이 발생한다는 것을 의미 한다. 하지만 리턴 값을 받기 위한 준비는 항상하고 있다는것도 중요하다.

리액티브 는 네트워크를 통한 데이터 요청, 리턴 모델에 적합하다. 리액티브 요청한 것에 대한 데이터들을 다룰때에는 스트림을 이용할 수도 있다.

Time-Series data 특징

Time-Series 데이터베이스가 존재한다. InfluxDB, Prometheus 등이 대표적인데 Time-Series 데이터베이스의 특징에 대해서 정리 본다.

  1. High-speed data ingest: ‘고속 데이터 수집’ 으로 번역된다. IoT 사용 사례나 시장 분석 데이터와 같이 꾸준히 고속으로 도착하는 연속된 혹은 한꺼번에 밀려드는 대량의 데이터를 처리해야 한다. 대부분의 솔루션들은 데이터를 24시간 365일 처리하도록 되어 있다.
  2. Immutable data: ‘변경될 수 없는 데이터’ 로 번역된다. Immutable 은 프로그래밍에서도 자주 언급되는 단어다. 데이터베이스에 한번 Insert가 되면 데이터 포인트는 데이터가 만료(expire) 되거나 삭제(delete) 되기전까지 그 어떠한 변경도 일어나지 않는다. 이런 데이터는 전통적으로 timestamp 와 아주 적은 데이터 포인트를 가지는 로그 데이터들이다.
  3. Unstructured labels: Time-series 데이터는 일반적으로 많은 소스들에 의해서 오랜 기간 동안 지속적으로 생산되어진다. 예를들어, Iot 경우에, 모든 센서들은 time-series 데이터의 소스다. 이런 상황에서, 시리즈의 각 데이터 포인트들은 소스 정보와 라벨로 다른 센서의 측정치들을 저장한다. 모든 소스들의 데이터 라벨들은 같은 구조나 순서들을 보장하지 않는다.
  4. Diminishing value over time: ‘시간이 지남에 따라 가치가 낮아진다’ 정도로 번역할 수 있겠다. 적절한 시간 범위를 가진 집계된 요약 데이터만이 미래에 연관성이 있을 수 있다. 1년 후에, 대부분의 사용자들은 마이크로초 단위의 범위에 저장된 모든 데이터 포인트를 필요로하지 않을 것이다. 오직 수분, 수시간, 몇일동안 집계되어지고 정형화된 데이터만이 필요로하게 된다.
  5. Queries are aggregated by time intervals: time-series 데이터에 기반한 차트는 확대/축소를 가능하게 한다. 이건 시간 간격에 의해서 그들이 데이터를 수집함으로써 그렇게 할 수 있다. 전통적으로, time-series 데이터 쿼리는 집계들이다. 이것은 데이터베이스 시스템으로부터 개별적인 레코드들을 검색하는 것과 대비된다.

대한민국 병.

앞에 DBA 에 대한 비판글을 쓴적이 있다. 실제로 겪은 일이기도 하고 실제로 일어나는 일이기도 하다. 혹자는 “너의 경험이 전부가 아닐 진데, 전체로 매도하고 있다” 라고 항변하겠지만 전체적인 흐름을 모르고 하는 소리다.

환경의 변화 = 안해도되는 것들.

DBA 들도 환경의 변화를 겪고 있다. 적어도 내가 있는 분야, 그러니까 구체적으로 말하면 AWS 클라우드 시스템을 다루다보면 이러한 현상을 자주 목격한다.

AWS 클라우드에는 관리형 데이터베이스인 RDS 서비스가 존재한다. 데이터베이스 서버를 설치하거나 백업을 직접 구축하는 것이 없이 이러한 것들을 웹 인터페이스를 통해서 손쉽게 할 수 있다. 서버 설치는 아예 없고 그것을 관리하라고도 안한다.

문제는 DBA 들이 이것을 자신들의 직업적인 업무의 축소, 그러면서 단가는 그대로이면서 하는일을 떨궈버리는데 적극 활용한다는 것이다.

이들의 사고 방식은 간단하다. AWS 클라우드에서 제공하는 RDS 서비스이다 보니 그건 AWS 를 다루는 사람이 해줘야 한다는 것이다. 자신들은 이제 SQL 만 짜고 데이터만 만지면 된다는 사고방식을 고수한다.

더 웃긴건 그것이 이제 업계 표준인냥 떠들어 댄다는 것이다. RDS 백업 설정은 더 이상 DBA 가 하는 일이 아닌것이고 그것에 대한 업무 일채를 AWS를 다루는 사람이 해야 한다는 입장인 것이다. 그것이 자신의 주장일 뿐이라고 생각하는게 아니라 이제는 DBA 업계가 그렇게 하고 있다라고 주장한다.

AWS 클라우드 환경에서는 이게 DA나 DBA 들이 작정하고 주장하고 있는게 현실이다. 100이면 100 전부다. 자신들은 AWS 를 모른다고 강변하고 그것을 자신들이 알아야 할 이유가 없다고 강변하면서 SQL 만 잘 다루면서 단가는 그대로 가지고 갈려는 입장인 것이다.

자신이 해야할 일의 영역을 축소하고 적게 일하면서 돈을 많이 받는 계기로 삼게다는 심리가 아니고서야 이렇게까지 할 이유는 없다. 만일 자신이 진정으로 직업적 마인드나 윤리 의식이라도 가지고 있다고 한다면 적어도 AWS 클라우드에서의 데이터베이스를 공부라도 할 텐데, 그래서 RDS 가 무엇이고 S3 나 혹은 Dynamo DB 등과 같은 것들도 공부를 해야할 텐데 AWS 클라우드의 자원이니 자신들은 안해도 되는 거다라는 사고방식.

이쯤되면 이건 병이다.

개발자 = 코딩만 하고 결과만 나오면 그거까지.

DBA 만 깐거 같으니 DBA가 만고의 역적인 되는 느낌인데, 개발자들도 만찬가지다.

논의에 앞서 간단하게 내 경력을 말하면 거의 풀스택에 가깝다. DBA 로서 SQL 을 깊게 섭렵하지는 않았지만 데이터베이스 시스템을 꽤 다뤘었다. 개발자… 자바, 파이썬, Golang, Javascript.. C. 이래저래 할거는 다 해봤다. 특히나 프리랜서로 일을 하면서 요새는 자바를 자주 쓰는 거 같다. 한국에 SI 에서는 죄다 스프링기반으로 제작되고 있다보니 어쩔 수 없는 노릇이다.

이런 개발자적인 경력외에도 시스템 및 클라우드 경력도 가지고 있기에 어느 프로젝트에라도 가게되면 개발자로 지원해서 일을하다가도 어느샌가 시스템 인프라과 개발자들 사이에 뭔가를 중재하는 역할을 하는 경우로 바뀌게 되기도 한다.

이런 상황에 서 있다보면 개발자들을 아주 혐오하게 되는 경우가 아주 많다. 대표적인것이 자신이 만든, 자신이 작성한 코드로 작동하는 기능 혹은 결과물에 대한 성능지표를 계산하지 않는 것이다.

AWS 클라우드에 자바로 제작한 서버를 올린다고 가정해보자. 그러면 AWS 클라우드에서 이 서버를 운영하기 위한 자원이 필요할 것이다. EC2 Instance 를 생성해 그곳에서 자바 서버를 운영할거라면 어떤 타입이 적절한지에 대한 성능지표가 중요해진다.

그러한 성능지표는 누가 제공해야 할까?

당연히 개발자가 제공해야 한다. 성능이란 결국에는 그것을 만든 사람이 제일 잘 알 수밖에 없다. 굳이 코드를 눈으로 보지 않는다고 하더라도 머리속에서 대충 돌려보고 어디에서 병목구간이 생길수 있는지를 알수 있는 사람은 그것을 만든 사람이 제일 잘지 않겠나.

거기다 서버를 제작했다고 한다면 당연히 기획했던대로 성능이 나오도록 제작을 해야할 의무 또한 개발자에게 있다.

하지만 개발자가 자신이 제작한 서버에 대해서 성능지표를 제공하지 않는다. 적어도 한국의 SI 업계에서는 말이지.

이들의 사고방식은 어짜피 서버를 세팅하는 것은 인프라를 담당하는 사람이 하는 거니까 알아서 하라는 사고방식이다.

경력이 얼만데 서버 세팅하는데 감도 못잡아요?

개발자가 인프라 담당자에게 한 말이다. 인프라 담당자는 서버를 세팅해야하니 개발한 서버가 필요로하는 스펙을 알려달라고 했다고 한다.

용어에 혼동은 있었던 것 같다. 인프라 담당자는 “서버 스펙” 이라고 말을 했으니.. 개발자들이 그런 용어를 사용할 이유는 없는 것이였다. 그렇다고 해서 개발자가 그것을 경력 운운하면서 감도 못잡냐고 핀잔을 줄 권한은 어디에 있나.

그래서 내가 말을 바꿔 개발자 당신이 개발한 서버가 프로덕트 환경에서 동작하는데 필요로하는 메모리, CPU 사용량등의 대한 성능지표를 알려달라고 했다.

모르겠는데요. 테스트를 해봐야 하는데 성능 테스트는 제가 안해요.

단 1초의 망설임도 없이 나온 말이다.

적어도 자신이 개발한 결과물에 대해 어느정도의 성능을 가지고 있는지 정도는 파악하고 있어야 하는게 예의 아닌가? 아무나 개발자라는 딱지를 붙이지 않는다. 내가 볼때 개발자라면 최소한 성능지표정도는 자신있게 말할 수 있어야 하는 것이며 그것은 개발자라면 당연히 가져야할 책임이자 의무라고 생각한다.

하지만 성능을 알려면 테스트를 해야하고 그래서 테스트는 개발자가 하는게 아니라는 의식때문인지 그것은 개발자가 해야할 일이 아니라고 강변하는게 개발자들의 현실이다.

책임과 의무를 회피하기에 급급한 한국사회

프리랜서를 하다보면 다양한 사람들을 만나게 된다. 개발자, 기획자, 회사의 임원등등 다양하다. 개발자들의 경우에 대부분 자신들의 환경에 대한 불만을 많이 토로한다. 야근에다가 주말에까지 나와서 일을 해야하는 현실을 못마땅해하는 것이다. 거기다 경력 뻥튀기에 대해 비양심적인 행위라며 비난하기도 한다.

그들이 야근을 많이하고 일을 많이하는 것은 안타까운 일이다. 하지만 내가 여태까지 만나본 개발자들 중에 책임감을 가지고 일을 하는 사람은 단연코 단 한 사람도 없었다. 거기다 개발을 하는데 있어서도 이런 부분은 이런 기술을 적용하는 것이 낫겠다라는 고민을 하는 개발자도 단 한명도 없었다.

저마다 벽을 만들어서 그 벽을 넘어서려고 하지 않는다. 문제는 그 벽이라는 것이 개발자는 코딩만 하면 장땡, DBA 는 쿼리만 날리면 장땡이라는 벽이다. 나머지는 신경을 써야할 이유도 없고 그것이 내가 책임을 가지고 있어야 하는 이유도 없다는 인식이 팽배하다 못해 그것이 법처럼 여기지고 있다는데 있다.

개발자들의 잘못된 코드로 인해서 발생되어지는 시스템 장애를 인프라 담당자가 독박쓰면서 막고 있는 현실을 그들은 나몰라라 한다.

거기다 프로젝트를 수행하는 업체도 개발에서 발생한 문제를 인프라적인 요소로 땜빵할려는 마인드가 거의 지배적이다. 그렇게 하는 것이 정식인냥 떠들어대고 있으니…

한국 병이다. 개발자들이나 DBA 들을 보고 있노라면 이런 생각이 든다. 어디 SI, IT 분야만 이러겠나. 뭐든 일을 적게하면서 돈은 많이 받고 싶고, 뭐든 문제가 복잡해보이고 귀찮은 것은 남에게 떠넘기는 것이 능력으로 치부되는 사회.

뭐든 안하고 돈 받는게 능력으로 인정되는 사회… 양심, 책임, 의무.. 그런걸 주장하는 사람은 능력이 없는 사람이건 뭘 없어보이는 사람정도 평가 절하되는 사회.

소프트웨어 엔지니어링에서 파이프라인(Pipeline)은 무엇인가? Deployment, CI & CD 파이프라인에 대한 소개

이 글은 bmc blogs 의 “What is a Pipeline in Software Engineering? Intro to Deployment, CI, & CD Pipelines” 을 번역한 것 입니다. 원작자의 허락없이 재개시 및 상업적 이용은 불가합니다.

https://www.bmc.com/blogs/deployment-pipeline/

소프트웨어 엔지니어링 팀에서 파이프라인(pipeline)은 개발자나 DevOps 전문가가 효율적이면서도 확실하게 그들의 코드를 컴파일(Compile), 빌드(Build) 그리고 그들의 프로덕션 컴퓨팅 플랫폼에 배포(Deploy) 하게 해주는 자동화된 프로세스들의 묶음(set) 이다. 파이프라인이 이래야 한다거나 반드시 활용해야할 도구(Tools)를 지정하는 강하고 빠른 규칙은 없지만, 파이프라인이 가져야할 가장 일반적인 컴포넌트들이 있다. 빌드 자동화(build automation)/지속적 통합(continuous integration), 테스트 자동화(test automation), 그리고 배포 자동화(deployment automation) 이다.

일반적으로 파이프라인은 다음의 카테고리로 구분되는 일련의 도구들로 구성된다.

  • Source Control
  • Build tools
  • Containerisation
  • Configuration Management
  • Monitoring

소프트웨어 배송 파이프라인(Software Delivery Pipeline) 개념의 핵심은 어떤 파이프라인의 단계 사이에 수동적 단계나 수동적인 변경이 필요없는 자동화(automation) 이다. 휴먼에러(Human error)는 이러한 지루하고 반복적인 작업을(역, 파이프라인의 단계들을 말한다) 수동으로 하게되면 발생할 수 있고 결국 잘못된 배포로 인해 올바른 산출물 생성에 영향을 미치고 잠재적으로 SLA 까지 영향을 준다.

Deployment Pipeline

배포 파이프라인은 자동화된 방식으로 버전 컨트롤(Version control) 로부터 코드를 가지고 오고 당신의 애플리케이션 사용자에게 손쉽게 활용할 수 있도록 만들어주는 프로세스이다. 개발팀이 프로젝트나 기능에 대해 일을 할때에 그들의 일을 빌드, 테스트, 배포를 위한 확실하고 효율적인 방법이 필요하다. 역사적으로, 이것은 많은 커뮤니케이션과 많은 휴먼 에러를 발생시키는 수동 프로세스들이였다.

전통적인 배포 파이프라인 단계는 다음과 같다.

Version Control

일반적으로 코드를 가지고 일을하는 소프트웨어 개발자들은 코드 변경사항을 소스 컨트롤에(e.g. github) 커밋 한다. 소스 컨트롤에 커밋하면 코드 컴파일화, 유니테스트, 코드 분석 그리고 인스톨러 생성을 트리거하는 배포 파이프라인의 첫번째 단계가 시작된다. 이런 모든 과정이 성공적으로 완료되면 실행결과물은 바이너리로 만들어지고 나중에 사용위해 아티팩트 저장소에 저장 된다.

Acceptance Tests

합격 테스팅은 비지니스에 의해서 미리 정의된 합격 기준에 부합하는 테스트를 위해 미리 컴파일/빌드된 코드에 대해 테스트 묶음을 실행하는 프로세스이다.

Independent Deployment

독립 배포는 컴파일되고 테스트된 아티팩트를 개발환경에 배포하는 프로세스다. 개발 환경은 이상적으로 프로덕트 환경의 판박이 복사본이여 하거나 최소한 아주 비슷해야 한다. 이것은 기능적으로 모든 추가적인 자동화되거나 수동 테스팅을 위한 인프라 준비와 같은 프로덕션에 대해 테스트되어질 수 있는 소프트웨어 이다.

Production Deployment

이 프로세스는 일반적으로 운영팀(Operations) 이나 데브옵스팀에 의해서 다루어진다. 이것은 독립 패포 프로세스와 매우 유사해야 하며 라이브 프로덕션 서버에 코드를 배포해야 한다. 전통적으로 이 프로세스는 예기치못한 이벤트 이슈에 쉽게 버전 롤백을 하거나 제로 다운타임 배포를 위해서 Blue/Green 배포 혹은 Canary 릴리즈를 포함 한다. 제로 다운 타임이 아닌 상황에서 배포 기능 릴리즈 윈도우는 일반적으로 비지니스와 협상을 했었다.

Continuous Integration and Continuous Delivery Pipelines

Continuous Integration (CI) 는 개발자들이 하루에 여러번 그들의 코드를 버전 제어 저장소에 체크하는 방법이다. 이러한 체크는 오류를 빠르고 쉽게 찾을 수 있게 해주며, 자동화된 빌드 파이프라인은 이러한 체크인으로부터 트리거 된다.

CI 의 장점은 다음과 같다.

  • 크기가 좀 더 작은 변경은 좀 더 큰 코드 베이스로 통합하기가 좀 더 쉽다.
  • 당신이 무슨 일을 하고 있는지를 다른 팀 멤버에게 보여주기가 좀 더 쉽다.
  • 좀 더 큰 작업 부분에 버그를 좀 더 적은 디버깅 작업으로 문제을 해결하기 아주 쉽도록 조기에 발견하게 한다.
  • 일관된 코드 컴파일/빌드 테스팅
  • 거의 없는 통합 이슈는 빠른 배포를 가능하게 한다.

Continuous Delivery (CD) 는 개발자와 운영엔지니어가 버그 수정, 기능 및 구성 변경 사항을 프로덕트에 안정적으로 신속하고 지속적으로 배포할 수 있도록 해주는 프로세스다. 지속적 배포는(Continuous Delivery) 필요시 자신있게 수행 할 수 있도록 반복적으로 수행되는 코드 배포 파이프라인의 이점을 제공한다.

CD의 이점은 다음과 같다.

  • 아주 낮은 리스크 릴리즈 – Blue/Green 배포와 카나리 릴리즈는 사용자가 신경쓰지 않아도 되는 제로 다운타임 배포를 보장하며 이전 릴리즈로 롤백하는데 상대적으로 어려움이 없다.
  • 빠른 버그 픽스 & 기능 배포 – 기능 또는 버그 수정을 완료하고 승인 및 통합 테스트를 통과한 경우 CI & CD 사용 – CD 파이프라인은 이것들을 재빨리 프로덕트에 배포한다.
  • 비용 절약 – 지속적인 배포를 통해 팀은 기능 및 버그 픽스를 작업 배치로 처리할 수 있으므로 사용자 피드백을 훨씬 빨리 받을 수 있다. This allows for changes to be made along the way thus reducing the overall time and cost of a project

Blue/Green Deployments

Blue/Green 배포 프로세스의 활용은 하나는 Blue, 다른 하나는 Green 으로 이름붙여진 프로덕트 환경 미러 복사본을 생성함으로써 리스크와 다운타임을 줄인다. 오직 하나의 환경만이 매시간 라이브 프로덕트 트래픽을 제공하도록 살아 있다. 배포 소프트웨어가 non-live 환경에 배포되는 중에는 – 이것은 라이브 프로덕트 트래픽이 배포되는 동안 영향을 받지 않는다는 것을 의미한다. 테스트는 현재 라이브 환경이 아닌 곳에서 실행되며, 모든 테스트가 사전에 정의된 기준을 충족하면 트래픽 라우팅이 라이브 환경이 되도록 라이브 환경이 아닌 환경으로 교체한다. 이 과정은 기존 라이브 환경이 현재 라이브 환경이 아닌것으로 되면서 다음번 배포에 반복된다.

Canary Deployments

Blue/Green 배포와는 달리, Canary 배포는 병렬로 운영되어지는 중복 환경에 의존하지 않는다. Canary 배포는 모든 사용자/서버들에 지속적으로 릴리즈를 롤 아웃(Roll out) 하기 전에 라이브 프로덕션 테스트를 위해 사용자/서버들의 백분율이나 특정 버전으로 릴리즈를 롤라웃 한다. Canary 릴리즈의 주요한 장점은 일찍 실패를 탐지할 수 있다는 것과 예외나 실퍠 이벤트 발생시에 영향을 받는 사용자/서버들의 수가 제한됨에 따라 일부 변경된 것만 롤백할 수 있다는 것이다.

요약을하면, CI 는 소프트웨어 개발팀이 그들의 코드를 체크인하고 품질을 확인하고 컴파일할 수 있도록 하는 자동화된 프로세스다. CD 는 개발과 운영 팀이 자동화된 방식으로 그들의 최종 기능에 새로운 기능과 버그 픽스를 안정적이고 효율적으로 배포할 수 있도록 해준다.

아래에 몇가지 CI/CD 파이프라인 빌드를 위해 사용할 수 있는 다양한 도구들이 있으며, 모두 무료로 시작할 수 있는 이점과함께 안정적이고 강력한 CI/CD 파이프라인을 빌드하는데 사용되어질 수 있다.