ScyllaDB 테스트 마무리

NOSQL 2015. 12. 9. 14:02

이전 포스트에서는 3차례에 걸쳐서 ScyllaDB vs. Cassandra Benchmark를 테스트해봤다. 3번에 걸친 테스트에서 ScyllaDB에서 주장하는 10배의 성능 향상을 직접 확인하지는 못했다. 그렇다고 ScyllaDB가 홈페이지에서 정확하지도 않은 내용을 주장하는 것은 아니다.

테스트 환경이 제한적이다보니 10배의 성능 향상을 확인 못한 것이지 ScyllaDB에서 테스트한 것과 동일한 사양으로 테스트를 진행한다면 충분히 가능할 것이라 생각한다.

중간 규모의 서비스에서는 Cassandra를 운영한다 하더라도 시스템 사양을 24Core/128Gb로 맞추기는 힘들다. 그나마 적절한 수준이 8~16Core/32Gb일 것이고 현실적으로는 8Core/16Gb정도의 사양으로 운영하게 될 수 있다.

사내 개발장비를 통해서 진행한 테스트(ScyllaDB vs. Cassandra benchmark 따라하기 2 : 사내 개발장비 테스트)가 현실과 가장 유사할 수 있다.

사내 개발장비 테스트에서 보여준 ScyllaDB의 성능은 "그럭저럭한 Request 부하에서는 Cassandra에 가까운 성능을 보여준다"고 할 수 있다. 하지만 AWS를 통한 테스트(ScyllaDB vs. Cassandra benchmark 따라하기 3 : AWS 테스트)에서 확인할 수 있었던 것 처럼 "Request가 증가할수록 ScyllaDB는 Cassandra보다 더 좋은 성능을 보여준다"는 것을 확인할 수 있었다.

지금까지 확인된 사실을 바탕으로 ScyllaDB와 Cassandra의 성능을 짐작해보면 다음과 같이 표현할 수 있다.

<그림 1. Cassandra와 ScyllaDB의 초당 Request 증가에 따른 TPS 변화 예상>

적절한 시스템 사양만 갖춘다면 "Redis의 속도를 가진 Cassandra"를 갖는 것이 불가능하지만은 않다고 보인다.

<그림 2. Redis의 속도를 가진 Cassandra>

만약 현재 Cassandra를 운영하고 있는 경우라면 2016년 1월에 ScyllaDB GA버전이 나온 후 ScyllaDB로 교체하는 것을 고려해볼만한 가치가 있는 것 같다. 

그리고 rowkey, column name 등이 정렬된다는 특징을 이용하면 Message Queue, Time Series DB, CEP 등을 구현하는데 매우 유용할 수 있을 것 같다.




Posted by 알 수 없는 사용자
,

ScyllaDB의 Benchmark 따라하기

지난 포스팅에서는 VirtualBox, 사내 개발장비에서 각각 ScyllaDB와 Cassandra의 성능을 테스트해보았다. 역시나 ScyllaDB 홈페이지에 나와있는 성능 10배 향상은 확인할 수 없었다. 게다가 사내 개발장비에서 테스트한 결과에서는 Cassandra가 약간이나마 더 좋은 성능을 보여줬다.

하지만 앞에서 진행한 2번의 테스트는 아직은 정확한 성능을 측정했다고는 할 수 없다. 부하를 주는 클라이언트가 1대뿐인 환경에서 진행되었고 시스템 사양도 실제 서비스에서 사용하기에는 무리가 있을만하기 때문이다.

이번에는 AWS에서 ScyllaDB와 Cassandra를 테스트해보도록 하겠다. 각 DB의 구성은 Single Node로 구성해서 테스트했다.


테스트에 사용할 EC2 Instance는 다음과 같다.

DB서버(ScyllaDB/Cassandra)

  • m3.xlarge : vCPU=4, 메모리=15G
  • Volume : m3.xlarge에서 기본 제공되는 SSD 사용
부하 테스트 클라이언트
  • t2.micro : vCPU=1, ECU=변수, 메모리=1G


:: m3.xlarge DB Server, 부하테스트 클라이언트 1대로 테스트 ::

1) 쓰기 테스트
쓰기 테스트에 사용한 명령은 앞서 진행했던 명령어와 동일하다.
cassandra-stress write duration=10m -mode native cql3 -rate threads=700 -node $SERVER

쓰기 성능테스트를 한 결과는 다음과 같다. 
ScyllaDB 평균 TPS : 17792

Cassandra 평균 TPS : 22692

ScyllaDB는 앞서 진행했던 사내 개발장비에서보다 낮은 성능을 보여주고 있다. AWS의 Instance Disk I/O 성능이 사내 개발장비보다 낮기 때문에 당연한 결과일 수 있다.


2) 읽기 테스트

VirtualBox 테스트와 마찬가지로 데이터를 먼저 채워넣은 후 읽기 테스트를 진행했다.

읽기 테스트는 다음의 명령은 다음과 같다.

cassandra-stress mixed 'ratio(read=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=700 -node $SERVER

결과는 다음과 같다.

ScyllaDB 평균 TPS : 15911

Cassandra 평균 TPS : 27918


3) 읽기/쓰기 테스트

테스트에 사용한 명령은 다음과 같다.

cassandra-stress mixed 'ratio(read=1,write=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=700 -node $SERVER

결과는 다음과 같다.

ScyllaDB 평균 TPS

   읽기 : 9262, 쓰기 : 9241

Cassandra 평균 TPS

   읽기 : 1700, 쓰기 : 1689


결과를 종합해서 살펴보면 다음과 같다.

 

 ScyllaDB

Cassandra 

쓰기

17792

22692

읽기

15911

27918

읽기/쓰기 

 7772/7767

10729/10739

<표 1. AWS에서의 benchmark결과>

m3.xlarge가 vCPU가 4개뿐이어서 큰 기대를 하지는 않았다. 그런데 결과는 기대 이하로 Cassandra가 더 빠르게 나왔다.

이정도까지 했으면 "ScyllaDB가 결과를 너무 과대포장했네~"라고 생각할 수 있을만 하다. 그런데 테스트 중 측정된 Load Average를 보면 좀 더 테스트가 필요하다는 필요성을 느낄 수 있다.

ScyllaDB와 Cassandra 테스트 중 측정된 Load Average는 다음과 같다.


 

 ScyllaDB

Cassandra 

 Load Average

4.2 ~ 5.5 

10.5 ~ 15.5 

<표 2. 테스트 중 측정된 Load Average>

만약 이렇다면 부하 테스트 클라이언트의 수를 늘렸을 때 ScyllaDB는 더 많은 일을 할 수 있을 것 같다. 물론 ScyllaDB는 Architecture상 Load Average가 크게 올라가지 않을 수 있다.


:: m3.xlarge DB Server, 부하테스트 클라이언트 10대로 테스트 ::

1) 쓰기 테스트

테스트 결과는 다음과 같다. 
ScyllaDB 평균 TPS : 71983

Cassandra 평균 TPS : 32966


2) 읽기 테스트

결과는 다음과 같다.

ScyllaDB 평균 TPS : 60496

Cassandra 평균 TPS : 27739


3) 읽기/쓰기 테스트

결과는 다음과 같다.

ScyllaDB 평균 TPS

   읽기 : 29018, 쓰기 : 28994

Cassandra 평균 TPS

   읽기 : 12555, 쓰기 : 


결과를 종합해서 살펴보면 다음과 같다.

 

 ScyllaDB

Cassandra 

쓰기

71983

32966

읽기

60496

27739

읽기/쓰기 

29018/28994

12555/12540

<표 2. AWS에서 Client가 10개인 경우의 benchmark결과>


부하테스트 클라이언트를 1대로만 했을때는 Cassandra의 약 80%의 성능 정도만 보이던 ScyllaDB였다. 그런데 클라이언트를 10대로 하니 Cassandra의 2배 이상의 성능을 보여주고 있다.

특이한 점은 Cassandra의 경우 부하를 1대에서 주는 경우와 10대에서 주는 경우 읽기 성능이 크게 달라지지 않았다. 반면에 ScyllaDB는 쓰기 성능과 비슷한 비율로 증가한 것을 알 수 있다.


Posted by 알 수 없는 사용자
,

이번 글에서는 E2E-Monitor 개발에 사용했던 웹기술들에 대해서 적어 볼까합니다.

기술적인 내용 보다는 해당 기술들을 사용하기 전까지 어떤 고민들을 했고, 그 고민들을 해결하기 위해서 어떤 기술들을 살펴봤는지, 그리고 최종적으로 왜 해당 기술을 사용하기로 결정했는지 등의 도입 배경 및 사용 후 소감 등을 중점적으로 다루도록 하겠습니다.


E2E- Monitor에 사용했던 웹기술들


E2E-Monitor 중에서도 운영자가 실제로 사용하게 되는 User Client는 Web Application으로 구현되었습니다.

개발에 사용했던 기술들은 다음과 같습니다.

1. jQuery
2. AngularJS
3. RequireJS
4. Bootstrap (for AngularJS)
5. D3.js
6. Dagre-D3
7. C3
8. Big Scatter Chart



1. jQuery


jQuery는 워낙 유명한 JavaScript Framework이기 때문에 따로 설명이 필요없을 것 같습니다.

이번에는 주로 간단한 DOM 제어 기능과 애니메이션 효과등을 구현하는데 사용했습니다.

기본 Framework로 사용한 AngularJS에서도 jqLite(jQuery 호환 API)를 지원합니다만, jqLite 대신 Original jQuery를 사용하는 것이 프로젝트를 진행하는게 더 효율적이겠다고 판단했습니다. 

아무래도 필수 기능 몇가지만 지원하는 jLite 보다는 jQuery의 모든 기능을 다 쓸 수 있는 환경이 개발에는 더 도움이 될것 같았고, jQuery 기반의 UI Component들을 사용해야 되는 상황이 있을 수도 있겠다고 생각했었던 것 같습니다.


결과적으로는 역시 jQuery를 사용하기로 했던 것이 좋은 결정이었다고 생각됩니다.

지금 코드를 보면 jQuery 구문이 사용된 코드가 실제로는 그렇게 많지는 않습니다.

하지만 jQuery로 작성된 부분을 AngularJS나 다른 방식으로 처리를 해야 했다면 지금 보다 훨씬 더 복잡한 코드가 되지 않았을까 생각합니다.



2. AngularJS


이번 프로젝트를 진행하면서 처음으로 접했던 Javascript Framework 입니다.

(사실 jQuery와 D3, Bootstrap 말고는 다 처음 접한 기술들 입니다.)

고객사의  Admin Application이 AngularJS를 기반으로 만들어져 있었기 때문에, 시스템 통합을 위해서는 선택의 여지가 없었죠.

AngularJS는 MVC  또는 MVVM 을 위한 Web Application Framework 입니다.

Data와 View, 그리고 둘사이를 연결해주는 Control를 분리함으로써 Application을 모듈화하고 체계적으로 설계할 수 있도록 도와주는 Library라고 생각하시면 될것 같습니다.

AngularJS에 대한 보다 자세한 기술적인 내용은 저희 개발팀 막내가 작성한 글, "주니어의 개발자 경험기 [1편-AngularJS]"을 참고하시기 바랍니다.

다른 대부분의 IT 기술들도 그렇겠지만 AngularJS를 처음에 접했을 때는 참 쉽다는 인상을 받았는데, 역시 사용하면 할수록, 파고 들면 들수록 어려워지면서도 어느순간 폭 빠져있는 저를 발견하게 되었습니다.

익숙해지기만 한다면, 속된 말로 "떡이 되기 쉬운" Web Application Code를 어느 정도 정리하고 체계화 시킬 수 있는 좋은 Library인것 같습니다. (개발 속도 향상은 덤?)

jQuery와 더불어 Web Application 개발에 꼭 사용해야 될 Javascript Library를 꼽는 다면, 저는 앞으로 AngularJS를 선택하겠습니다.

하지만 Application의 성능이라는 측면에서 본다면, 다수의 접속자를 위한 Web Application에는 적합하지 않은 기술 일 수 있습니다. 

필요한 곳에만 부분 적용하는 것도 가능하긴 하지만, 기본적으로 AngularJS를 기반으로 개발된 Web Application들은 Javascript Engine이 HTML DOM을 실시간으로 생성하는 방식이기 때문에, Core HTML Page보다 느릴 수 밖에 없습니다.

