고압 A (3,300V~66,000V), 고압 B (154,000V 이상), 고압 C(345,000V 이상)로 나뉜다.
3. 사용패턴에 따라서 "고압X" 에서 선택1,2,3 으로 나뉜다.
전기요금은 기본요금 * 사용량 요금의 합산인데, 선택1,2,3은 기본요금을 적게 가져 갈 것이냐, 사용량 요금을 적게 가져 갈 것이냐의 구분 문제이다. 예를들어 우리 공장은 한달에 몇일 사용하지 않는다면, 기본요금을 낮게 가져가고 사용량 요금을 높게 가져가는게 유리하다. 반대로 한달 내내 계속 사용한다면 기본요금을 높게 가져가고 사용량 요금을 낮게 가져가는게 유리하다.
구분
기본요금 (원/kW)
전력량 요금 (원/kWh)
시간대
여름철 (6~8월)
봄 · 가을철 (3~5,9~10월)
겨울철 (11~2월)
고압A
선택 Ⅰ
7,220
경부하
61.6
61.6
68.6
중간부하
114.5
84.1
114.7
최대부하
196.6
114.8
172.2
선택 Ⅱ
8,320
경부하
56.1
56.1
63.1
중간부하
109.0
78.6
109.2
최대부하
191.1
109.3
166.7
선택 Ⅲ
9,810
경부하
55.2
55.2
62.5
중간부하
108.4
77.3
108.6
최대부하
178.7
101.0
155.5
선택 1을 사용하면 기본요금이 낮으므로 한달에 몇일만 사용하는 공장에 유리할 것이다.
4. 사용 계절 / 시간에 따라서 경부하 요금/중간부하 요금/최대 부하 요금로 나뉜다.
대략적으로 여름/겨울에는 전기 값이 비싸지고, 봄/가을에는 싸진다.새벽에는 싸지고, 한낮에는 비싸진다. 예를들어 최대부하 계절/시간에 1KW 당 약 200원이라면, 경부하 시간 계절/시간에는 약 60원이 될 수 있다.
5. 요금 계산
우리 공장이 만약 산업-을-고압A-선택2를 사용한다고 가정해보자. 계약전력 1,200kW공장에서 월 500,000kWh (중간부하 350,000kWh/최대부하 150,000kWh)을 사용한다고 하자.
요금은 간단하게 말하면 = ( 기본요금 + 사용량 요금 ) * 세금 이다.
기본요금은 이렇게 구한다.선택 II의 기본요금인 8,320 (원/kW) x 피크 (저압사용자라면 계약전력)
전력량 요금은 중간부하와 최대부하 요금을 참고하여, 여름철이라고 가정한다면
109.0 (중간부하 요금) x 350,000 (그 시간대 사용량) + 191.1 (최대부하 요금) x 150,000 (그 시간대 사용량) 으로 계산된다.
이 요금합계에 부가가치세 10% 와 전력산업기반기금 3.7% 을 구해서 더하면 이 공장이 여름철 한 달에 내는 총 전기요금을 구할 수 있게 된다.
6. 피크란?
작년 12,1,2,6,7,8월 중 중부하 시간대, 최대부하 시간대에서 가장 높은 피크 (만약 500kw) 라고 하면그게 기준이 되서 다음 년에 모든 달에 적용이 된다. 다만 다음 년의 12,1,2,6,7,8월에 더 높은 피크가 나오면 그 값으로 갱신되고 12,1,2,6,7,8 월을 제외한 다른 달에 더 높은 피크가 나오면 해당 달만 그 높은 피크 값으로 적용된다.
7. 기타 Q/A
1. 검침일이 4월 15일이면 한달 요금은 어떤 기간으로 산정되는가?
-> 3월15일~4월14일까지를 한달로 보고 계산한다.
2. 검침일이 31일이면 2월달은 언제 검침되는가?
-> 2월29일날 검침된다. 30일까지 있는 달은 30일날 검침된다.
3. 페널티는 무엇인가?
-> 계약용량이 700kw이면 700kw 이상 사용된 경우 즉 750kw 정도 사용되면,
1회 경고, 3년이내에 재발하면 (50kw * 종별 기본요금)*1.5 를 패널티로 물게된다.
이름에서 느껴지듯이 Economy of Things (이하 EoT) 는 IoT 의 인프라를 이용해서 경제 활동에 이용한다는 개념인데, 블록체인과는 어떤 관계일까? 이것을 이해 하기 위해 먼저 사물인터넷과 블록체인에 대해 간단히 알아보자.
IoT (사물인터넷)
사물인터넷이란 각 사물들이 인터넷으로 연결 (직접 연결되지 않아도 된다. 보통 게이트웨이등을 통해서 연결됨) 되는 현상을 말하는데, 스마트폰의 확산 + 강력한 센서 + 사물로 부터 나온 빅데이터를 저장 할 수 있는 저장공간의 발전 및 기계 학습을 통한 추가정보생산이라는 저변에 의해 탄생 된 개념이다. 홈의 각 가전들도 사물이 될 수 있고, 공장의 많은 기계들도 사물이 될 수 있으며, 빌딩도 마찬가지이다. 만약 홈,공장,빌딩의 사물들에 대한 에너지를 효율적으로 활용하기 위해 그것들을 인터넷으로 연결해서 인간의 개입 없이 서로 소통하게 하고, 스마트폰을 통해서 인간과 소통하게 한다면 그런 부분에서 여러가지 BM( BEMS,HEMS,FEMS base on IoT) 이 탄생 할 수 있게 된다.
블록체인
블록체인은 데이터를 분산해서 처리를 하되, 그 데이터에 대한 신뢰를 개인간 서로 믿을 수 있게 만드는 기술이라고 볼 수 있다. (컴퓨터공학적으로 해쉬,비대칭암호화,링크드리스트,P2P 등의 기술을 기반으로한 쿨한 아이디어를 통해 완성한다.) 상호 신뢰를 위해서 기존에는 위임된 기관(은행,부동산중개소,페이팔,마스터카드 등등)를 통해 거래를 해야 했던 구조를 벗어나, (은행등을 유지하기 위한 비용을 없애고) 각자(이 글을 보고 있는 당신)가 해당 기관이 되어 분산된 형태의 신뢰 구축 시스템을 만들기 위한 기술 토대이다. 주체가 있는 프라이빗 블록체인이 있으며 완전히 일반 개인에게 분산 시키는 것을 퍼블릭 블록체인이라고 한다. 주요 특징은 모든 거래에 대한 기록을 담은 전자장부를 모든 개인(정확히는 마이닝 하는 노드)이 공동 소유하여, 거래에 대한 신뢰를 구축 한다는 점이다. 여기서 어느 기간동안의 기록을 담은 장부를 블록이라고 칭하며, 그 다음 기간의 장부들과 서로 연결되어 있기 때문에 블록체인이라고 한다.
퍼블릭 or 프라이빗 블록체인과 사물인터넷이 결합된 세상을 꿈꿔보는 소프트웨어 개발자로써 지금 당장은 기술에는 설레지만 사회에 초래되는결과에 대해서는 부정적인 입장이다. 신뢰 비용은 낮추되, 개인간의 균형은 기울어지게 될 까봐 우려된다. 많은 지식 공유/토론이 필요하다. 당장은 퍼블릭 블록체인에 대해서는 폐해 및 구현 상 문제점 이 많아 보이고 그렇다고 프라이빗으로 하자니 과연 그게 블록체인 철학에 부합하는 것일까? 하는 의문도 있다.
Economy of Things ??
이제 두가지 개념에 대한 설명을 들었으니, 이걋을 잘 합쳐서 어떤 새로운 개념을 만들 수 있지 않을까 생각하는 사람이 생겨 날 것이라고 추측하는것은 어렵지 않다. 여러분도 지금 바로 생각해 보라.
"사물들 간에 인간의 개입없이 블록체인을 통해 신뢰 기반의 거래를 할 수 있는 인프라"
이게 떠올랐을 것이며, 나도 마찬가지이다. 이제 앞으로의 글을 통해 우리가 떠올렸을 개념이 다른 사람들도 떠올렸을 개념인지 확인 해 보는 시간을 갖도록 하자.
실제 말해지고 있는 Economy of Things 들
IBM
구글을 통해 "Economy of Things" 를 검색해보면 IBM The Economy of Things 이 먼저 나오는데, 싸이트에서 제공하는 PDF의 내용을 살펴보면, 기존 IoT에 의해 조성된 인프라를 경제활동에 참여 시킨다는 아이디어이다. 블록체인은 따로 언급되지 않고 있다.
Elavon
두번째는 Elavon사 (2011년에 창립된 신용카드 결재 처리 시스템 개발사. 북미 3대 업체의 하나로 매년 처리하는 크레딧 카드 결제 금액은 2000억 달러를 초과하고 있다.) 에서 소개하는 PDF 를 살펴보자. EoT를 실현되는 여정으로 다음과 같이 소개하고 있다.
1. 스마트 기기 출시&확산 (영국가정에 평균 18개의 스마트 디바이스가 있는것으로 조사 되었다)
2. 기기와 경제와의 콜라보레이션 (사고 파는 대상이 넓어지며 행위가 자율적,지능적으로 변화 한다) 3. 신뢰 할 수 있는 거래에 대한 위임 (자신의 회사가 필요하다는 야그~, 하지만 블록체인이라면? 필요 없지 않을까?) 4. 거래 빅데이터 기반의 인공지능 학습에 의한 예측을 통한 진보
한글,한국어 공부했다고 멋진 소설이 안나오듯이, 해쉬,비대칭암호화,링크드리스트 ,p2p를 학생때 이해했다고 블록체인/비트코인등의 멋진 알고리즘,아이디어를 만틀 수 있는 것은 아니다. "천지차이"
컴퓨팅파워, (특히) 저장용량, 네트워크의 엄청난 발전으로 그 동안 알고있었던 기술,아이디어를 확장,발전시킨 빅데이터,딥러닝,사물인터넷(특히 스마트폰 같은 개인화 네트워크 덕분) 등은 모두 "블럭체인" 의 탄생과 궤를 같이 하고 있다고 볼 수 있다.
"규제 받지 않는 인간(민간)의 무한한 탐욕"
신뢰의 사회적 비용을 낮추는 탈중앙화 때문에 역설적이게도 민간 자본 괴물이 나타날 수도 있을 거란 우려가 드는 것은 왜 일까.. 즉 사회적 비용은 낮추지만, 균형은 더욱 무너트리는..앞으로 누군가 (or 단체) 는 이 탐욕을 위해서 계속 시도 할 것인데, 누군가(or 단체)는 이 탐욕에 뒤떨어져서 손해를 보는 세상이 온다면 참 마음 아플것 같다.
요즘 방송에 나오는 유시민 작가님의 말하는 것에서의 몇가지 기술 스펙적 오류 (있다) 는 저 주장에 크게 흠이 잡히지 않는다. 유시민 작가는 컴공/역사/경제/인문학적인 기반하에 주장을 펼치기 때문이기 때문이다. 따라서 " 좀 더 제대로 공부했으면 " 라고 유시민작가에 비판을 하는 사람들은 공염불에 그칠 것이다. 저런 논쟁은 좀 더 제대로 비트코인 스펙을 공부한 사람이라도 찬/반, 흑/백을 알 수 있는 문제가 아니기 때문이다. 컴퓨터 공학적으로의 불록체인 이해와 인문학/경제학 적으로의 블록체인 이해 양쪽이 합쳐져야만 하는 문제인데다가 양쪽을 합쳐서 잘 이해하고 있다고 해도 정답을 맞출 수 없는 문제이기도 하기 때문이다. 따라서 유시민 작가의 논리/의견을 반대 하는 사람들은 좀 비겁해 보이는 "좀 더 제대로 공부 했으면" 을 대신해서 그냥 자신들의 논리로 반박하는게 좋지 않을까 한다.
이제 논점을 바꾸어 보자.
지금 당장은 저런 투기성 문제에 힘을 실어줘서, 한 단락은 정리 하는게 좋지 않을까 한다. 지금 현실적으로 큰 문제가 있는데 토론의 방향이 그냥 지나쳐 줄 수는 없을 것이다. (저런 토론 자체를 자신의 코인가격이 떨어 질까봐 싫어하는 일부 투기 코인꾼들에겐 미안하지만) 투기성 및 기술적 한계등 뻔하게 있는 문제에 대해 빨리 썩은 가지는 처 내고 보다 발전적인 논의로 나아가길 기대한다. 지금 당장 혹은 몇년 내로 블록체인의 응용 제품들이 나오고 활성화 되는 시대가 되지 않는다고 생각 된다. 그 과정에서의 이런 잡음은 당연히 필요하다고 보며, 너무 서두를 필요도 없으며 차분히 준비하면 된다. 유시민 작가님과 지금 상황에서 대적(?) 하기 위해서는 그 논점에서는 님 말씀이 옳습니다.라고 인정하자. 그리고 이제 그럼 비트코인의 한계를 벗어난 다른 시스템들의 효용성과 퍼블릭 or 국가적 프라이빗 블록체인 (유시민 작가 의견으로는 이게 퍼블릭. 공공재니깐.) 블록체인과 사물인터넷과의 접점 및 그런 탈중앙기반 신뢰 시스템이 4차혁명에서의 어떤 역할을 할 수 있는지, 국가 경쟁력과는 어떤 상관관계가 있을 것인가에 대한 심도있는 토론을 해 가도록 하는 방향으로 가닥을 잡아야 할 것이다.
개인적으로 퍼블릭 or 프라이빗 블록체인과 사물인터넷이 결합된 세상을 꿈꿔보는 소프트웨어 개발자로써 지금 당장은 기술에 대해서는 설레지만 사회에 초래되는 결과에 대해서는 부정적인 입장이다. 신뢰 비용은 낮추되, 개인간의 균형은 기울어지게 될 까봐 우려된다. 많은 지식 공유/토론이 필요하다. 당장은 퍼블릭 블록체인에는 폐해 및 구현 상 문제점 이 많아 보이고 그렇다고 프라이빗으로 하자니 과연 그게 블록체인 철학에 부합하는 것일까? 하는 의문도 있다.
보통 처음 소프트웨어 코딩을 시작 할 때 우리는 여기 저기 문의도 해보고 기웃기웃 하다가 결국 언어를 선택하고 나서 드는 생각은 내가 왜 이 언어를 선택했으며, "이제 무엇을 할 까?" 일 것이다.
현재 출판된 많은 파이썬 책들이 문법 및 파라다임에 대한 레퍼런스를 가르치고 있다면, 혹은 하나의 기술에 집중하고 있다면, 이 책의 특징은 데이터 분석에 관한 종합선물세트로써 (물론 모든것이 있는것은 아니다. 웹,서버개발 등의) why 에 대해 저자가 다양한 파이썬의 이야기 보따라를 풀면서 지겹지 않게 적절히 독자를 끌어가고 있다.
아마 이 책을 읽고 나면, 데이터 분석의 기초(라고 말하고 대부분)을 할 줄 알게 될 것이며, 이 후에는 머신러닝,딥러닝 개발로 가는데 있어서 좋은 디딤돌이 되어 줄 것이다.
분산 응용 프로그램을 작성하는 것은 매우 어렵습니다. 특히 단일 노드만 염두해 두고 개발하기 시작할 때 그런데요. 프로그래머는 처음에는 기능에 초점을 두는 경향이 있지만, 나중에는 확장성 문제가 발생하곤 합니다. 다행히도 Akka는 확장을 위한 많은 도구를 제공하며, 개발 프로세스 초기 부터 Akka를 이용할 수 있습니다. 이 글을 통해 단일 노드의 확장 불가능한 웹 서비스를 확장 가능한 웹 서비스로 변환하는 예를 통해 이러한 기능을 활용하는 방법을 알아 보겠습니다.
이 웹 서비스는 컨테이너가 교차점(Junction) 앞에서 스캔 될 때마다 호출될 하나의 엔드 포인트를 정의 합니다. 엔드 포인트는 대상 컨베이어의 ID를 리턴해야 하며, 컨테이너는 하드웨어에 의해 리턴 받은 목표 컨베이어로 밀려나게 됩니다.
GET /junctions/<junctionId>/decisionForContainer/<containerId>
를 호출하면 아래처럼 컨베이어 ID 가 리턴됩니다.
{"targetConveyor":"<conveyorId>"}
구현을 시작하기 전에 몇 가지 가정을 하겠습니다.
id 만 가지고 있는 단순한 Container 객체를 사용한다 (persistence layer는 나중에 추가 될 수 있다)
id 만 가지고 있는 단순한 Junction 객체를 사용한다. (같은 이야기)
비즈니스 로직은 이미 whereShouldContainerGo 함수에 정의되어 있다. 매개 변수 중 하나로 주어질 교차점 상에서 해당 Container가 이동해야 할 컨베이어에 대한 결정을 반환한다. (즉 교차점과 컨테이너를 주고 목적 컨베이어를 받는다) 이 함수에 대한 호출에는 약 5-10ms가 소요된다.
비즈니스 로직 함수를 호출 할 앞 단의 HTTP 서비스를 구현해야 합니다. 이 글에서의 촛점은 다양한 기능이 아니라 (위의 단순한 가정들), 성능과 확장성이라는 것을 명심해 두십시요. 유일한 기능 요구 사항은 각 교차점에서 스캔된 컨테이너가 어떤 컨베이어로 이동해야 할 지에 대한 결정을 내려야한다는 것입니다.
우리는 평범한 Akka를 사용할 것이고 추가 확장은 없으며 특히 아직 클러스터링에 대한 고려는 없습니다. 하나의 JVM에서 실행되는 간단한 애플리케이션으로 시작하며, 확장성에 대해 초반에 생각하지 않았더라도 나중에 그것을 추가하는 것이 생각보다 쉽다는 것을 보여주고 싶습니다.
Step 0: 도메인 및 내부 API 정의
이제 어떤 언어로 컴포넌트(actor) 가 소통하고 어떻게 도메인을 모델링할 것인지에 대해 정의 할 것인데요. 우리는 Scala 언어의 케이스 클래스를 사용하여 쉽게 표현 할 수 있었습니다.
HTTP 서비스를 구현하기 위해 우리는 spray-json과 함께 spray-http를 사용할 것입니다.(akka-http 를 이용한 소스도 깃헙에 준비되어 있습니다.) 우리의 서비스는 하나의 엔드 포인트만 정의할 것이며, 전달된 매개 변수를 가지고 비즈니스 로직 함수를 호출한 후에, Go 객체로 래핑하여 결과를 반환할 것 입니다.
classRestInterface(exposedPort:Int)extendsActorwithHttpServiceBasewithActorLogging{valroute:Route={path("junctions"/IntNumber/"decisionForContainer"/IntNumber){(junctionId,containerId)=>get{complete{log.info(s"Request for junction $junctionId and container $containerId")valjunction=Junction(junctionId)valcontainer=Container(containerId)valdecision=Decisions.whereShouldContainerGo(junction,container)Go(decision)}}}}defreceive=runRoute(route)implicitvalsystem=context.systemIO(Http)!Http.Bind(self,interface="0.0.0.0",port=exposedPort)}
[역주] 스칼라 문법 - "함수 호출에 관한 썰~"
스칼라에서는 함수호출시 () 대신 {} 중괄호를 사용 할 수 있다. 단 !!! 인자가 하나일 경우 !!
def formatEuro (amt : Double) = {.......} <-- 와 같은 Double 형의 매개변수 하나를 받는 함수가 있을 시 formatEuro { val rate = 1.32; 0.235 + 0.7123 + rate * 5.32 } <-- 이렇게 호출 할 수 있는데, {} 블럭 안에서 평가된 rate 가 매개변수로 들어가게 된다.
더 간단하게 예를 들면
println ("Hello World") 대신해서 println { "Hello World" } 를 사용 할 수 있다는 의미이다.
스칼라에서 이렇게 한 이유는 프로그래머가 중괄호{} 내부에서 좀 복잡한 함수 리터럴을 직접 사용하도록 하기 위해서인데, 이렇게 작성한 메소드는 호출 시 좀 더 제어 추상화 구문과 비슷해진다. 좀 더 수준높은 예제를 들어 보면
def withPrintWriter(file: File, op: PrintWriter => Unit ) {
val writer = new PrintWriter(file) try { op (writer) } finally { writer.close() }
HTTP 요청을 처리 할 액터를 정의했습니다. (역주: Akka 를 이용하여 간단히 Rest Api 서버를 만들 수 있습니다.) 이 액터 내에서 중첩된 지시문(Directives)들로 생성된 route 를 만들었습니다
path 지시문은 지정된 PathMatcher를 HTTP 요청 경로에 적용하고 매칭이 성공하면 중첩 경로에 요청을 전달합니다. 또한 전달된 Path에서 junctionId 및 containerId 라는 두 개의 값을 추출합니다.
get 지시문은 HTTP GET 메서드 요청만 중첩 된 경로로 전달되도록 합니다.
complete 지시문은 마샬러블 객체를 받아 들여 HTTP 응답으로 반환합니다. 이 경우 블럭킹을 하지 않고 비동기적으로 Future [Go] 를 내 개체로 반환합니다. 이 Future가 완료되면 실제 응답이 반환 (역주: 이 역할 전용의 쓰레드풀이 따로 있음. Scala Future 이해하기) 되는 식입니다.
지금까지의 것만으로 우리는 첫 번째 테스트를 실행하는 데 필요한 거의 모든 것을 갖추고 있습니다. 더 만들어야 할 유일한 것은 응용 프로그램의 진입점입니다. (extends App 을 통하여 maiin 감춰짐)
교차로#2 에서 컨테이너#3를 스캔 한 후 , 컨테이너#3를 컨베이어 CVR_2_9288 로 밀어 넣기로 결정했군요.
현재 아키텍처는 다음과 같습니다.
Performance testing
우리의 웹 서비스에 대해 10000건의 호출을 시뮬레이션 하여 5개의 교차점에 대한 결정을 확인해 봅시다. 우리는 성능 검사도구로 ab (ApacheBench)와 parallel (GNU Parallel)을 사용할 것입니다. 각 교차점에 대해 호출 시퀀스(ab 사용)를 시뮬레이트합니다. 병렬 처리는 웹 서비스 수준에서 시작할 것입니다. 5개의 다른 교차점에 대해 병렬 호출을 수행하기 위해 Parallel 을 사용할 것입니다.
성능 평가에 대한 노트
이 섹션에 표시된 숫자는 컴퓨터, OS, 실행중인 응용 프로그램, 네트워크 등에 따라 다를 수 있지만 이 게시물의 목적에 는 충분합니다.
Akka 에 대한 성능 제약
테스트가 의미가 있기 위해서는 Akka를 약간 제한해야 합니다. 저는 4코어 프로세서를 사용하고 있지만 실행중인 각 액터 시스템에 대해 기본 MessageDispatcher의 병렬 처리를 2로 제한 할 것입니다. 이렇게 하면 자원 확보에 어려움을 겪고 있는 서비스를 시뮬레이션 할 수 있을 것 입니다.
이제 우리는 RestInterface를 수정하여 하나의 decider액터를 의존성에 추가하고, 의사 결정이 필요할 때 마다 이를 사용 할 것 입니다. (역주: ask 는 요청한 후 Future 로 값을 되돌려 받겠다는 의미이고, tell 은 보내기만 하는 함수이다)
classRestInterface(decider:ActorRef,exposedPort:Int)extendsActorwithHttpServiceBasewithActorLogging{valroute:Route={path("junctions"/IntNumber/"decisionForContainer"/IntNumber){(junctionId,containerId)=>get{complete{log.info(s"Request for junction $junctionId and container $containerId")valjunction=Junction(junctionId)valcontainer=Container(containerId)decider.ask(WhereShouldIGo(junction,container))(5seconds).mapTo[Go]}}}}// ...
}
지금 RestInterface가 서로 다른 교차점에 대해 2개의 메시지를 받으면 두 개의 다른 액터로 메시지를 전달하고 병렬로 의사 결정을 할 수 있게 됩니다. (역주: 이전에는 하나의 액터로 요청(ask)했었지요? 여기서는 여러 액터로 전달(foward) 합니다.) 이제 우리는 상당한 개선을 기대해 볼 수 있을 것 입니다.
± % cat URLs.txt | parallel -j 5 'ab -ql -n 2000 -c 1 -k {}' | grep'Requests per second'
Requests per second: 67.36 [#/sec] (mean)
Requests per second: 69.03 [#/sec] (mean)
Requests per second: 67.75 [#/sec] (mean)
Requests per second: 66.88 [#/sec] (mean)
Requests per second: 66.28 [#/sec] (mean)
Scalability testing
지금까지 우리는 서비스의 성능에 대해서만 관심을 가졌었는데요. 아시다시피 성능이란 시간 이라는 단어로써 얼마나 빠른지를 보여줍니다. 지금까지 사용해온 "초당(시간당) 요청 수"가 좋은 예입니다. 그럼 성능과 확장성의 차이점은 무엇일까요?
시스템의 확장성은 더 많은 자원을 추가하는 것이 성능에 어떻게 영향을 미치는지에 관한 것입니다. 예 : 리소스를 두 배로 늘림으로써 처리량을 배가시키는 것은 선형 확장성입니다. 이 예에서는 더 많은 CPU를 추가 할 때 강력한 Akka 및 액터 모델 덕분에 확장성이 자동적으로 조정 될 수 있었습니다.이 방법을 스케일 업이라고합니다. (역주: CPU 를 더 추가하면 교차점 만큼 액터가 늘어나면도 서로 간섭을 최소화 함) 스케일 아웃, 즉 성능을 향상시키기 위해 더 많은 컴퓨터를 추가하는 것은 어떨까요?
Step 4: 웹서비스를 규모 확장성 있게 만들기
간단히 생각하면 우리는 더 많은 컴퓨터 (또는 JVM)를 추가하고 응용 프로그램을 따로 배포한 후 각각 실행하여 확장 할 수 있습니다. 그런 다음 요청에 따라 특정 컴퓨터로 트래픽을 수동으로 전달할 수 있을 것 입니다. (역주: 라운드 로빈으로 하든, 12345 교차점은 A 서버, 678910 교차점은 B 서버로 가게 하든 앞단에서 분배처리. 소프트웨어 LB)
이 기법을 수동 스케일 아웃이라고합니다. 이 방법에는 몇 가지 단점이 있는데요. 로드밸런서에 비즈니스 로직을 유지해야 하며, 새로운 교차점 또는 컴퓨터를 추가 할 때마다 전체 설정을 조정해야 할 수 있습니다.
정말 다행히도 우리는 이러한 모든 작업을 수동으로 만들 필요가 없습니다. 대신, Akka Cluster extension을 사용 하면 됩니다. 우리는 SortingDecider를 Sharded (역주: 여러 서버로 자동 분배) 할 것이구요. 이 액터의 인스턴스는 요청의 junction ID를 기반으로 자동으로 생성 될 것입니다. 우리는 두 개의 애플리케이션 인스턴스 (두 개의 노드)를 실행 할 것이며 그들은 서로를 자동으로 엮어서 Akka Cluster를 형성 할 것입니다. 클러스터의 모든 노드는 Sharding Extension이 SortingDecider액터를 만들고 마이그레이션 하는 데 사용될 것입니다. 우리는 노드를 별다른 신경을 쓰지 않고 추가하고 제거 할 수 있으며, Akka는 자동으로 추가된 자원을 사용할 것입니다. 샤딩을 추가하기 위해 기존 코드를 변경할 필요는 없습니다. 또한 모든 책임은 Akka의 ShardRegion으로 마이그레이션되므로 이전에 우리가 만든 DecidersGuardian은 필요하지 않습니다.
SortingDecider를 샤드하기 위해 해야 것은 단지 두 단계 입니다.
첫 번째 단계는 props, shardName 및 두 개의 "해시 함수"를 정의하는 동반자 객체를 만드는 것입니다.
extractShardId- (역주: 간단히 어느 서버로 갈 것인가) 이 함수는 수신 메시지를 기반으로 샤드 ID를 정의합니다. 샤드는 우리 액터의 집합일 뿐입니다. 그러한 세트는 하나의 노드에만 존재할 수 있으며 Akka는 사용 가능한 각 노드에서 비슷한 수의 샤드를 갖기 위해 시도합니다. 그래서,이 함수를 정의하는 것만으로, 어플리케이션이 지원하는 샤드의 수를 제어 할 수 있습니다. 여기서는 2 개의 샤드 만 지원합니다 (아래 코드 참조).
extractEntityId- (역주: 어느 교차점 액터로 갈 것인가) 이 함수는 이 메시지를 처리 할 액터의 고유 식별자인 엔티티 ID를 정의합니다. Akka는 우리가 정의한 Props을 바탕으로 그 액터를 자동으로 만듭니다.
두 함수는 ShardRegion이 메시지를 받을 때마다 호출됩니다. 먼저 extractShardId 함수가 호출되고 샤드 id를 반환합니다. Akka는 이것이 어느 노드에 보관되어 있는지 확인합니다. 이것이 다른 노드 인 경우 메시지는 이 쪽에서 추가 작업 없이 다른 노드로 전달됩니다. 이것이 이 노드에서 처리해야할 것이라면 extractEntityId 함수가 평가되고 특정 액터의 식별자인 엔티티ID를 반환합니다. 메시지는 액터로 전달되어 거기에서 처리되며 액터가 존재하지 않으면 자동으로 생성됩니다. 우리의 경우에는 교차점 (junction ) 마다 하나의 액터가 있으므로 extractEntityId 함수는 junction ID 만 리턴합니다.
± % cat URLs.txt | parallel -j 5 'ab -ql -n 2000 -c 1 -k {}' | grep'Requests per second'
Requests per second: 68.39 [#/sec] (mean)
Requests per second: 66.30 [#/sec] (mean)
Requests per second: 65.99 [#/sec] (mean)
Requests per second: 64.86 [#/sec] (mean)
Requests per second: 64.54 [#/sec] (mean)
보시다시피 JVM 인스턴스 (= 하나의 컴퓨터) 만 사용하기 때문에 초당 요청 요청 수가 비슷합니다. 이 시나리오에서 응용 프로그램은 확장(scale up) 만 할 수 있습니다.
노드 2개 실행
두 번째 노드를 실행하면 첫 번째 노드와 자동으로 클러스터를 형성한 다음 SortingDecider 액터를 생성하기 위해 두개의 노드가 사용됩니다. 이렇게 하면 어플리케이션은 더 많은 요청을 처리하게 됩니다. 두 번째 노드를 실행하기 위해 동일한 jar를 사용합니다. 노출된 웹 인터페이스 포트와 Akka Cluster 노드 포트를 다시 정의하면 됩니다.
테스트를 실행하기 전에 간단한 라운드 로빈 기반 로드 밸런서를 설정해야 합니다. (역주: HAProxy 란)
haproxy -f src/main/resources/haproxy.conf
이렇게 하면 포트 8000에서 서버가 실행되고 8080 (첫 번째 노드)과 8081 (두 번째 노드) 모두에게 트래픽이 교대로 전달됩니다. 마지막 테스트에서는 이 변경을 위해 다른 URL 파일 (shardedURLs.txt)을 사용 할 것입니다.
± % cat shardedURLs.txt | parallel -j 5 'ab -ql -n 2000 -c 1 -k {}' | grep'Requests per second'
Requests per second: 106.80 [#/sec] (mean)
Requests per second: 108.15 [#/sec] (mean)
Requests per second: 100.60 [#/sec] (mean)
Requests per second: 99.92 [#/sec] (mean)
Requests per second: 100.07 [#/sec] (mean)
보시다시피 성능이 눈에 띄게 향상 되었습니다.
우리가 배운것
우리는 이 튜토리얼에서는 다음의 내용을 배웠습니다.
Akka 및 Spray를 사용하여 웹 응용 프로그램을 만드는 방법,
액터 모델을 사용하여 애플리케이션을 확장하는 방법,
Akka 클러스터 및 Sharding 확장을 사용하여 응용 프로그램을 수평 확장하는 방법,
웹 애플리케이션의 성능과 확장성을 테스트하는 방법.
akka-sharding-example repository on GitHub. 저장소를 체크 아웃하여 동일한 서비스를 구현하고 동일한 성능 분석을 수행 할 수 있습니다. 이 블로그 게시물의 각 단계 (제목 참조) 에 저장소branch가 매칭되어 있어 있어, 보다 쉽게 사용할 수 있을 것입니다. 4단계를 구현하려면 step3 branch를 체크아웃 하고 블로그 섹션을 따르십시오.
보너스 : Akka HTTP
Spray 대신 Akka HTTP를 사용하는 저장소에 bonus branch가 있습니다. 이 구현은 약간 다르게 동작하며 가까운 미래에 이에 대해 블로그를 작성할 것입니다.
akka in action 이라는 책에서 클러스터란 무엇인가에 대해 잘 정리하였습니다. 아래 링크를 참고하세요.