1. erlang OTP란
erlang OPT는 Open Telecom Platform의 약자입니다. 여기서 Telecom이라는 부분 때문에 오해를 사는 부분이 있는데,erlang OTP는 매우 generic한 Actor Model입니다. Akka Document에도 알 수 있듯, 대표적인 Actor Model인 Akka에도 erlang OTP를 계승 발전하여 만든 부분이 많습니다.(Akka의 원래 이름도 Scala OTP였습니다. 그 흔적은 다음에서 찾을 수 있습니다. https://github.com/jboner/scala-otp) 그렇다면 Akka의 조상 격인 erlang OTP에 대해서 설명을 시작하겠습니다.(그렇다고 erlang/OTP가 구식이라는 것은 아닙니다. 현재 erlang과 OTP는 계속 새 버전이 나오고 있으며, 발전중입니다.)
OTP의 구성을 말하자면 아래와 같은 그림으로 표현할 수 있습니다.
www.rabbitmq.com |
- erlang : OTP는 erlang으로 만들어졌습니다.
- Application Libraries & Tools : OTP는 Concurrency를 위한 다양한 Library가 embedded 되어 있습니다.
- 그리고 System Design Principle. OTP는 하나의 시스템 디자인의 Template의 기준이 되어줍니다. 이것은 따로 이야기를 하고 싶습니다.
2. OTP System Design Principle - Generic과 Specific.
OTP의 기본 원칙은 "Generic한것은 OTP 라이브러리를 사용하고, 개발자는 Specific한 부분만 짜자." 라는 생각에서 출발을 합니다. 생각을 해보면 매우 합리적인데, 기본적인 Server의 경우, 보통 다음과 같은 Life cycle을 갖습니다.
기본적으로 "시작을 해서 Client의 요청을 들어준 다음 종료한다."가 하나의 Common한 template이라고 봐도 될 것 같습니다. 이러한 상황에서 개발자가 Init한 Code와 Request에 대한 Routine Code, 종료시 termination Code만 체워 넣는다면 매우 Happy한 상황이 될 것입니다. 또한, 유지 보수의 측면에서 봐도, template이 정해져 있다면 어떠한 함수에 어떠한 기능이 들어갈 것이라는것을 알 수 있기 때문에 Code를 쉽게 파악할 수 있을 것 입니다.
그렇다면 한번 Code를 보면서 이야기를 해봅시다.
%% %% F.Cesarini & S.Thomson, Erlang Programming, p.121. %% Client/Server Pattern. %% %% 1> frequency:start(). %% 2> frequency:allocate(). %% ... %% 8> frequency:allocate(). %% 9> frequency:deallocate(11). %% 10> frequency:allocate(). %% 11> frequency:stop(). %% -module(frequency). -export([start/0, stop/0]). -export([allocate/0, deallocate/1]). -export([init/0]). % Start function to create and initialize the server start() -> register(frequency, spawn(frequency, init, [])). init() -> Frequencies = {get_frequencies(), []}, loop(Frequencies). get_frequencies() -> [10,11,12,13,14,15]. loop(Frequencies) -> receive {request, Pid, allocate} -> {NewFrequencies, Reply} = allocate(Frequencies, Pid), reply(Pid, Reply), loop(NewFrequencies); {request, Pid, {deallocate, Freq}} -> NewFrequencies = deallocate(Frequencies, Freq), reply(Pid, ok), loop(NewFrequencies); {request, Pid, stop} -> reply(Pid, ok) end. allocate({[], Allocated}, _Pid) -> {{[], Allocated}, {error, no_frequency}}; allocate({[Freq|Free], Allocated}, Pid) -> {{Free, [{Freq,Pid}|Allocated]}, {ok, Freq}}. deallocate({Free,Allocated}, Freq) -> NewAllocated = lists:keydelete(Freq, 1, Allocated), {[Freq|Free], NewAllocated}. reply(Pid, Reply) -> Pid ! {reply, Reply}. % Client functions stop() -> call(stop). allocate() -> call(allocate). deallocate(Freq) -> call({deallocate, Freq}). call(Message) -> frequency ! {request, self(), Message}, receive {reply, Reply} -> Reply end.
출처 : Programming erlang
Code를 본다면 다음과 같은 Routine을 볼 수 있습니다.
● Server를 시작 - init/0
● Server의 request을 받고 처리 : loop/1
● Server를 종료 : loop/1내의 stop
그렇다면 여기서 Generic한 부분은 Template으로 만들어 Library가 담당을 하고, Specific한 부분만 개발자가 개발을 한다면, 아까 말한 Happy한 상황을 맞이할 수 있을 것입니다.
Generic한 부분
● Process를 생성하는 부분
● 상태를 저장하는 부분
● Process에 요청하는 부분
● Sender에게 답하는 부분
● Process를 termination하는 부분
Specific한 부분
● Init/1 Code
● Request Routine Code
● Sender에게 Replay할 Data
● Termination Code
생각보다 Code를 찢어보면 의외로 Generic한 부분이 많이 나옵니다. 사실 여러분이 "이런것 쯤은 Code몇줄 안하는데 그냥 짜면 안되나?"라는 생각을 하실수도 있습니다. 그러나, 이것은 Architecture Template의 요소도 있으므로, 어느정도의 Value는 있다고 봅니다. 하나의 통일된 Architecture는 매우 유용하니까요.
3. OTP Server의 종류
그렇다고 위의 gen_server가 만능은 아닐 것입니다. 상황에 따라, 더욱 Generic한 부분은 생길 수 있고, 더 필요한 부분도 생길 수 있습니다. 그래서 OTP에서는 다음과 같이 다양한 Server의 종류를 지원합니다.
- supervisor : erlang의 Actor관리하는 Supervisor actor Template
- gen_server : 기본적인 erlang의 Client & Server Architecture
- gen_event : event handling 기반의 gen_server
- gen_fsm : finite state machine 기반의 gen_server
위의 종류는 나중에 하나하나 다룰것입니다. 전부 매우 유용하며, 기본적인 Concurrent Architecture를 공부하는데도 도움이 많이 될 것 입니다.
'01. Concurrent Programming > OTP' 카테고리의 다른 글
OTP의 기본 gen_server (0) | 2016.04.04 |
---|