개발 속도와 코드 관리의 효율성을 선택할 것인가, 서비스의 품질(특히 반응속도)를 선택할 것인가를 잘 분석하고 판단한 다음, AngularJS 도입에 대해 고려하는 것이 좋을 것 같습니다. 




3. RequireJS


사실 RequireJS는 프로젝트 중간에 필요에 의해서 사용했다가 마지막 통합 단계에서 RequireJS 때문에 통합이 불가능하게 되면서 다시 모두 걷어낸 Library 입니다. (전문 용어로 삽질이라고 하죠)

RequireJS는 AMD(Asynchronous module definition) 스팩을 실제로 구현한 Library입니다. 

Javascript의 범용성과 표준안을 확립하기 위한 여러 활동 중에서도 Browser 환경(Asynchronous 환경)에 집중해 표준안을 만드는 곳이 AMD라는 곳이구요. 거기에서 내놓은 표준안을 그대로 구현한 Library가 RequireJS입니다.

보다 자세한 내용을 알고 싶으신 분들은 "Javascript 표준을 위한 움직임: CommonJS와 AMD"를 읽어보시는게 좋을 것 같습니다.

프로젝트 중간에 RequireJS를 고려하게 된 이유는 Javascript 파일들의 로딩 순서에 문제가 발생했기 때문입니다.

Web Application을 개발하다 보면 수많은 Javascript 파일들이 생기게 되고 이 js 파일들은 HTML에 <script> 태그를 통해 Web Application에 추가 되게 됩니다.

Browser는 HTML 적혀 있는 순서대로 js 파일 로딩을 시작하게 됩니다.

하지만 네트워크 환경에서는 반드시 시작 순서대로 로딩이 끝난다는 보장이 없죠.

여기서 문제가 발생하게 됩니다. 필요한 js파일의 로딩이 늦어지는 경우가 불특정하게 발생하게 되고, 이런 현상은 바로 에러 상황으로 이어지게 됩니다.

결국 사용자는 잘 되다 안 되다 하는 불안한 Web Application을 접하게 되겠네요.

이런 문제를 해결하기 위해 도입했던 기술이 RequireJS였습니다.

RequireJS는 비동기 방식으로 필요한 js파일을 로딩하는 것이 가능합니다.

각각의 js 파일마다 dependency를 설정할 수 도 있습니다.

RequireJS를 사용하면 HTML문서에서 <script>태그를 이용하는 대신, 필요한 js파일을 Javascript code에서 require() 함수를 이용해 추가할 수 있습니다.

Javascript code에 집중할 수 있으며, 모듈화가 가능하게 되는 잇점을 가질 수도 있습니다.

하지만, 자칫 잘못하면 Callback의 수렁에 빠지는 수도 있으니 조심해야 합니다.

개인적인 느낌으로는 Code 가독성도 상당히 나빠지는 것 같습니다.


프로젝트 마지막에 RequireJS를 다시 모두 걷어 내야 했던, 이른바 "삽질"을 하게 된 이유는 어이 없게도 RequireJS로 인해 통합이 불가능하게 되었기 때문입니다.

RequireJS 기반 Application은 모든 코드가 RequireJS 베이스에서 작성되고 작동되어야 합니다. 그래서 RequireJS 기반이 아닌 Legacy System과는 통합이 불가능 했던 것입니다.

어쩔수 없이 RequireJS를 걷어내는 삽질을 할 수 밖에 없게 됩니다.

그래서 모든 문제가 해결이 되었을까요?

당연히 아니죠....

RequireJS를 도입해서 해결했던 문제(간헐적 에러 발생)가 다시 발생합니다.

결국 해결은 마음에 들진 않지만, 무식한 방법을 사용했습니다. Dependency 가 있는 js 파일들을 하나의 파일로 통합해 버렸습니다. ㅡㅡ;;;;

지금 생각해 보면 lazy load 같은 기법을 사용했으면 깔끔했을 것 같은데, 그 때 당시는 맨붕상태였기 때문에 일단 가능한 쉽고 빠른 방법을 찾을 수 밖에 없었습니다.

그 때 당시의 삽질이 기억나 다소 감정적인 글이 되어 버렸는데, 혹시나 저희와 비슷한 상황에서 RequireJS를 고려하시는 분들이 혹시라도 계신다면 이 글이 조금이나마 도움이 되길 바랍니다. 

 



4. Bootstrap(for AngularJS)


Bootstrap은 반응형, 모바일 웹앱을 위한 HTML, CSS, JavaScript 통합 Framework라고 소개 되고 있지만, 저는 개인적으로 HTML계의 jQuery 정도의 위치를 가지는 기술이라고 생각합니다.

jQuery의 모토인 "최소한의 Javascript code로 최대의 효과를 내기 위한 Framework (Write less, do more)"에서 "Javascript"라는 단어를 "HTML"로 바꾸면 딱 Bootstrap에 어울리는 설명이 되는 것 같거든요.

실제로 Bootstrap을 사용하면 적은 HTML 코드로 훌륭한 Web UI를 구현할 수 있습니다.

그리고, 미적 감각이 상대적으로 떨어지는 개발자라도 Bootstrap을 사용하는 것 만으로도 어느 정도 그럴듯한(?) 결과물을 얻을 수 있기 때문에, 요즘 Web Application 개발자들 사이에서는 거의 필수 요소가 되어가고 있는것 같습니다.

이번 프로젝트에서도 디자이너의 부제 및 개발 기간 단축을 위해 사용하게 되었는데, AngularJS를 기본으로 사용하는 Application에서는 Original Bootstrap를 사용할 수 없는 문제가 있었습니다.

다행히도 AngularJS용 BootStrap이 따로 있어서 그걸 사용하게 되었는데, 일반 Bootstrap의 사용법과는 약간 다른 부분들이 있기 때문에 익숙해 지는데 시간이 조금 필요했던것 같습니다.


프로젝트 마지막 통합 과정에서 RequireJS와 함께 Bootstrap도 문제가 되었습니다.

RequireJS 같은 경우는 하루 정도의 삽질로 끝이 났지만, Bootstrap 같은 경우는 해결하는데는 사흘이 넘는 시간이 걸렸던것 같습니다.

문제가 되었던 부분은 고객사가 사용하는 UI Library와 Bootstrap과의 총돌이었는데, 고객사에서는 Bootstrap 코드를 약간 수정해서 고객사 전용 UI Component를 사용하고 있었습니다.

그런데 고객사가 사용했던 Bootstrap의 버전과 저희가 사용했던 Bootstrap의 버전이 서로 달랐던것 같습니다.

저희 Bootstrap용 js 파일을 추가하면 다른 화면들이 모두 깨지고, 고객사의 Bootstrap 파일을 그대로 사용하면 우리가 만든 Application의 화면이 깨지는 문제가 발생했습니다.

버전따라 사용법이 서로 달랐던 것 같습니다.

결국 고객사의 UI용 js파일을 사용하고 저희 Application은 수정하기로 결정하고, 고객사의 bootstrap.js 소스코드를 분석하면서 깨지는 UI Component들을 하나씩 수정하는 삽질을 했습니다.

충돌나는 부분을 jQuery UI로 변경하는 것으로 해결 할 수도 있었는데, 그렇게 하면 Lock & Feel 이 안 맞을 수 있기 때문에 그 때 당시에는 선택할 수 없었습니다.

결국은 프로젝트 완료 후 코드 관리의 효율성을 위해 Bootstrap UI 중에서 충돌이 났던 Component들을 모두 jQuery UI로 변경하는 작업을 다시 한번 수행하게 됩니다.




5. D3.js


D3.js는 Data Visualization Javascript Framework입니다.

단순하게 Web에서 Graph를 그리기 위한 Library라고 생각하실 수도 있지만, 그것보다는 훨씬 강력한 기능들을 가지고 있는 Framework입니다.

가령, 전국의 현재 기온을 한눈에 볼 수 있는 Bar Chart가 있다고 생각해 봅시다. 전국의 현재 기온을 표로 보는 것보다는 Bar Chart로 보는 것이 휠씬 보기도 좋기 이해도 잘 될거라고 생각합니다.

여기서 조금만 발전 시켜 보죠. 

기존 Bar Chart에 하룻동안의 기온 변화를 애니메이션로 표현하는 기능을 추가해 보면 어떨까요?

원래는 지역과 기온, 이렇게 두가지의 정보를 가지고 있던 그래프에 시간이라는 정보를 추가하는 거죠.

Bar Chart 하단에 시간을 선택할 수 있는 스크롤바가 있고, 그 스크롤바를 마우스로 드래그 할때 마다 Bar Chart가 변하게 하면 될 것 같습니다.

전국의 기온을 한눈에 볼수 있을 뿐만 아니라 하룻동안의 기온차를 시각적으로 볼수 있는 새로운 Graph가 탄생했습니다. 

Data Visualization이라는 것은 알고 보아왔던 형태의 그래프 뿐만 아니라, 다양한 아이디어와 기술을 적용해 새로운 정보를 재생산하는 기술입니다.

그리고 D3.js는 이러한 Data Visualization을 실제로 웹에서 구현할 수 있도록 다양한 API를 제공하는 Library입니다.


이번 프로젝트에서는 사실 D3.js를 직접적으로 사용하지는 않았습니다.

D3.js를 기반으로 한 다른 Library들을 사용하기 위해서 프로젝트에 포함한 거라서, 사실 사용 소감이라고 적을만 한 것이 없네요.

그래도 개인적으로 관심있는 분야라 D3.js에 대해서는 따로 카테고리를 만들어서 연재해 볼까 생각하고 있습니다.





6. Dagre-D3


Dagre-D3는 Directed Graph(방향 그래프)를 쉽게 그릴 수 있는 Javascript Library 입니다. D3 기반으로 작성되어 있기 때문에 D3.js가 기본으로 필요합니다.

Server Map을 Directed Graph로 그려야 해서 도입한 기술입니다.

Directed Graph를 지원하는 Javascript Library가 몇가지 있었지만, 대부분 범용으로 Diagram을 그릴 수 있는 것들이었고, 저희는 Directed Graph만 그리면 되었기 때문에 Directed Graph에 특화된 Dagre-D3를 선택하게 되었습니다..

Dagre-D3를 사용함으로써, 쉽고 빠르게 일정 수준 이상의 퀄리티를 가지는 결과물을 얻을 수 있었던 것 같습니다.

Directed Graph가 프로젝트의 메인 기능이었기 때문에 전체 프로젝트에서 작업 시간도 가장 많이 할당했었고, 삽질도 가장 많이 한 기능이긴 합니다.

하지만, 여전히 고객사의 다양한 요구사항을 모두 충족시키기에는 기능이 부족했던 것도 사실입니다.

(예를 들어 그룹핑 되어 있는 노드를 펼쳤을 때 노드의 배치가 이쁘지 않다던지 하는....)


결국 요구사항을 모두 충족 시키기 위해서는 D3.js를 사용해 직접 Directed Graph를 구현할 수 밖에 없을 것 같습니다.

물론 시간과 비용이 그만큼 더 들어가겠죠.

현실적으로는 여러 Library들의 기능들을 파악 한 다음에 요구사항을 타협해서 적당한 Library를 선택해서 사용하는 것이 개발사나 고객사 모두에게 좋지 않을까 생각합니다.


Dagre-D3의 장단점을 나열 하자만 다음과 같습니다.

1. 장점

- 안정적인 Rendering 속도

- 사용법이 간단하다.

- Layout만 지정해 주면 노드들은 알아서 배치해 준다.

- 원하는 대로 수정할 수 있을 만큼 충분히 다양한 디자인 요소를 제공해 준다.

- 노드를 Group으로 묶는 것이 가능하다. ()

2. 단점

- 사용자가 마음대로 노드를 움직할 수 없다.

- 실시간으로 Layout을 바꾸는 것은 불가능하다.

- 노드를 그룹핑하고 푸는 것도 당연히 가능하지만, 풀었을 때 노드들의 배치가 엉망이다. (이쁘지 않다)

- 노드들의 위치를 고정할 수 없다. (새로운 노드가 추가되거나 기존 노드가 삭제되면 전체적인 배치가 다 바뀌어 버린다)

 

더 자세한 내용은  "주니어 개발자의 경험기 [2편 - Javascript 시각화 라이브러리]" 를 보시면 될 것 같습니다.



7. C3


D3 기반 Chart 지원 Library 입니다.

선그래프, 막대그래프,파일그래프 등 일반적인 형태의 Chart들을 그리기 위해 사용했습니다.

C3.js에 대한 내용은 "주니어 개발자의 경험기 [2편 - Javascript 시각화 라이브러리]" 를 읽어보시는게 더 좋을 것 같네요.




8. Big Scatter Chart


Scatter Chart(분산,분표도 그래프)는 API Call의 응답 시간을 한눈에 보기 위한 용도로 사용했습니다.

X축은 시간(24시간), Y축은 응답시간으로 호출 하나 마다 그래프에 점을 하나씩 찍어 그리는 그래프 입니다.

원래는 Jennifer에서 X-View라는 이름으로 동일한 기능을 지원하는 Library를 제공하고 있습니다.

Jennifer 뿐만 아니라 Scatter Chart를 지원하는 Library는 몇가지 있었는데, 그 Library들의 공통된 문제점은 HTML5의 그래픽 요소인 Canvas와 SVG중 상대적으로 느린 SVG를 사용한다는 것이었습니다.

백터 기반의 SVG는 다양한 그래픽 요소를 자유롭게 표현하기에 적합하지만, CPU 연산에 의존해 이미지를 생성하기 때문에 픽셀 기반의 Canvas 보다 느린 문제점이 있습니다.

실제로 SVG 기반의 Scatter Chart Library를 사용해 본 결과 고객사의 요구사항을 충족하긴 힘들었습니다.

(하룻동안의 데이터를 모두 출력하기 위해서는 Scatter Chart는 약 8천만건의 Dot를 한번에 찍어야 함)

그래서 구글링중 발견한 것이 Big Scatter Chart 입니다. 

찾고 보니 Pinpoint에서도 사용하고 있는(사실은 Pinpoint에서 사용할려고 만든) Library 더군요.

Canvas기반의 Library로 다양한 사용자 Interaction을 지원하지는 못하지만, 대량의 데이터를 표현하는데는 훌륭한 성능을 보였습니다.


Dagre-D3.js에 대한 기술적인 내용은 "주니어 개발자의 경험기 [2편 - Javascript 시각화 라이브러리]" 에 잘 정리되어 있습니다.


이번 프로젝트를 진행하면서 전체적으로 느꼈던 점은, 이제 정말 데이터 레이어와 프리젠테이션 레이어가 확실히 분리되어 가고 있으며, 프리젠테이션 레이어쪽 기술들이 많이 정리가 되어가고 있는 것 같다는 인상을 받았습니다.

PHP로 데이터에서부터 HTML문서까지 모두 작업하던 시대와는 참으로 많이 달라진것 같습니다.

아마 AngularJS를 통해 Javascript code 단이 깔끔하게 정리되는 것을 보면서 더 그렇게 느꼈던 것 같습니다.


항상 Javascript와 관련된 새로운 기술들을 접하면 두렵기도 하면서 한편으로는 신나기도 하는 것는 것이, 역시 저는 Javascript 오덕이었던 듯 합니다.

(그래도 Callback의 수렁은 정말 싫습니다.)


다음 글은 E2E-Monitor의 Back-end 기술들을 살펴 보도록 하겠습니다.

긴글 읽어 주셔서 감사합니다.



Posted by 알 수 없는 사용자
,

ScyllaDB의 Benchmark 따라하기 2

지난 포스팅에서는 VirtualBox를 통해서 ScyllaDB와 Cassandra의 성능을 테스트해보았다. 결과에서 ScyllaDB 홈페이지에서 주장하던 10배의 성능 향상을 볼 수는 없었다. 몇가지 조건은 오히려 ScyllaDB쪽에 불리한 것도 있었기 때문에 이번에는 사내에 있는 개발장비에서 성능테스트를 진행해봤다. 사내에서 테스트에 활용할 수 있는 개발장비는 다행히 3대가 있었다.


사내에는 남는 개발장비가 3대 있다. 시스템 사양은 각각 다음과 같다.

DB서버(ScyllaDB/Cassandra)

  • CPU : 4 Core
  • 메모리 : 16G
  • HDD : 128G SSD
부하 테스트 서버
  • CPU : 2 Core
  • 메모리 : 16G
  • HDD : 250G SSD
1) 쓰기 테스트
쓰기 테스트에 사용한 명령은 앞서 진행했던 VirtualBox 테스트에 사용한 명령어와 동일하다.
cassandra-stress write duration=10m -mode native cql3 -rate threads=700 -node $SERVER

사내 개발장비에서 성능테스트를 한 결과는 다음과 같다. 
ScyllaDB 평균 TPS : 56191

Cassandra 평균 TPS : 58621

오히려 Cassandra가 더 좋은 성능을 보여주고 있다.


2) 읽기 테스트

VirtualBox 테스트와 마찬가지로 데이터를 먼저 채워넣은 후 읽기 테스트를 진행했다.

읽기 테스트는 다음의 명령은 다음과 같다.

cassandra-stress mixed 'ratio(read=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=700 -node $SERVER

결과는 다음과 같다.

ScyllaDB 평균 TPS : 47363

Cassandra 평균 TPS : 56500

읽기에서도 오히려 Cassandra가 더 좋은 성능을 보여주고 있다.


3) 읽기/쓰기 테스트

테스트에 사용한 명령은 다음과 같다.

cassandra-stress mixed 'ratio(read=1,write=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=700 -node $SERVER

결과는 다음과 같다.

ScyllaDB 평균 TPS

   읽기 : 25183, 쓰기 : 25146

Cassandra 평균 TPS

   읽기 : 27023, 쓰기 : 27038


결과를 종합해서 살펴보면 다음과 같다.

 

 ScyllaDB

Cassandra 

쓰기

56191

58621

읽기

47363

56500

읽기/쓰기 

 25183/25146

27023/27038 

<표 1. 사내 개발장비에서의 benchmark결과>

테스트 결과 ScyllaDB가 말하는 "Cassandra보다 10배 빠르다"는 확인할 수 없었다. 오히려 Cassandra보다 떨어지는 성능을 보여주고 있었다.

그런데 이번 테스트에서는 ScyllaDB 홈페이지에서 진행한 것 처럼 여러대의 Client에서 부하를 준 것이 아니라 한대의 시스템에서만 부하를 준 것이다. 이번 결과로 확인할 수 있었던 것은 그럭저럭한 사양의 시스템에서 그리 많지 않은 수준의 Request가 들어오는 경우에는 Cassandra가 더 빠를 수 있다는 것이다.

사내 개발장비에서는 여러대의 클라이언트에서 부하를 주는 등의 테스트 진행이 불가능하기 때문에 다음번 포스트에서는 아마존 AWS에서 테스트를 진행해보도록 할 계획인다.

Posted by 알 수 없는 사용자
,


ScyllaDB의 Benchmark 따라하기 1

ScyllaDB가 주장하는 10배 빠르다는 사실을 확인하기 위해서 ScyllaDB 홈페이지의 Scylla vs. Cassandra benchmark에 나와있는 내용을 직접 확인해보도록 하자.

홈페이지에 나와있는대로 하려면 DB서버는 24Core CPU, 128G 메모리가 있어야 한다. NIC도 DB서버에는 10Gbps를 사용했다.

우선 그런건 다 무시하고 PC에서 VirtualBox를 통해서 ScyllaDB와 Cassandra를 테스트해봤다. 테스트 환경은 다음과 같다.

DB 서버 (ScyllaDB/Cassandra)

  • CPU : 1 Core
  • 메모리 : 2G
  • HDD : 20G 고정크기 저장소

부하 테스트 서버

  • 없음. DB서버하고 같이 사용함.
1) 쓰기 테스트
다음의 명령을 사용하여 테스트했다.
cassandra-stress write duration=10m -mode native cql3 -rate threads=700 -node $SERVER

ScyllaDB 홈페이지에서도 사용한 명령을 적어주긴 했는데 대충 적은듯한 티가 많이 난다. duration=15min으로 되어있는 부분은 duration=15m이 되어야 한다.

VirtualBox에서 테스트한 결과는 다음과 같다.

ScyllaDB 평균 TPS : 20636

Cassandra 평균 TPS : 19789


2) 읽기 테스트

읽기 테스트를 진행하기 전에 다음의 명령으로 채워넣어놨다.

cassandra-stress write n=10000000 -pop "seq=1..100000000"  -mode native cql3 -rate threads=700 -node $SERVER

읽기 테스트는 다음의 명령을 사용했다.

cassandra-stress mixed 'ratio(read=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=700 -node $SERVER

ScyllaDB에 테스트를 할 때 데이터가 늦게 나온다 싶더니 나오라는 결과는 안나오고 다음의 메시지만 나오고 있었다.

com.datastax.driver.core.exceptions.ReadTimeoutException: Cassandra timeout during read query at consistency LOCAL_ONE (1 responses were required but only 0 replica responded)

메시지가 말해주는건 다음과 같다.

"LOCAL_ONE Consistency level을 가지고 read를 수행하려 했는데 timeout이 발생했다! LOCAL_ONE Consistency level에서는 1개의 응답이 와야 하는데 아무것도 못받았다"

아무래도 thread 700개는 무리였던 것 같다. ScyllaDB 홈페이지에서 테스트에 사용했던 시스템은 24core에 128Gb 메모리를 가진 시스템이었기 때문에 thread를 300개로 낮춰서 다시한번 테스트한 결과는 다음과 같다.

ScyllaDB 평균 TPS : 236

Cassandra 평균 TPS : 150

ScyllaDB가 약 65%의 성능 향상이 있는 것으로 결과가 나오긴 했지만 테스트 중 ScyllaDB는 ReadTimeoutException이 한두번 발생하기는 했다. ReadTimeoutException은 Cassandra에서는 발생하지 않았다.


3) 읽기/쓰기 테스트

읽기/쓰기 테스트는 읽기 테스트에 사용했던 명령에 ratio만 read=1,write=1로 수정해서 테스트했다. 그런데 Cassandra에서 ReadTimeoutException이 많이 발생해서 thread 수를 300 -> 100 -> 50 으로 줄여나갔다. 

cassandra-stress mixed 'ratio(read=1,write=1)' duration=10m -pop 'dist=gauss(1..10000000,5000000,500000)' -mode native cql3 -rate threads=100 -node $SERVER

결과는 다음과 같다.

ScyllaDB 평균 TPS

   읽기 : 249, 쓰기 : 255

Cassandra 평균 TPS

   읽기 : 114, 119


결과를 종합해서 살펴보면 다음과 같다.

 

 ScyllaDB

Cassandra 

쓰기

 20636

 19789

읽기

 236

150

읽기/쓰기 

 230/234

 114/119

<표 1. VirtualBox에서의 benchmark결과>

ScyllaDB가 아주 약간의 성능 향상이 보여지기는 했다. 그리고 특히 읽기/쓰기 테스트에서는 Cassandra에서만 ReadTimeoutException이 발생해서 Thread 수를 줄여나가야 했다.


아무래도 테스트 환경이 VirtualBox이고 매우 낮은 사양의 VM이라서 이런 결과가 나온 것일 수 있다. 특히 VM마다 CPU Core를 하나만 할당했기 때문에 Request당 하나의 CPU를 할당하는 ScyllaDB는 좀 더 불리한 상황에서 테스트를 진행한 것이다.

아무튼 ScyllaDB가 Cassandra보다 약간 빠른 성능을 보여주는 것은 확인되었다. 다음번에는 사내 개발장비에서 테스트를 진행해보도록 할 계획이다.



Posted by 알 수 없는 사용자
,

최근 회사에서 storm kafka에 대해 공부하면서 작은 미니 프로젝트를 했는데 공부했던 경험을 정리도 할겸 포스팅을 한다.

먼저 storm과 kafka에 대해 간단히 알아보도록 하자. 


storm 


storm은 실시간 분산 처리 시스템이고, 방대한 양에 데이터 스트림을 안정적으로 처리한다. storm은 실시간 분석, 머신러닝 등에 사용된다. storm 클러스터는 Hadoop 클러스터와 표면적으로 유사한데 Hadoop에서  "MapReduce job"을 실행하는 반면에, storm은 "topology"를 실행시킨다.  "jobs" 와 "topology"는 매우 다른데 한가지 핵심적으로 다른 점은 MapReduce job 은 결국 끝나게 되지만 토폴로지는 kill하지 않는 이상 계속 message를 처리한다. 

storm에 대한 좀 더 자세한 설명은 링크링크2 를 확인해 보기 바란다.








kafka


kafka는 LinkedIn에서 자신들의 내부 데이터 처리를 위해 개발한 Distriubted Processing Message System이다. 전통적인 메세지큐시스템과는 다르게 Apache Kafka는 Broker Cluster를 여러 대의 Machine으로 구성하여 분산처리가 가능하다는 장점을 가지고 있다. Big Data시장의 발전과 함께 가장 주목받고 있는 Queue System이기도 하다. 

Kafka에 대한 좀 더 자세한 설명은 링크를 확인해 보기 바란다.  

아래의 사이트도 설명이 잘 되어있다.

http://epicdevs.com/17 

http://kafka.apache.org/documentation.html#introduction




storm kafka에 대한 간단한 소개를 마쳤으니, 이제 내가 했던 작은 프로젝트에 대해 간단히 설명하고 프로젝트에 대한 step 하나 하나 설명해 보도록 하겠다. 


storm kafka 미니 프로젝트 


producer에서 로그 파일을 읽어  message를 broker(연습 용이므로 broker는 하나만 사용한다)로 publish하고  storm spout에서 consume하여 bolt에서 처리하도록 한는것이 프로젝트 목표이다.




로그 파일의 log는 다음과 같은 형태로 되어 있다고 가정한다.

@timestamp : 2015-11-10T15:32:06.046+09:00; doctype : sns; key : 974cfc83-99e0-420e-bfd1-2262e4e82dbd; appid : com.facebook.katana; appversion : 48


개발환경은 ubuntu 14.04를 기반으로 한다. 


step 1. kafka 준비 (kafka 다운로드 페이지)


다운 받은 kafka 앞축을 풀고 설치된 kafka디렉토리로 이동한다. 


(1)zookeeper 서버 실행 (zookeepr에 대한 자세한 설명은 여기 링크를 참조 하기 바란다.  )

$ bin/zookeeper-server-start.sh config/zookeeper.properties


(2)kafka서버 실행 

$ bin/kafka-server-start.sh config/server.properties


(3)topic 만들기 

kafka의 broker는 topic이라는 기준으로 메시지를 관리한다. producer에서 특정 topic의 메시지를 생성 한 후 broker에 전달하면 broker는 전달받은 메시지를 topic별로 분류하여 쌓아놓는다.  "onlytest"라는 이름의 topic을 생성하도록 하겠다. 

$ bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic onlytest



step 2. producer 준비 



log 파일을 읽어 message를 send하는 간단한 코드이다. 추가 properties 설정에 대한 자세한 사항은 이 링크에서 확인해 보기 바란다.


import java.io.File;

import java.util.Properties;

import org.apache.commons.io.input.Tailer;

import org.apache.commons.io.input.TailerListenerAdapter;

import kafka.producer.KeyedMessage;

import kafka.producer.ProducerConfig;


public class TestProducer {

private static final int SLEEP = 500;

public kafka.javaapi.producer.Producer<String,String> producer;

public void setConfig(){

Properties properties = new Properties();

       properties.put("metadata.broker.list","localhost:9092"); // broker list 필수!

       properties.put("serializer.class","kafka.serializer.StringEncoder"); //메시지를 serialize할때 사용하는 인코더

       ProducerConfig producerConfig = new ProducerConfig(properties);

       producer = new kafka.javaapi.producer.Producer<String, String>(producerConfig);

}

public static void main(String[] args) throws InterruptedException{

      

       TestProducer testProducer = new TestProducer();

       testProducer.setConfig();

       testProducer.run();

   }

private void run() throws InterruptedException {

OnlyLogListenter onlyLogListenter = new OnlyLogListenter(producer);

       Tailer tailer = Tailer.create(new File("your file path"), onlyLogListenter,SLEEP);

       while(true){

        Thread.sleep(SLEEP);

       }

   }

public class OnlyLogListenter extends TailerListenerAdapter{

kafka.javaapi.producer.Producer<String,String> producer;

public OnlyLogListenter(kafka.javaapi.producer.Producer<String,String> producer){

this.producer = producer;

}

@Override

public void handle(String line){

System.err.println(line);

KeyedMessage<String, String> message =new KeyedMessage<String, String>("onlytest",line);

    producer.send(message);

}

}

}

 

step 3. storm 준비 


(1) storm 다운받기 

storm은 요기 링크에서 다운로드 받으면 된다.(source code가 아닌 release 버전으로 다운로드 한다.) 나는 0.9.5버전을 다운로드 받았다.압축을 풀고 해당 디렉토리로 이동한다. 

나의 경우는 local에서 production cluster 모드로 테스트 하려고 하기 때문에  conf 디렉토리에 storm.yaml을 수정하도록 하겠다.

$ vi ~스톰 디렉토리/conf/storm.yaml


(2) storm.yaml 파일 수정하기 

아래와 같은 항목을 storm.yaml 파일에 추가하도록 하자  nimbus라는 것을 발견할 수 있는데 이에 대한 설명은 다음 링크에서 확인해 볼수 있다.

#zookeeper 서버 설정. local에서 production cluster모드로 테스트 해볼 것이니 실제 아이피 주소로 셋팅하자.

storm.zookeeper.servers:

      - 192.168.0.11


#nimbus host 및 seed 설정. local에서 production cluster모드로 테스트 해볼 것이니 실제 아이피 주소로 셋팅하자.

nimbus.host: 192.168.0.11

nimbus.seed: "192.168.0.11"


#storm local dir 설정 

storm.local.dir: "storm local dir path "


#storm ui  

ui.port : 8087


위와 같이 파일을 수정하고 저장한다. 


step 4. storm topology 만들기  


kafka broker에 전달된 메세지를 spout에 consume하여 bolt에서 처리 하도록 하는 코드이다. 




(1) zookeeper url을 설정한다. 

storm을 local cluster 모드로 테스트 해보자고 한다면 zookeeper 는 "localhost:2181"이 될것이다. 만약에 production cluster모드로 local에서 테스트 해보자고 한다면 "127.0.0.1:2181" 이 아닌 자신의 "실제  ip 주소 : 2181"로 셋팅하면 된다. 

String zkUrl = "zookeeper url:2181";


(2)kafkaspout설정을 해준다. 

아래 코드에 대해 좀더 자세히 알아 보고 싶으면 다음 링크에서 확인해 볼수 있다.

ZkHosts hosts = new ZkHosts(zkUrl);

SpoutConfig spoutConfig = new SpoutConfig(hosts, "onlytest", "/onlytest", UUID.randomUUID().toString());

spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());

KafkaSpout kafkaSpout = new KafkaSpout(spoutConfig);


(3)spout과 bolt설정을 해준다. 

shuffleGrouping,fieldsGrouping 이라고 보이는데 이것을 stream grouping이라하고 토폴로지에  두개의 컴포넌트사이에서 어떻게 튜플을 send할지 알려주는 것을 말한다. 여기서 쓴 shuffleGrouping은 튜플을 무작위로 동일한 비율로 나눠서 볼트에 task를 할당하는 것이고, fieldsgrouping은 튜플에 있는 필드 값을 기준으로 파티셔닝되어 각 볼트 task에 튜플을 할당한다.  그 외 다른 stream grouping을 살펴보고 싶다면 다음 링크에서 확인해 볼 수 있다. 

TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("spout", kafkaSpout, 1);

builder.setBolt("cutbolt", new CutLogBolt(), 8).shuffleGrouping("spout");

builder.setBolt("classifybolt", new ClassifyKeyBolt(), 8).fieldsGrouping("cutbolt",new Fields("key","doctype"));

builder.setBolt("docbolt", new DoctypeCountBolt(), 8).fieldsGrouping("classifybolt",new Fields("subdoctype"));


(4) nimbus host,storm local dir 등등을 설정해준다.

local mode와 cluster mode를 제량에 따라 설정할 수 있는데 나는 local에서 production cluster mode로 실행 하려고 했기 때문에 아래와 같이 설정했다. 

//=============================

// local mode

//=============================

// LocalCluster cluster = new LocalCluster();

// cluster.submitTopology("log-stat", conf, builder.createTopology());

// Thread.sleep(10000);

// cluster.shutdown();

//=============================

// cluster mode

//=============================

conf.put(Config.NIMBUS_HOST, "nimbus url");

conf.put(Config.STORM_LOCAL_DIR,"your storm local dir");

conf.put(Config.NIMBUS_THRIFT_PORT,6627);

conf.put(Config.STORM_ZOOKEEPER_PORT,2181);

conf.put(Config.STORM_ZOOKEEPER_SERVERS,Arrays.asList(new String[] {"zookeeper url"}));

// conf.setNumWorkers(20);

// conf.setMaxSpoutPending(5000);

StormSubmitter.submitTopology("onlytest", conf, builder.createTopology());


아래는 최종 topology 코드이다. 

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

import java.util.UUID;


import soeun.storm.kafka.bolt.ClassifyKeyBolt;

import soeun.storm.kafka.bolt.CutLogBolt;

import soeun.storm.kafka.bolt.DoctypeCountBolt;

import storm.kafka.KafkaSpout;

import storm.kafka.SpoutConfig;

import storm.kafka.StringScheme;

import storm.kafka.ZkHosts;

import backtype.storm.Config;

import backtype.storm.StormSubmitter;

import backtype.storm.spout.SchemeAsMultiScheme;

import backtype.storm.topology.TopologyBuilder;

import backtype.storm.tuple.Fields;


public class StormKafakaSimpleTopology {

  

   public static void main(String[] args) throws Exception {


       String zkUrl = "zookeeper url:2181";        // zookeeper url

       String brokerUrl = "localhost:9092";


       if (args.length > 2 || (args.length == 1 && args[0].matches("^-h|--help$"))) {

           System.out.println("Usage: TridentKafkaWordCount [kafka zookeeper url] [kafka broker url]");

           System.out.println("   E.g TridentKafkaWordCount [" + zkUrl + "]" + " [" + brokerUrl + "]");

           System.exit(1);

       } else if (args.length == 1) {

           zkUrl = args[0];

       } else if (args.length == 2) {

           zkUrl = args[0];

           brokerUrl = args[1];

       }


       System.out.println("Using Kafka zookeeper url: " + zkUrl + " broker url: " + brokerUrl);


       ZkHosts hosts = new ZkHosts(zkUrl);

       SpoutConfig spoutConfig = new SpoutConfig(hosts, "onlytest", "/onlytest", UUID.randomUUID().toString());

       spoutConfig.scheme = new SchemeAsMultiScheme(new StringScheme());

       KafkaSpout kafkaSpout = new KafkaSpout(spoutConfig);

       TopologyBuilder builder = new TopologyBuilder();

builder.setSpout("spout", kafkaSpout, 1);

builder.setBolt("cutbolt", new CutLogBolt(), 8).shuffleGrouping("spout");

builder.setBolt("classifybolt", new ClassifyKeyBolt(), 8).fieldsGrouping("cutbolt",new Fields("key","doctype"));

builder.setBolt("docbolt", new DoctypeCountBolt(), 8).fieldsGrouping("classifybolt",new Fields("subdoctype"));

Config conf = new Config();

conf.setDebug(true);

List<String> nimbus_seeds = new ArrayList<String>();

nimbus_seeds.add("nimbus url");


if (args != null && args.length > 0) {

conf.setNumWorkers(3);


StormSubmitter.submitTopologyWithProgressBar(args[0], conf, builder.createTopology());

}

else {


//=============================

// local mode

//=============================

// LocalCluster cluster = new LocalCluster();

// cluster.submitTopology("log-stat", conf, builder.createTopology());

// Thread.sleep(10000);

// cluster.shutdown();

//=============================

// cluster mode

//=============================

conf.put(Config.NIMBUS_HOST, "nimbus url");

conf.put(Config.STORM_LOCAL_DIR,"your storm local dir");

conf.put(Config.NIMBUS_THRIFT_PORT,6627);

conf.put(Config.STORM_ZOOKEEPER_PORT,2181);

conf.put(Config.STORM_ZOOKEEPER_SERVERS,Arrays.asList(new String[] {"zookeeper url"}));

// conf.setNumWorkers(20);

// conf.setMaxSpoutPending(5000);

StormSubmitter.submitTopology("onlytest", conf, builder.createTopology());


}

}

}


CutLogBolt.java

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class CutLogBolt extends BaseBasicBolt{

@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String[] splitArray = input.getString(0).split(";");

String key = "";

String doctype = "";

for(int i = 0; i < splitArray.length; i++){

if(splitArray[i].contains("key"))

key  = splitArray[i];

if(splitArray[i].contains("doctype"))

doctype = splitArray[i];

}

collector.emit(new Values(key,doctype));

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("key","doctype"));

}


}


ClassifyKeyBolt.java

import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class ClassifyKeyBolt extends BaseBasicBolt{


@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String[] splitdoctype = input.getStringByField("doctype").split(":");

String[] splitkey = input.getStringByField("key").split(":");

if(splitkey.length == 2 && splitdoctype.length == 2){

String doctype  = splitdoctype[1].trim();

String key  = splitkey[1].trim();

// System.err.println(key + ":" + doctype);

collector.emit(new Values(key + ":" + doctype));

}

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("subdoctype"));

}

}


DoctypeCountBolt.java 

import java.util.HashMap;

import java.util.Map;


import backtype.storm.topology.BasicOutputCollector;

import backtype.storm.topology.OutputFieldsDeclarer;

import backtype.storm.topology.base.BaseBasicBolt;

import backtype.storm.tuple.Fields;

import backtype.storm.tuple.Tuple;

import backtype.storm.tuple.Values;


public class DoctypeCountBolt extends BaseBasicBolt {

Map<String,Integer> docMap = new HashMap<String,Integer>();

@Override

public void execute(Tuple input, BasicOutputCollector collector) {

String doctype = input.getStringByField("subdoctype");

Integer count = docMap.get(doctype);

if(count == null)

count = 0;

count++;

docMap.put(doctype, count);

System.out.println(docMap);

collector.emit(new Values(docMap));

}


@Override

public void declareOutputFields(OutputFieldsDeclarer declarer) {

declarer.declare(new Fields("docmap"));

}

}


(5) pom.xml 


pom.xml 설정할 때 주의 할 것은 storm과 kafka의 버전의 따른 dependency 를 꼭 확인해 보고 pom.xml에 추가해야 한다는 것이다.

현재 나는 storm 버전이 0.9.5이기 때문에 

storm-core : 0.9.5

storm-kafka : 0.9.5

앞서 설치한 kafka는  kafka_2.10_0.8.1 이기 때문에 버전에 맞게 설정했다. 버전에 맞지 않으면 엄청난 삽질을 하게 된다. 

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

 <modelVersion>4.0.0</modelVersion>

 <groupId>com.soeun.storm</groupId>

 <artifactId>ministorm</artifactId>

 <version>0.0.1-SNAPSHOT</version>

 <packaging>jar</packaging>

 <name>ministorm</name>

 <url>http://maven.apache.org</url>

 <properties>

   <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>

 </properties>

<dependencies>

 <dependency>

   <groupId>junit</groupId>

   <artifactId>junit</artifactId>

   <version>4.11</version>

   <scope>test</scope>

 </dependency>

<dependency>

<groupId>org.apache.storm</groupId>

<artifactId>storm-core</artifactId>

<version>0.9.5</version>

<scope>provided</scope>

</dependency>

 <dependency>

     <groupId>org.apache.storm</groupId>

     <artifactId>storm-kafka</artifactId>

     <version>0.9.5</version>

   </dependency>

   <dependency>

<groupId>org.testng</groupId>

<artifactId>testng</artifactId>

<version>6.8</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.mockito</groupId>

<artifactId>mockito-all</artifactId>

<version>1.9.0</version>

<scope>test</scope>

</dependency>

<dependency>

<groupId>org.apache.kafka</groupId>

<artifactId>kafka_2.10</artifactId>

<version>0.8.1</version>

<exclusions>

       <exclusion>

         <groupId>org.apache.zookeeper</groupId>

         <artifactId>zookeeper</artifactId>

       </exclusion>

       <exclusion>

               <groupId>com.sun.jmx</groupId>

               <artifactId>jmxri</artifactId>

           </exclusion>

           <exclusion>

               <groupId>com.sun.jdmk</groupId>

               <artifactId>jmxtools</artifactId>

           </exclusion>

           <exclusion>

               <groupId>javax.jms</groupId>

               <artifactId>jms</artifactId>

           </exclusion>

     </exclusions>

</dependency>

</dependencies>

<build>

 <plugins>

   <plugin>

     <artifactId>maven-assembly-plugin</artifactId>

     <version>2.2.1</version>

     <configuration>

       <descriptorRefs>

         <descriptorRef>jar-with-dependencies</descriptorRef>

       </descriptorRefs>

       <archive>

         <manifest>

           <mainClass />

         </manifest>

       </archive>

     </configuration>

     <executions>

       <execution>

         <id>make-assembly</id>

         <phase>package</phase>

         <goals>

           <goal>single</goal>

         </goals>

       </execution>

     </executions>

   </plugin>

 </plugins>

</build>

</project>


(6) maven install 


앞에 코드가 작성되어있는 프로젝트 디렉토리에 이동한후 maven clean install해준다.  

$ mvn clean install


target폴더에  ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar 파일이 생긴 것을 확인 할 수 있을 것이다. 


step 5. storm nimbus,supervisor,ui 실행 


스톰이 설치 되어 있는 디렉토리로 이동하여 nimbus,supervisor,ui를 실행 시킨다. 

$ bin/storm nimbus


$ bin/storm supervisor


$ bin/storm ui


ui를 실행시키면 http://localhost:8087로 접근하여 topology 상황을 웹으로 확인해 볼 수 있다. 



step 6.  ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar summit 


ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar 를 아래와 같이 실행 시킨다 


$ bin/storm jar ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar {main class path} 


ex)

bin/storm jar ministorm-0.0.1-SNAPSHOT-jar-with-dependencies.jar soeun.storm.kafka.topology.StormKafakaSimpleTopology 


정상적으로 submit 하면 다음과 같은 메세지가 출력된다.

..............


[main] INFO  backtype.storm.StormSubmitter - Finished submitting topology: onlytest


http://localhost:8087로 접근하면 아래와 같은 화면이 나올것이다 Topology summary에 "onlytest"라는 이름이 있으면 성공한 것이다.




step 7.  test

 

이제 위에서 작성하였던 TestProducer를 실행시켜 "onlytest"라는 topic의 메세지가 broker에 전송하여 storm worker 로그에 잘 찍히는지 확인해 보자 log는 storm디렉토리/logs/worker-.. .log에서 확인해 볼수 있다. 

$ tail -f worker-{}.log 

 또한 storm ui 에 Topology Visualization에서도 확인해 볼 수 있다. 




예제 소스는 다음 링크에 존재한다. 


-끝-


p.s storm kafka에 대한 좀더 자세한 사항을  알아보고자 한다면 다음 레퍼런스를 참고하기 바란다. 


참고 레퍼런스:

http://storm.apache.org/index.html

https://storm.apache.org/documentation/Setting-up-development-environment.html

http://bcho.tistory.com/995

https://github.com/apache/storm/blob/master/docs/documentation/Tutorial.md

https://github.com/apache/storm/tree/master/examples/storm-starter

https://storm.apache.org/documentation/Tutorial.html

Posted by 알 수 없는 사용자
,

ScyllaDB 소개

NOSQL 2015. 11. 26. 18:44

Cassandra는 대표적인 Column Family Oriented NoSQL 중 하나이다. Cassandra의 Use Cases 페이지(http://www.planetcassandra.org/apache-cassandra-use-cases/)만 들어가봐도 얼마나 많이 사용되고 있는지 알 수 있다. 

Cassandra가 Java로 만들어져있다는 것은 대부분 알고있는 사실이다. Java 오픈소스 중 C/C++로 구현하는 것은 어렵지 않게 찾아볼 수 있다. Lucene과 CLucene, HBase와 HyperTable 이 대표적인 예이다. Cassandra도 이와 같이 C++로 구현한 오픈소스가 있다. ScyllaDB라는 NoSQL은 Cassandra와 호환되는 Column Family Oriented NoSQL이다.

ScyllaDB의 홈페이지(http://www.scylladb.com/)에서는 다음과 같이 믿기 힘든 내용을 볼 수 있다.


<그림 1> ScyllaDB 홈페이지에 있는 소개 문구

Cassandra와 완전히 호환되면서 10배나 빠르다는 내용을 볼 수 있다. 2배정도 빠르다고 하면 순진한 척 하고 믿어줄 수는 있다. 하지만 10배나 빠르다는 것은 진짜 그런지 확인하지 않고 믿기에는 무리가 있다. 

다음번 포스팅에서는 ScyllaDB 홈페이지에서 진행한 "ScyllaDB vs Cassandra Benchmark"를 직접 확인해보도록 하겠다.

먼저 ScyllaDB에 대해서 알아보면 다음과 같다.


ScyllaDB 소개

ScyllaDB는 C++로 구현한 Cassandra이다. 일반적인 경우 구현 언어가 Java에서 C++로 바뀐다 해서 성능이 10배 향상되는 경우는 거의 찾아보기 힘들다. 그럼에도 테스트를 진행해서 실제 10배 성능향상이 있는지 확인하고자 하는 이유는 ScyllaDB를 만든 사람들 때문이다.

ScyllaDB는 KVM을 만든 사람들이 만들었다. KVM은 Google Compute Engine, OpenStack에서 사용하는 가상화를 위한 Hypervisor이다. 아무래도 시스템에 대해서 매우 잘 알고있는 사람들이 만들었기 때문에 시스템 자원을 최대한 효율적으로 활용해서 10배의 성능 향상을 얻을 수 있었던 것으로 예상한다.

ScyllaDB는 요즘 사용되는 하드웨어에 최적화된 Architecture를 사용하고 있다. 일반적인 JVM 아키텍쳐와 비교해보면 다음과 같다.


<그림 2. 일반적인 JVM 아키텍쳐)와 ScyllaDB 아키텍쳐>

ScyllaDB 홈페이지에서는 좌측의 일반적인 JVM 아키텍쳐는 낮은 CPU 활용, 상대적으로 비용이 많이 드는 Locking, 갑작스런 Latency 불안정 문제를 가지고 있다고 한다. 그러면서 우측 ScyllaDB 아키텍쳐는 shared-nothing 방식을 기반으로 하기 때문에 한대의 시스템에서 1백만 CQL까지도 처리할 수 있다고 한다.

Shared-nothing 방식은 리퀘스트가 처리할 CPU를 할당받아서 거기에서 다 처리하는 방식이라고 ScyllaDB 홈페이지에서 설명하고 있다. 이와 같은 방식으로 만든 Seastar라는 Framework이 있는데 ScyllaDB는 Seastar Framework를 사용하고 있다.

아무튼 ScyllaDB는 shared-nothing 방식의 Seastar Framework를 사용하여 구현하였기 때문에 context switching이 적고 시스템 자원을 최대한으로 활용할 수 있다.


시스템 자원을 최대한 활용하는 방식으로 만들었다고는 하지만 몇가지 문제점(?)들이 예상되기는 한다. 특히 이벤트 처리하는 부분에서 이벤트 처리를 OS에게 맡기는게 아니라 직접 polling하기 때문에 아무 일도 하지 않는 상태에서 모든 CPU를 사용하게 된다.

예를들어 4Core짜리 시스템이 있다면 ScyllaDB를 실행시켜놓은 것만으로 Load Average가 4.0 이 될 것이다. 물론 실행시 옵션을 줘서 사용할 Core 수를 조절할 수는 있다. 서비스를 위해서는 적어도 Core 2개는 일하지 않는 상태로 두는 것이 좋을 것 같다.


현재 ScyllaDB는 Cassandra 2.1.8에서 Thrift와 SSL을 제외하고는 완벽히 호환된다고 한다. 하지만 성능 향상을 위해서 Gossip과 Internal Streaming은 ScyllaDB가 따로 구현했기 때문에 Cassandra 노드와 ScyllaDB 노드를 하나의 클러스터로 구성하는 것은 불가능하다.


NoSQL이 Scalability와 성능을 강조하는 경우가 많기 때문에 Cassandra보다 10배 빠르다고 하는 ScyllaDB는 곧 있으면 주목받을 수 있을꺼라 생각한다. ScyllaDB GA버전이 나오면(2016년 1월에 나온다고 한다.) 어떤 반응이 있을지 기대된다.


Posted by 알 수 없는 사용자
,

얼마전에 마무리 했던 프로젝트에 대한 글을 적어 볼까 합니다.

이번 프로젝트는 사내에서 진행한 것이 아니라 외주를 받아 진행한 것이기 때문에 고객사에 대한 정보와, 보안과 관련된 부분들은 당연히 모두 빼고, 일반적인 내용들과 공개된 오픈소스, 알고리즘, 솔루션에 대한 내용만 다루도록 하겠습니다.

프로젝트에 대한 소개부터 시작하는게 좋겠죠? ^^

그럼 시작합니다.



Application Transaction Monitoring System, E2E-Monitor


개발할 시스템의 이름은 "E2E-Monitor" 였습니다.

공식 문서에서나 쓰이는 딱딱한 문장(?)으로 표현하자면,

"E2E-Monitor는 애플리케이션을 구성하고 있는 서버간의 통신 흐름을 운영자가 화면을 통해 쉽게 파악하고 모니터링 할 수 있는 일종의 관제 플랫폼입니다."

Request를 받은 순간부터 Response를 보내는 순간까지의 모든 처리 과정을 운영자가 한눈에 파악 할 수 있도록 Diagram으로 보여주는 프로그램입니다.

(E2E는 End to End의 약자로 여기서는 Request에서부터 Response까지를 의미합니다.)

Request에서부터 Response까지를 하나의 Transaction이라고 보면, "E2E-Monitor"는 결국 "Application Transaction Monitoring System"이라고 할 수 있을 것 같습니다.


[1. E2E-Monitor Dashboard 화면]



프로젝트 배경


고객사는 최근 신규 서비스를 개발해 운영중이었습니다. 

서비스의 규모가 어느정도 되다 보니, 여러 종류의 Application들과 여러대의 서버들이 운영되고 있는 상태였습니다.

하나의 Request는 최소 3단계에서 최대 10단계가 넘는 처리 과정(서버간 네트워크 통신)을 거쳐서 최종 Response를 내려주고 있습니다.

그런데 간헐적으로 정상적이지 않은 Response들이 발생하기 시작합니다. 

Application이나 서버 자체의 장애일 수도 있고, 비즈니스 로직 상의 문제일 수도 있습니다. 

문제가 발생한 위치만 알 수 있다면, 해당 Module의 담당자가 원인을 분석해서 문제를 해결 할 수 있을 것입니다.

하지만, 문제는 어느 서버의 어느 Application에서 문제가 발생했는지 추적이 힘들다는 점이 었습니다.

전체 서비스를 관제하고 있는 운영자는 문제의 시발점을 찾기 위해 해당 Request가 어떤 처리 과정들을 거치는지 파악해서 해당 과정들을 순서대로 점검해야 할겁니다. 문제의 시발점을 찾을 때까지요...

운영자가 하나의 서비스만 담당하고 있으며, 문제가 자주 발생하지 않는다면, 그리고 시간이 넉넉하다면.... 그냥 하면 됩니다.

그러나 실제로 그런 업무 환경에서 일하는 운영자는 거의 없겠죠?

운영자는 여러개의 서비스를 담당하고 있으며, 문제는 자주 발생하고, CS 담당자는 빨리 문제들을 해결해 달라고 재촉을 해댈겁니다.

운영자는 문제가 발생하면 그 위치와 문제점의 종류를 즉시 알 수 있는 "관제 시스템"이 필요하다는 결론을 내립니다.



요구사항


운영자의 요구 사항은 간단합니다.

"운영중인 시스템에서 문제(Error)가 발생하면 바로 어떤 문제가 어디에서 발생했는지를 알려주는 시스템이 필요합니다."


운영자의 요구 사항을 들어주기 위해 개발팀은 여러가지 관제 솔루션들을 찾아보기 시작합니다.

그러다가 요구 사항과 가장 근접한 오픈소스를 찾게 됩니다.

바로 Naver에서 개발한 Pinpoint입니다.

[2. pinpoint Dashboard 화면]


Pinpoint UI를 보시면 아시겠지만, Server Map이라는 Diagram이 화면의 대부분을 자체하고 있습니다. 

Server Map은 서버들을 노드로, 네트워크 연결을 간선으로 표시하고 있으며, 간선에는 단위 시간동안 발생한 네트워크 통신의 횟수가 적혀 있습니다.

Server Map을 보고 있으면 시스템의 전체적인 흐름를 한눈에 알 수 있을것 같습니다.

하지만, Pinpoint를 실무에 도입하기 위해서는 풀어야 될 숙제들이 너무 많았습니다.

가장 큰 문제는 고객사의 시스템은 Message Queue가 중요한 구성요소중에 하나 인데, Pinpoint로는 Message Queue를 거치는 처리 과정은 추적이 불가능하다는 것이었습니다.

그 외, Pinpoint 적용을 위한 추가 공수가 예상외로 많이 들어간다던지 하는 문제점들이 많았습니다.

결국 고심끝에 고객사는 자신들이 운영하는 시스템에 딱맞는 관제 시스템을 새로 만들기로 결정하고, 저희에게 개발을 의뢰하게 됩니다.


고객사의 요구사항을 정리하면 간단하게 이렇습니다.

1. Server Map Diagram을 통해 모든 시스템의 연결 상태 모니터링 (HTTP 통신 뿐만 아니라 TCP, Message Queue등 운영중인 시스템에서 사용되고 있는 모든 통신 수단 포함)

2. 에러 발생시 화면 출력과 소리, E-mail, SMS 등의 수단으로 운영자에게 알림

3. 에러 발생 위치를 Server Map에 표시

4. 특정 검색 조건으로 원하는 Transaction 정보 검색

5. HTTP 통신 이외에, TCP 통신, Message Queue 통신에 대해서도 모니터링 

4. 운영 시스템의 개발자가 추적 정보를 쉽게 생성할 수 있도록 최대한 사용이 간단한 Library와 친절하고 상세한 가이드 문서



프로젝트를 2단계로 분리하다


프로젝트는 일단 파일럿팅 개념으로 작게 시작하기로 합니다.

관제 시스템의 효용성에 대한 검증이 우선이라고 생각한 것입니다. 


관제 시스템은 크게 3가지 구성요소를 가지고 있습니다.

1. 각 Application에서 추적을 위한 정보를 생성하는 "추적정보 생성기"

2. 생성된 추적 정보를 수집해서 Transaction 별로 묶어서 저장소에 저장하는 "추적정보 처리기"

3. 저장소에 있는 추적정보를 사용자가 쉽고 볼 수 있도록 화면에 출력해주는 "사용자 프로그램"


"추적정보 생성기"는 두가지 방식이 있습니다.

첫번째 방식은 "수동 추적 방식"입니다.

추적 정보 생성을 위한 코드를 개발자가 Legacy System에 추가해주는 방식입니다.

관제 시스템 개발자 입장에서는 가장 쉽고 편한 방법입니다. 추적 정보에 대한 스팩만 정의하고 실제 추적 정보는 운영 시스템의 개발자가 알아서 잘 생성해 줄거라 믿고 관제 시스템을 개발하기만 하면 되죠.

하지만, 운영 시스템의 개발자 입장에서는 마른 하늘에 날벼락 같은 상황 일겁니다.

잘 돌아가고 있는 시스템에 비지니스 로직과는 상관도 없는 코드를 추가하라고 할 때 좋아할 개발자는 아마 없을 겁니다.

설령 억지로 코드를 추가 했다고 해도 새로 들어간 코드 검증을 위해 모든 시스템에 대한 테스트, 검증, 검수 등의 복잡한 단계를 다시 거쳐야 할 수도 있습니다.


반면, 두번째 방식은 "자동 추적 방식"으로 추적 정보를 수집해 주는 Agent가 System Low Level에서 돌아가면서 알아서 정보를 수집해 주는 방식입니다.

자동으로 추적 정보를 생성할 수만 있다면, 운영 시스템의 개발자는 추가로 해야될 일이 없습니다. 물론 기존 코드를 수정할 필요가 없기 때문에 위험 부담도 적고, 운영 시스템에 적용하기도 쉬울 겁니다.

이 경우 문제는 관제 시스템을 개발하는 쪽에 있습니다.

운영 시스템에서 사용되고 있는 통신관련 기술, 솔루션등을 모두 수집해야 하고, 각 요소마다 어떤 식으로 추적정보를 남길지 결정하고, 요소별로 따로 개발을 해줘야 합니다.


빠른 시간안에 결과물을 만들어서 검증을 하기 위해 "자동 추적 방식"을 버리고 "수동 추적 방식"을 선택합니다.

사실 "자동 추적 방식"을 버린다는 것은 운영 중인 시스템에 새로 개발한 관제 시스템을 바로 적용 시키는 것을 거의 불가능 합니다.

하지만, 새로 개발되는 서비스에서는 초반 개발단계에서 부터 "추적정보"를 의무적으로 생성하도록 하는건 상대적으로 쉽습니다.

이번 프로젝트의 전략은 일단 수동방식으로 빨리 개발하고, 신규 서비스나 상대적으로 규모가 적은 기존 서비스에 관제 시스템을 먼저 적용해 보고 운영하면서, 관제 시스템을 고도화 하는 것입니다.

그 대신 "추적 정보 처리기"와 "사용자 프로그램"은 추후에 추적 방식을 자동으로 바꾸기 용이하도록 Pluggable한 구조로 개발하기로 합니다.



E2E-Monitor Architecture


E2E-Monitor는 모니터링 대상 시스템의 Scale 변경에 유연하게 대처하기 위해 Scale-out이 가능한 구조로 설계했으며, 각 모듈들은 최대한 느슨하게 연결되도록 신경을 썼습니다.


[3. E2E 구조도]

 

A. 추적 정보 수집기

추적 정보 생성기에 해당하는 부분입니다.

모니터링 대상 시스템에서 추적에 필요한 정보를 생성해서 파일에 적는 일을 합니다.

Log Collector가 생성한 정보는 최종적으로는 Search Engine에 저장되게 됩니다. 그런데 구조도를 보면 Search Engine에 저장되기 전까지 몇단계를 거치도록 되어 있는걸 보실 수 있습니다.

단계는 다음과 같습니다.

1. Log Collector는 정보를 일단 파일에 적습니다.

2. Log Stash는 로그 파일을 감시하고 있다가 Log Collector가 정보를 파일에 적으면, 그걸 바로 읽어서 Message Queue로 실어보냅니다.

3. Message Queue Consumer는 Message Queue를 감시하고 있다가 Message가 들어오면 바로 읽어서 Search Engine에 저장합니다.

Log Collector가 바로 Search Engine에 저장하지 않고 여러 단계를 거치는 이유는 모듈간의 느슨한 연결과 Pluggable한 구조를 위해서 입니다.

일단 정보를 파일에 우선 적음으로써 모니터링 대상 시스템이 무슨언어로 만들어지던지 상관이 없어집니다.

어찌되었던 정해진 포멧대로 파일에 정보를 적을 수만 있으면 되니까요.

Log Stash가 파일에서 읽어들인 정보를 바로 Search Engine에 넣지 않고 Message Queue에 태우는 이유는 System Performance 때문입니다. 

보통 읽기 성능은 Scale-out을 통해 쉽게 향상이 가능하만, 쓰기 성능은 크게 향상되지 않습니다. 가장 쉽게 쓰기 성능을 향상 시킬수 있는 방법은 Bulk Write 방식을 사용하는 것이라고 판단했습니다.

그래서 일단 Message Queue에 정보를 쌓아두고, 일정 조건에 맞춰 대량의 정보를 한번에 Search Engine에 쓰기로 했습니다. 또한 1차 가공 단계를 거치기 때문에 무리하게 모니터링 대상 시스템에서 작동하는 Log Stash에 비지니스 로직을 추가할 필요가 없게 되는 장점도 있습니다.


B. 추적 정보 처리기

일련의 단계를 거쳐 Search Engine에 저장된 정보는 다시 주기적인 배치작업을 통해 Transaction 별로 분류되고, Transaction 관련 통계 정보를 생성하는 과정을 거치게 되는데, 이런 일을 하는 모듈이 바로 Log Composer 입니다.

Log Composer는 1분에 한번씩 기초 데이터(Log Collector에서 생성되 Search Engine에 저장된 원시데이터)들을 읽어 들여서 분류 및 재가공 과정을 수행합니다.

재가공된 정보는 다시 용도에 따라 Search Engine과 RDB에 나누어 저장됩니다.


C. 사용자 프로그램

운영자가 사용하는 프로그램입니다.

Search Engine과 RDB에 저장되어 있는 정보를 가져와 Web Client로 전송해 주는 API Server와 Web Client로 구성되어 있습니다.

API Server는 Java로 구현되어 있고, Web Client는 AngularJS를 사용했습니다.




앞으로 해야 할 일들


현재 E2E-Monitor에서 가장 선행되어야할 추가 작업은 추적정보 수집 방식 변경입니다.

모니터링 대상 시스템의 개발자가 직접 추적 정보 수집을 위해 기존 코드에 손을 대는것은 참으로 위험하고 힘든 작업일 것입니다. 

현실적으로는 정말 작은 시스템이거나 새로 개발되는 시스템이 아니라면, 적용이 거의 불가능한 방식이라고 생각합니다.

그래서 E2E-Monitor의 생명력은 자동으로 추적정보를 수집하는 기능을 갇추느나 마느냐가 결정할 것으로 보고 현재 이 부분을 최우선 과제로 삼고 있습니다.

그 외 운영자 마다, 또는 시스템 마다 보고 싶은 정보가 조금씩 다를 수 있기 때문에 Customizing이 가능한 Dashboard를 구상중에 있습니다.

 


범용성


E2E-Monitor는 특정 회사의 요구사항에서 시작했지만, 다양한 구성의 어떠한 시스템에도 적용 가능하도록 설계하고 개발되었습니다.

물론 서비스 고유의 기능등 추가적으로 필요한 부분들은 추가적인 공수가 들어가겠지만, 최소한의 공수만으로 추가 가능하도록 설계했습니다.

혹시라도, 운영 중인 시스템에 적용할 관제 시스템을 찾고 계시거나, System Trouble Shooting에 애를 먹고 계신다면 저희에게 연락 주시면 도와드리겠습니다.



프로젝트에 대해 간단하게 설명드릴려고 했는데 생각보다 글이 길어졌네요.

다음 글에서는 E2E-Monitor에 사용했던 Web 기술들에 대한 소개를 하겠습니다.






Posted by 알 수 없는 사용자
,

이번 포스팅에서는 약 2달 반동안 프로젝트를 진행 하면서 처음 접했던 JavaScript 프레임워크 및 라이브러리 소개 중 2편 

 JavaScript 라이브러리인 C3Dagre-D3BigScatterChart에 대한 내용을 다루려고 한다.


이전 포스팅은 주니어 개발자의 경험기 [1편 - AngularJS] 을 참고 하기 바란다.


이번 편은 JavaScript 시각화 라이브러리이다. 시각화 라이브러리는 HTML5의 SVG를 이용한것과 Canvas를 이용한 것으로 나눠진다. SVG를 이용한 라이브러리로 잘 알려진 것은 D3.js가 있다. 프로젝트에 내가 사용한 라이브러리는 SVG를 이용하고 D3.js를 기반으로 한 C3.js 라이브러리와 Dagre-D3.js 라이브러리 그리고 Canvas를 이용한 BIgScatterChart.js 라이브러리 였다. 각 라이브러리를 살펴보기에 앞서 SVG와 Canvas의 차이점에 대해 잠깐 짚고 넘어 갈까 한다.


 

Canvas 

SVG 

그래픽 시스템 

픽셀 기반의 즉시 모드 그래픽 시스템 

모양 기반의 유지모드 그래픽 시스템 

이미지 처리방식 

Bitmap (해상도 의존적) 

Vector (해상도 독립적) 

DOM 

존재하지 않음 (DOM Control 불가) 

존재함 (Script로 Control 가능) 

외부 이미지 편집

Bitmap image 편집 용이 

Vector image 편집 용이 

성능저하 요인 

높은 해상도의 이미지를 사용할 경우 

이미지가 복잡해질 경우 

Animation 

Animation API가 없으므로 Script의 Timer를 사용 

높은 수준의 Animation 지원 

외부이미지로 저장 

jpg, png등으로 저장가능 

불가능 

적합한 서비스  

Graph 구현, Game, 실시간 데이터 출력, 동영상 조작 

Graph구현, 매우 세밀한 해상도를 지원하는 UI 및 Application 

적합하지않은 서비스  

Standalone Application UI 

Game, 실시간 데이터 출력 

[표] SVG vs Canvas  (출처 - http://www.nextree.co.kr/p9307/)



SVG와 Canvas의 차이점에 대해 간단히 살펴 보았다. 그럼 다시 본론으로 들어가 C3.js 라이브러리에 대해 살펴 보자.


1. C3.js


C3.js 는 앞서 말한바와 같이 D3.js를 기반으로 되어 있는 차트 라이브러리 이다. 프로젝트에 해당 라이브러리를 채택하게된 배경은 단순했다. 워낙에 많은 차트 라이브러리가 존재하고 있기 때문에 어떤걸 선택해야될까 고민을 하다가 Google에 'Chart Library' 라고 검색을 해보았다. 많이 쓰이면 그만큼 사용자들의 만족도가 높다고 생각했고, C3.js 라이브러리는 첫페이지에 검색되어 보여졌다. 물론 많이 쓰인다는 이유만으로 선택 한 것은 아니었다. 내가 구현하고자 하는 부분을 충분히 구현 가능한가? 라는 질문에 대해 홈페이지에 있던 Reference를 통해 충분히 확인 해보았다. 당시 내가 원하는 조건은 다음과 같았다.


1. JSON 타입의 Data를 표현 할 수 있어야 된다.

2. 디자인적인 면에서 커스터마이징이 가능할 것 

3. 이벤트 동작을 지원 가능할 것

4. 마우스 오버시 Tooltip으로 정보가 표시될것 


원하는 조건을 충족할 만한 라이브러리 였기에 최종적으로 채택을 하였다. 뿐만아니라 MIT 라이센스라는 점도 선택에 한몫 거들었다. 또한 C3의 장점중 하나로 얘기 하고 싶은 것은 다양한 Example을 제공 한다는 것이었다.

C3.js는 Line chart, Bar chart, Scatter Plot, Pie chart, Donut chart, Gauge chart 등 여러 종류의 차트를 위한 api 및 예제를 제공 하고 있고, Axis, Data, Grid, Region, Interaction, Legend, Tooltip 등과 관련된 설정 및 제어가 가능하다. 공식 홈페이지의 Examples 를 통해 해당 부분에 대한 예제를 직접 수정해보며 적용해 볼 수 있다. 그외에 좀더 상세한 부분은 Reference를 참고 하면 된다. 커스터마이징 하는 것이 어렵지 않도록 되어 있고, Google에도 C3.js에 관련된 많은 정보가 있기 때문에 어렵지않게 원하는 모습의 chart를 그려낼 수 있을 것이다.


그럼 C3.js를 이용한 예제를 한번 살펴보자.

해당 예제는 공식홈페이지에서 제공하는 Example 몇개를 합친 것이다.



차트 라이브러리의 경우 다른 라이브러리도 살펴 보았지만 크게 차이가 나지 않는 선에서 대부분 사용자들의 구미에 맞게 구현이 되어 있다. 다만 개인적인 의견으로 C3의 경우 예제가 잘되어 있어 좀더 쉽게 접근이 가능해 좋았던것 같다.


2. Dagre-d3.js

다음은 Dagre-d3.js 라이브러리를 살펴 보겠다.
해당 라이브러리 역시 D3.js 를 기반으로 되어 있고 Chris Pettitt이라는 사람이 개발한 오픈소스 라이브러리로 Directed Diagram Graph를 표현 할 수 있다.
진행했던 프로젝트에서 Server Map이라고 불리우는 부분을 표현 하기 위해 사용하게 되었는데, 모티브가 된 것은 Naver Pinpoint의 Server Map 이었다. 오픈 소스인 Pinpoint에서 사용한 라이브러리를 사용 하면되지 않을까 하고 처음에 생각하였지만 Go.js라는 유료 라이브러리를 사용했다는 점에서 대체할 수 있는 다른 라이브러리를 찾게 되었다. 라이브러리 선택 조건은 다음과 같았다.

1. 자동으로 Layout을 구성할 수 있을 것
2. Directed Graph 즉, Node간 연결 관계인 간선이 방향성을 가지는 화살표로 표현이 가능할 것
3. Node에는 이미지와 Label을 간선에는 Label을 입력 할 수 있도록 지원 가능 할 것
4. 무료 오픈 소스이면서 라이센스 조건이 이용하기 편리할 것

열심히 구글링을 한 결과 Dagre-d3.js를 알게 되었고 선택하게 되었다. Dagre-d3의 Auto Layout 기능은 Graphviz의 방식을 이용해 구현한 라이브러리이다. 특징을 살펴 보면 자동으로 레이아웃을 구성해 주고, Node에 이미지 삽입이 가능하며, Node와 간선의 스타일링이 가능하고, zoom 및 마우스 이벤트 제어가 가능하고, Node 간 Grouping 이 가능하다. 해당 라이브러리를 처음 사용할 때는 그다지 불편함을 발견 하지 못하였다. 

하지만 프로젝트를 진행 하면서 큰 단점이 발견 되었는데 그건 Grouping 하여 표현할 경우 혹은 일반적인 경우에도 Node수가 많아 지면 Auto Layout 기능으로 인해 오히려 알아보기 힘들게 모양이 변형 되는 것이었다. 단순한 Diagram 을 표현할 때는 좋았지만 Data가 많아지고 복잡해지자 오히려 해당 기능이 독으로 작용을 하였다. 해당 이슈를 해결할 방법을 찾으려 노력했지만 라이브러리 개발자는 잠정적으로 commit을 중단하였고 결국은 해결하지 못하였다.
그 밖에도 나같은 초보 개발자에게는 Example이 많은 도움이 될텐데 기본적인(?) 기능의 예제만 Demo로 제공되어 있어서
활용하기가 조금은 힘이 들었다. 프로젝트를 진행하면서 Node를 클릭하는 이벤트 라던지 클릭 시 관련된 간선과 노드의 CSS를 조작한다던지 하는 부분은 직접 조작하는 코드를 추가 하였다. 이런 부분은 내가 아직 역량이 부족하여 라이브러리를 제대로 이해하지 못했거나 라이브러리 내의 기능을 발견하지 못한걸수도 있다. 

해당 라이브러리로 인해 프로젝트에 난항을 겪었던것은 사실이지만 어찌되었든 처음부터 끝까지 함께한 아래 Dagre-d3.js 라이브러리의 예제 샘플을 한번 살펴보도록 하겠다. 해당 예제는 내가 직접 프로젝트에 사용했던 코드의 일부분만 빼내어 작성한 것이다. 노드를 클릭했을 경우 해당 노드와 노드에 연결된 간선의 색이 하이라이트 되고 toggle방식으로 작동이 된다.


Dagre-d3의 Grouping기능 (cluster)은 썩 추천할만 하지않지만 예제와 같이 기본적인 모습의 graph를 표현 하기에는 나쁘지 않은 라이브러리라고 생각한다. 라이브러리에 대한 업데이트가 이후 언제 이뤄질지 장담할 수 없기때문에 더이상의 발전은 기대할 수 없는 실정이다. 해당 라이브러리를 사용하고자 한다면 이 점을 유의해야 할것 같다.



3. BigScatterChart.js


마지막으로 소개할 라이브러리는 Canvas를 기반으로한 BigScatterChart.js 라는 라이브러리이다. 해당 라이브러리는 Naver의 개발팀이 개발하여 오픈소스로 공개한 라이브러리이고 이름에서도 유추해 볼 수 있듯이 일반 ScatterChart보다는 많은 양의 Scatter를 표현하기위해 개발되었다고 한다. 개발 목적은 Naver의 Pinpoint에 적용할 Scatter Chart라이브러리를 찾다가 본인들의 요구조건에 적합한 라이브러리가 존재하지 않아 개발하게 되었다고 쓰여있다. 해당 라이브러리는 Canvas를 기반으로 개발되었는데 그 이유는 개발자의 요구조건에 쓰여있었다. 포스팅 초반에 살펴보았듯이 SVG의 큰 단점중의 하나는 많은 양의 Data를 렌더링 하게될 경우 속도가 현저히 느려지는 것이다. 해당라이브러리는 수십만개의 점을 표현가능해야 된다는 요구조건을 가지고 있었기 때문에 SVG로는 그 문제점을 해결할 수가 없었다고 한다. 기본적인 배경 소개는 이 정도로 하고 그렇다면 BigScatterChart.js가 가지고 있는 특징이자 강점을 살펴보자. 


1. 수십만개의 Data표현에 적합하다. (대략 50만개의 Data 렌더링 시 약 6초 소요)

( D3.js의 경우 대략 8만개의 Data 렌더링 시 약 33초 소요)

2. 차트영역에서 Drag & Drop으로 셀렉이 가능하다.

3. 셀렉된 부분에 담긴 Data를 검색할 수 있다.

4. 상단에 Type바가 존재하며, 이동 및 toggle이 가능하다.


내가 파악한 특징은 위와 같다. 그 밖에 내가 파악하지 못한 기능들이 더 있을 수도있겠지만, 4가지 특징만으로도 꽤나 매력있다고 생각한다. 해당 라이브러리의 경우는 원청의 요구사항으로 BigScatterChart를 사용해달라고 하여 사용하게 되었었다. 덕분에 좋은 라이브러리 하나를 더 알게 되었고, 사용해볼 수 있는 좋은 계기가 되었다. 


아래는 BigScatterChart라이브러리의 Example 예제를 jsfiddle로 옮겨 작성한 것이다.



이번 프로젝트를 준비하고 진행하면서 라이브러리 선택의 중요성을 새삼 깨달았다. 요구 조건에 맞는 라이브러리를 선택하는 것도 중요하지만 많이 쓰이고 있는 라이브러리는 그 만큼 사용하기 편했었고 그렇지 않다 하더라도 개발자의 능력에 따라 충분히 부족한 부분을 구현함으로써 채워 나갈수도 있는걸 느꼇다. 새로운 라이브러리들은 끊이 없이 쏟아져 나오고있다. 그 라이브러리가 좋다 나쁘다를 판단하기 보다는 내가 원하는 조건에는 맞는지 사용하는데 어려움이 없을지를 판단할 수 있다면 좋을 것 같다고 느꼇다. 물론 그러려면 많은 라이브러리들을 직접 경험해보고 판단하기 위한 안목(?)을 길러야 할 것이다. 

나와 같은 주니어 개발자라면 될 수 있으면 많은 라이브러리들을 사용해보고 직접 느껴보는 것이 좋은 방향이라고 생각한다. 또한 나와 같이 비록 부족하더라도 블로그가 되었든 흔적을 남겨놓는 다면 좋은 자산이 될 것같다. 해당 포스팅을 작성하면서 JSfiddle을 이용해보며 새로운 경험도 하고 사용했던 라이브러리를 다시 한 번 조사 하면서 프로젝트 준비때는 놓쳤던 부분이라던지 잘못 이해했던 부분을 깨달을 수도 있었던 것 같다. 해당 포스팅에는 주관적인 생각이 많이 담겨있고, 주니어 개발자의 수준에서 작성되었기에 잘못된 부분이 있을 수도 있다. 잘못된 부분에 대한 지적은 적극 환영 하고, 이 글을 보는 사람들에게 조금이나마 도움이 되었으면 한다. 






Posted by 알 수 없는 사용자
,

이번 포스팅에서는 약 2달 반동안 프로젝트를 진행 하면서 처음 접했던 JavaScript 프레임워크 및 라이브러리를 간단히 소개 하고 사용하면서 알게된 점과 느낀점을 작성할까 한다.

총 2편의 포스팅으로 나눠서 작성을 할 예정이고, 1편은 JavaScript 프레임워크인 AngularJS에 대한 내용이고, 2편은 JavaScript 라이브러리인 D3, C3, Dagre-D3, BigScatterChart에 대한 내용을 다루려고 한다.


1편에서 다룰 AngularJS는 이번 프로젝트를 통해 처음 접하고 알게된 것이었다.


AngularJS가 무엇인지 먼저 알아보자.

AngularJS는 무엇일까? 


AngularJS의 공식사이트인 https://www.angularjs.org/ 에 들어가면 가장 메인페이지에 다음과 같이 쓰여 있다.

HTML enhanced for web apps!

[웹 앱을 위해 강화된 HTML]

그리고 'AngularJS is a structural framework for dynamic web apps. - AngularJS는 동적인 웹 앱을 위한 프레임워크이다.' 라고 정의 되어 있다. 구글링을 통해 알게된 AngularJS의 몇가지 특징은 MVW (Model-View-Whatever) Architecture 패턴을 이용한 개발이 가능하다. SPA(Single Page Application) 개발에 용이하다. 양방향 Data-Binding이 가능하다. 프레임워크의 사용으로 인해 협업이 용이하다. 등 여러가지 특징이 있었다. 이런 특징을 바탕으로 공홈에 있는 Developer Guide에 명시된 개념설명중 몇가지와 예제를 살펴보도록 하자.

설명에 앞서 AngularJS는 SPA(Single Page Application) 개발에 특화되어 있고 대부분 Module 별로 나누어 구현 하는 방식을 이용한다. 여기서 Module이란 UI Components (ex. Modal, 특정 Button Group, Line Chart, table 등등..)라고 생각하면 이해하기가 쉬울 것 같다.
ng-app이라는 지시어를 통해 AngularJS 코드가 적용 및 시작 된다. ng-app은 HTML코드에서 <html></html>과 같은 느낌이다. 

1. Template : HTML with additional markup (추가된 마크업으로 작성된 HTML)
Template은 HTML로 작성된 Module이라고 이해하면 된다.

2. Direvtives : extend HTML with Custom attributes and elements (커스텀 속성과 요소로 된 확장된 HTML)
Directives는 말그대로 사용자가 만든 HTML 속성 혹은 사용자가 만든 Tag 등이다. 보통 하나의 Directives는 하나의 Module을 담당한다.


3. View : what the user  sees (the DOM) 말그대로 DOM이다.

4. Model : View에서 사용자에게 보여지는 데이타 그리고 사용자와 상호작용하는 것(양방향 Data-binding) 이다. 자바스크립트 객체라고 생각하면 이해하기 편할 것이다.
아래 예제에서 수량과 금액을 수정할 경우 양방향 Data-binding을 통해 수정된 합계가 바로바로 적용되는 것을 확인 할 수 있을 것이다. ng-model을 통해 'qty'  'cost'를 model로 선언해 주었고 Expression인 '{{ }}'를 통해 바라보고 있는 model의 변화를 바로바로 반영 하는 것이다.


5. 양방향 Data-binding

아래는 일반적인 JavaScript에서 볼 수 있는 단방향 Data-binding의 동작 모습과 4.Model의 예시에서 설명한 AngularJS의 양방향 Data-binding 동작 방식을 잘보여 주는 그림이다. 기존의 JavaScript 혹은 JQuery는 Data-Binding 동작을 표현 하기위해서 여러가지 작업을 해주어야 하지만 AngularJS에서는 View와 Model이 서로를 바라보면서 자동으로 Data를 업데이트 하기 때문에 간단하고 쉽게 표현을 할 수 있다. 



[그림 ] One-Way Data Binding in Classical Template Systems (출처 - https://docs.angularjs.org/guide/databinding)



[그림 ] Two-Way Data Binding in AngularTemplates (출처 - https://docs.angularjs.org/guide/databinding)


6. scope : context where the model is stored so that controllers, directives, and expressions can access it

스코프는 angular에서 컨트롤러나 디렉티브의 유효범위내의 저장 공간이라고 생각하면 이해하기 쉬울 것이다. 경험해본 바에 의하면 Angular에서 Scope의 범위는 상당히 중요한것 같다. 처음에 Angular로 코딩을 할 때 Scope에 대해 제대로 이해하지 않은 상태로 진행 하여 많이 애를 먹었었다. angularJs를 처음 접한다면 꼭 Scope에 대해 이해하고 이용하길 바란다. 


아래는 Scope를 저장공간으로 활용한 예제 이다.




아래는 Scope를 컨트롤러나 디렉티브의 유효범위 별 Scope를 표현한 예제이다.

HTML의 Expressions와 Javascript의 Scope를 보면 같은 name 이지만 Scope범위 별로 보여지는 값이 다른 것을 확인 할 수 있다. 



이처럼 Scope의 범위에 따라 컨트롤 할 수 있는 범위가 달라지기 때문에 Angular에서는 항상 내가 사용하는 Scope의 범위를 생각하면서 구현해야한다.



AngularJS 홈페이지에 보면 왜 AngularJS 인가? 라는 질문과 그에 대한 답이 다음과 같이 제시되어 있다.

 - HTML is great for declaring static documents, but it falters when we try to use it for declaring dynamic views in web-applications. AngularJS lets you extend HTML vocabulary for your application. The resulting environment is extraordinarily expressive, readable, and quick to develop.

- HTML은 정적인 documents를 표현하기 위해 훌륭하다. 그러나 HTML은 우리가 그것을 웹 애플리케이션에서 동적인 모습을 표현하려 할때 주저하게 한다. AngularJS는 애플리케이션을 위한 HTML 문법의 확장을 제공한다. 결과적으로 더 나은 표현을 할 수 있게 되고, 더 나은 가독성을 제공하고, 개발을 빠르게 할 수 있게 한다.


AngularJS를 접하기 전까지 나는 정적인 HTML을 동적으로 표현하기 위한 방법으로 JavaScript 혹은 JQuery를 사용하여 Dom을 조작하는 방식을 사용해 왔다. 사실 AngularJS를 사용해보기 전까지는 JavaScript와 JQuery를 사용한 개발 말고 다른 방식으로 동적인 웹 앱을 개발하는 것에 대해 생각을 해보지 못했었다. JavaScript, JQuery 만으로도 충분한 표현이 가능하다고 생각했었기 때문이다.

프로젝트를 진행하기 위한 준비 과정 역시 JavaScript와 JQuery만으로 MockUp을 개발 했었다. 실제 AngularJS를 이용한 개발은 프로젝트 투입 며칠전 요구사항으로 AngularJS를 이용해 개발해달라고 하여 접하게 된 것이다. 

AngularJS에 대한 첫 느낌은 '아... 뭔 소리지 이게?' 였다. 마치 대학교에서 처음 개발 언어를 접했을 때와 같은 기분이었다. 

다른 주니어 개발자들에게는 어떨지 모르겠지만 나에게 AngularJS의 진입 장벽은 꽤나 높게 느껴졌다.

대략 1주일 정도의 학습 기간을 가진 뒤 JQuery로 짜여진 코드를 AngularJS를 적용한 버전으로 바꾸는 작업을 진행 하였다. 

물론 1주일이라는 기간동안 AngularJS를 완벽히 파악하기에는 짧은 기간이었기에 작업에는 많은 어려움이 있었고 개발 속도 또한 더디게 진행 되었다. 당시 어려움을 느꼈던 부분은 크게 3가지가 있었다.


1. 위에서도 언급했던 Scope 사용범위에 대한 이해 부족

2. 단기간에 학습하고 활용하기에는 너무 많은 API들

3. 버전별 비호환성


많은 API 만큼 많은 것을 할 수 있겠지만 프로젝트 기간 동안 학습하고 활용하기에는 너무 많은 양이었다. 때문에 내가 실제 개발에 사용한 API 함수 혹은 Service역시 극히 일부분이었다. 또한 처음 적용할 당시 버전에 따라서 Directive(지시어) 사용법이라던지 Provider 사용법이 다르고 호환이 되지 않는 다는것을 알지 못해서 많은 삽질(?)을 하였다. 


AngularJS가 매력적이고 유용한 점이 많지만 개인적으로 프로젝트 진행중에는 'JQuery를 사용했으면 더 쉽게 해결될 문제였을 텐데..'  라고 생각되는 부분이 많았었다. 물론 학습기간도 짧았고 당시에는 내가 AngularJS의 개념을 완벽히 파악하고 개발을 했던게 아니었기 때문에 발생하는 문제였을 것이다.

아무래도 주니어 개발자로 이제 막 첫걸음을 내딛는 단계이기에 이해하는데 한계와 경험부족으로 인한 어려움이 많았고, 그로인해 포스팅에도 안좋은 점이 많이 부각된 것 같다. AngularJS를 어렵게 느낀 것은 사실이지만 좀 더 공부해 보고싶다. 아직 AngularJS의 매력에 푹 빠져보지도 못했고 내가 경험한 AngularJS는 극히 일부분이기 때문이다. 언젠가 Angular의 매력을 충분히 느끼게 된다면 Angular강좌 같은 포스팅도 가능 하지 않을까 싶다. 강좌 포스팅을 할 수 있는 날을 기대 하며 1편 AngularJS, RequireJS 포스팅은 마치도록 하겠다. 


다음 포스팅인 주니어 개발자의 경험기 [2편 -  JavaScript 시각화 라이브러리] 에서는 프로젝트에 사용했던 JavaScript 시각화 Library들에 대해 알아보도록하겠다.


Posted by 알 수 없는 사용자
,