Vert.x 3 의 아버지 Tim Fox 와의 인터뷰 

(https://www.infoq.com/articles/vertx-3-tim-fox)



Vert.x는 비동기식의 확장 가능한 동시 서비스 개발 모델을 제공하는 JVM용 리액티브 마이크로 서비스 툴킷입니다. 자바스크립트, 루비, 그루비, 스칼라에 대한 폴리글랏 언어 개발을 지원합니다. 물론 자바도.

InfoQ는 Vert.x 수석 아키텍트이자 창시자인 Tim Fox와 함께 Vert.x와 곧 출시 될 Vert.x 3 릴리즈에 대한 생각을 접할 기회를 얻었습니다. Tim은 Vert.x와 Java EE, Spring, Akka 를 비교하고 Vert.x가 마이크로서비스, 리액티브 개발에 어떻게 적합한 지를 설명할 것입니다.


InfoQ : Vert.x 란 무엇이며 왜? 그리고 가 Servlet이나 Java EE 또는 Spring과 같은 전통적인 Java 스택 대신 이를 선택합니까?

Tim : Vert.x는 JVM에서 플리글랏 리액티브 애플리케이션을 작성하기위한 툴킷입니다. 전통적인 스택과는 달리 마이크로 서비스를 염두에 두고 설계되었으며, 확장성을 염두에두고 설계되었으므로 거의 블로킹되지 않습니다 (OS 스레드).

이는 많은 동시성을 처리해야하는 많은 현대적 응용 프로그램에 중요합니다. 많은 메시지 또는 이벤트를 처리하거나 많은 연결을 처리합니다.

또한 기존 Java 스택과 달리 Vert.x는 Java가 아닌 다른 언어를 지원합니다. JS, Ruby 및 Groovy를 사용하여 항상 Java를 사용하도록 강요하지 않으며 현재 작업 중이거나 팀의 기술 집합에 가장 적합한 언어를 사용할 수 있습니다.

Vert.x는 컨테이너 또는 "프레임 워크"가 아니라 툴킷입니다. 즉, 기존 응용 프로그램에서이 응용 프로그램을 사용하여 Vert.x 초능력을 사용할 수 있습니다. 예를 들어 Spring 애플리케이션 내에서 사용할 수 있으며 많은 사용자가 사용할 수 있습니다.


InfoQ : 스프링에 대한 당신의 생각은 무엇입니까? 스프링 리액터? 스프링 부트? Node.js?

Tim : 나는 스프링을 존경합니다. 그들은 풍부한 생태계를 만드는 데 놀라운 일을 해 왔으며 에코시스템 아래 모든 것을 수행하는 구성 요소가 있습니다. Vert.x의 가장 큰 장점은 Spring API가 블로킹하고 확장성을 제한한다는 것입니다. 그리고 Spring은 Vert.x와 같이 다중 언어가 아니라 Java 만 사용합니다.

그러나 나는 Vert.x를 Spring의 경쟁자로 보지는 않을 것 입니다. Vert.x는 Spring 생태계 만큼 많은것들을 포함 할 것 같지 않습니다. Vert.x는 단지 라이브러리이며 같은 애플리케이션에서 Spring과 함께 사용할 수 있다는 것을 잊지 마십시오. 시간이 지남에 따라 Spring 생태계의 더 많은 구성 요소가 블로킹 되지 않게 되므로 확장성있는 응용 프로그램에서 더 성공적으로 사용 될 수 있습니다.

우리는 이미 Spring 에코 시스템의 일부 (예 : project reactor) 가 논블럭, 이벤트 중심 접근법을 사용하고 있음을 알고 있습니다.

참고 : SpringSource / VMWare의 프로젝트 리액터는 Tim Fox가 VMware를 떠나 RedHat에 합류 한 직후에 나왔습니다. Project Reactor는 Vert.x의 경쟁자이며 초점과 스타일면에서 비슷합니다.


InfoQ : Java EE에 대한  생각은 무엇입니까? 애플리케이션 서버에 대한 귀하의 생각은 무엇입니까?

Tim : Java EE는 원래 현대 응용 프로그램에 필요한 것과 매우 다른 개발 및 배포 모델로 설계되었습니다. Java EE 애플리케이션 서버는 네트워크상의 어딘가에 있고 JAR 패키지 된 애플리케이션을 배치한 monolithic  서버를 가지고있었습니다. 이것은 마이크로서비스 모델의 반대입니다.

게다가 대부분의 Java EE API는 기본적으로 동기식이므로 대부분의 Java EE 응용 프로그램 서버는 I / O에서 블럭되는 많은 것들 (원격 JDBC 호출, JTA 호출, JNDI 조회, JMS가 많이 포함되어 있어도) 이기 때문에 스레드 풀을 추가하여 확장해야합니다. 우리가 알고있는 것처럼 스레드 풀을 추가한다고 해도 확장성 측면에서 부족합니다. 그래서  Java EE는 기본 디자인 자체가  절름발이라 많은 동시성이 필요한 응용 프로그램에 대해서는 결코 좋은 선택이 될 수 없습니다.

Vert.x는 여러면에서 Java EE에 대한 반작용으로 만들어지게 되었습니다.공정하게 말하면, 지난 몇 년 동안 Java EE를보다 쉽게 ​​사용하기위한 움직임이 있었고 일부는 리액티브 마이크로 서비스에 적합한 모델로 Java EE를 재 포장하려는 노력을 보았습니다. Java EE는 그 모델을 염두에두고 설계된 적이 없으며,  그러한 깊은 변화를 위해서는 이를 버리고 다시 시작해야 할 수도 있습니다. 나에게 이것은 엄청나게 힘든 투쟁일것 같습니다. Vert.x를 만들 때 우리가 했던 것과 정확히 같습니다.


InfoQ : Scala와 Akka에 대한 생각은 무엇입니까?

Tim : 스칼라의 힘, 그리고 스칼라에서 프로그램 할 수있는 사람들을 존경합니다. 나에게 스칼라가 가지는 주요한 문제는 너무 어렵고 많은 일을 시도하는 것 입니다. 이는 자바처럼 진정한 주류가 될 수 없다는 것을 의미하는데요. 하지만 당신이 그것을 처리 할 수있는 슈퍼 인텔리전트 개발팀을 가지고 있다면 스칼라가 훌륭한 선택이 될 것입니다.  

Akka는 제가 존중할 수있는 훌륭한 시스템입니다. 일부에서는 Vert.x와 유사한 접근 방식을 취합니다. Vert.x는 동시성에 대해 "액터와 같은"접근 방식을 사용하며 변경 가능한 공유 데이터를 사용하지 않습니다. 또한 Vert.x와 Akka는 새로운 반응형 스트림 "표준"을 사용하여 상호 운용됩니다. Vert.x와 Akka는 어느 정도까지 동일한 Zeitgeist (시대정신) 을 가지고 있습니다. ^^

저는 Vert.x와 Akka를 "어느 쪽이든" 하나를 선택하는 상황으로 보지 않습니다. 나는 Vert.x와 Akka 각각이 설치되어 서로가 행복하게 대화하는 것을 보게 될 것이라고 생각합니다.


InfoQ : Vert.x의 성능은 Node.js의 성능과 어떤 차이가 있습니까?

Tim : 글쎄, 요기서 함 보실래요?   -TechEmpower BenchMarks (Vert.x 가 월등) 


InfoQ : 마이크로 서비스 및 리액티브 아키텍처에 대한 업계의 방향을 감안할 때 Vertx 의 방향이 옳은 것으로 느껴지십니까?

Tim : 그렇게 생각합니다. 2011 년에 Vert.x (또는 Node.x)를 시작했을 때 응용 프로그램 서버 기반 응용 프로그램의 복잡성에 대한 대처 방법으로 시작된 것입니다.

첫 번째 버전부터 Vert.x는 항상 자체 인프라 스트럭처 또는 "응용 프로그램 서버"를 사전에 배포하지 않고도 어디서나 원하는 언어로 실행하여 자체 코드로 코드를 작성했습니다.

응용 프로그램 개발 및 배포의 마이크로 서비스 모델을 추진하는 첫 번째 프로젝트 중 하나였습니다. 이제는 이것이 대중화되고 있음을 알았습니다. 그렇습니다. 나는 확신합니다.

그러나 이것에 또 다른면이 있습니다. 아이디어가 더 주류가되면 보다 넓은 생태계가  함께 오는 것 같아요. 모두가 뛰어들어서 스스로를 마이크로 서비스로 선언할것입니다.  우리는 이제 이것을 하나의 jar 로 압축하여 메인 클래스를 추가하고 스스로를 마이크로서비스 또는 "비 반응적"이라고 선언하는 모놀리식 (monolithic)으로 설계된 다양한 전통적인 플랫폼을 볼 수 있습니다.

마이크로서비스 외에도 Vert.x의 핵심 기능은 항상 논블럭입니다. 최소한의 스레드를 사용하여 많은 동시성을 처리 할 수 ​​있도록 응용 프로그램을 확장 할 수 있어야합니다. 많은 사용자가 이제 이것이 중요하다는 것을 깨닫고 있습니다.

대부분의 최신 응용 프로그램은 많은 양의 데이터를 처리하고 많은 메시지와 이벤트를 처리하거나 많은 연결을 처리하기 때문에 스레드 풀 및 블로킹 (OS 스레드) 구현을 통해이를 효과적으로 수행 할 수 없습니다. 확장성을위한 이벤트는 "반응성"의 큰 부분이며 반응성 또한 주류가되고 있음을 알면 좋습니다. 지난 2년 동안 리액티브 시스템이 JAX 혁신상 (작년 Vert.x, 올해 Akka)을 수상했습니다.

많은 사용자가 이제 이것을 얻었으며 논블럭은 이제 몇 년 전보다 훨씬 주류처럼 보이기 때문에 이것이 접근법의 근거라고 생각합니다.


InfoQ : Vert.x 2와 Vert.x 3의 주요 차이점은 무엇입니까?

Tim : 우리는 Vert.x 3에서 많은 것을 할애하여 사용하기가 더 쉬워졌습니다.

몇 가지면에서 Vert.x 2는 꽤 컨테이너와 비슷했지만 Vert.x 3에서는 그 중 많은 부분을 제거했으며 Vert.x 3은 실제로 삽입 가능합니다. Vert.x 3 문서에서 Vert.x가 프레임 워크 또는 컨테이너가 아니라고 말하는 이유가 여기에 있습니다.

또한 클래스 로더 모델을 간소화했으며 기본적으로 단순한 플랫 모델 (예 : 별도의 클래스 로더가 없음)을 보유하고 있습니다. 이것은 단순 메인 클래스로 마이크로 서비스를 작성하고 원하는 부분을 사용하고 이동하려는 세계에 훨씬 더 적합합니다.

Vert.x 3에는 RxJava에 대한 지원 기능이 내장되어 있습니다. 모든 API의 Rx ified 버전을 제공하므로 콜백 기반 방식 (예 : Node.js와 유사)을 선호하지 않는 경우가 있습니다. 특히 여러 개의 데이터 스트림을 조정하려는 경우 Rx API를 사용하면 기능 스타일 작업을 사용하여 스트림을 결합 및 변환 할 수 있습니다.

또한 우리는 Vert.x의 실험적인 새로운 기능을 연구하여 클래식 동기 스타일로 응용 프로그램을 작성할 수 있지만 실제로 OS 스레드를 블럭하지는 않습니다. 아이디어를 사용하면 확장 성 이점을 얻을 수 없습니다. OS 스레드를 블럭하지만 비동기 API에 대한 프로그래밍의 콜백 지옥이 없습니다. 즉, 누워서 떡먹기입니다. 우리가 올바르게 이해할 수 있다면, 이것이 킬러 기능이라고 생각합니다.

Vert.x 3의 또 다른 핵심 기능은 바로 Vert.x-Web입니다.이 도구는 Vert.x를 사용하여 최신 웹 응용 프로그램을 작성하기위한 툴킷입니다.

Vert.x-Web에는 세련되고 현대적인 확장 가능한 웹 응용 프로그램을 만드는 데 필요한 모든 요소가 포함되어 있으며 물론 Vert.x에서 지원하는 모든 언어에서 사용할 수 있습니다. 여기에는 쿠키 및 세션 처리, 플러그 가능 인증, 템플리트 작성, 웹 소켓, SockJS 지원, 컨텐츠 협상 및 기타 많은 기능 등 기대할 수있는 모든 것이 포함되어 있습니다.

'전통적인'서버에서 렌더링 된 웹 응용 프로그램, HTTP / REST 마이크로 서비스 또는 클라이언트에서 렌더링 된 웹 응용 프로그램 등, 작성중인 모든 종류의 웹 응용 프로그램에 가장 적합합니다.

진행중인 새로운 작업 인 Vert.x 3 웹 사이트를 탐색 할 수 있습니다.이 사이트에는 다양한 부품에 대한 많은 정보가 있습니다.


InfoQ : 리액터 패턴에 대한 귀하의 의견은 무엇입니까?   Vert.x 에 어떻게 들어 맞는다고 생각합니까?

Tim : Vert.x는 "다중 반응기"라고 불리는 리액터 패턴의 변형을 사용합니다. 따라서 하나의 이벤트 루프를 갖는 대신 다중 이벤트 루프를 갖지만 동일한 핸들러가 항상 동일한 이벤트 루프에 의해 호출되도록 보장합니다. 즉, 코드를 단일 스레드로 작성 (동기화, 휘발성 등) 할 필요는 없지만 쉽게 확장 할 수 있습니다.

따라서 리액터 모델의 이점을 누릴 수 있지만 순수한 리액터 구현 (Node.js 와 달리)은 여러 서버 인스턴스를 배포하지 않고도 서버 코어를 통해 쉽게 확장 할 수 있습니다.


InfoQ : 런타임 메트릭스가 현대 개발에 얼마나 중요하다고 생각하십니까? Vert.x 3은 메트릭 수집을 쉽게 지원합니까?

Tim : 런타임 메트릭은 매우 중요하므로 Vert.x에서 무엇이 진행되고 있는지 알 수 있습니다. Vert.x 3은 Vertix 용 메트릭을 수집하기 위해 제공자를 플러그인 할 수있는 메트릭 SPI를 제공합니다. 우리는 DropWizard 메트릭을 사용하는 상자 메트릭 구현과 Hawkular를 사용하는 작업에서 또 다른 메트릭 구현을 제공합니다.


InfoQ : 성능 : TechEmpower 벤치 마크를 보았을 때 Vert.x를 사용하기 시작했고 Vert.x가 매우 독창적이었습니다. 일부 테스트에서는 가장 빠른 속도 였지만 가장 빠른 속도는 아니지만 3 위 안에 포함되었습니다. 최근에 나는 벤치 마크에서 경쟁하는 것을 보지 못했으며 Vertx 3에 초점을두고 있다고 가정합니다. Vertx 3 성능은 어떻습니까?

Tim : 이전 버전의 Vert.x에 대해 테스트했기 때문에 더 최근의 결과를 철회 했으므로 최신 버전을 실제로 나타내지 못했으며 벤치 마크를 최신 상태로 유지할 시간이나 리소스가 없었습니다. 

Vert.x 3은 아직 성능이 조정되지 않았지만이를 완료하고 3.0을 완성하면 벤치 마크를 최신 상태로 유지하고 결과를 게시하는 데 시간을 할애 할 수 있습니다.


InfoQ : Vertx는 다국어인가요? 어떤 언어의 개발자가 가장 성가시며 Vertx 커뮤니티가 구성하는 각 언어의 몇 퍼센트입니까? Vert.x 커뮤니티의 규모는 어느 정도입니까?

Tim : 제 견해로 모든 언어 커뮤니티에는 성가신 개발자와 매우 유익하고 지식이 많은 개발자가 있습니다.


InfoQ  Vert.x 커뮤니티의 규모는 어느 정도입니까? 

측정하기는 꽤 어렵지만 우리는 매우 적극적인 구글 그룹을 가지고 있으며 우리는 GitHub에서 가장 유명한 자바 프로젝트 중 하나입니다. 우리는 생산에서 우리를 사용하는 회사가 많습니다. 


InfoQ : Vert.x 개발을 쉽게하기 위해 Vert.x 3에서 수행 된 작업은 무엇입니까?

Tim : Vert.x 2에서 우리가 발견 한 사실 중 하나는 바로 Vert.x 전용 방식으로 일부 작업을 수행했기 때문에 일부 개발자들, 특히 "전통적인"Java 배경에서 나오는 개발자들에게는 상당히 까다로워 보일 수 있습니다. Maven 기반의 프로젝트와 패키징에 사용되는 것들. 예를 들어 다른 곳에서는 찾을 수없는 자체 설명자를 가진 모듈 시스템이있었습니다. 또한 Vert.x 2에는 다소 복잡한 클래스 로더 모델이있어서 IDE에서 쉽게 실행하기가 다소 까다 롭습니다.

Vert.x 3에서 우리는 그 grain 에 대한 압박을 그만두고 대부분의 Java 개발자가 기대하는 방식으로 작업을하기로 결정했습니다. 따라서 모듈 시스템을 제거하고 클래스 로더 모델을 단순화했습니다 (기본적으로 평면 클래스 로더 모델). 이제 Vert.x 구성 요소는 Maven 또는 Bintray의 다른 종속성처럼 처리 할 수있는 표준 병으로 패키지됩니다. 플랫 클래스 로더 모델은 IDE에서 실행하고 디버그하는 것을 더 쉽게 만들어주었습니다. 이 모든 것들이 Vert.x 3에 훨씬 쉽고 간단한 개발자 경험을 제공하는 데 기여했습니다.


InfoQ : Vert.x는 MongoDB, MySQL, PostgreSQL에 어떤 지원을합니까? 데이터베이스에서 비동기가 중요한 이유는 무엇입니까?

Tim : Vert.x 3은 단지 라이브러리이므로 다른 데이터베이스 라이브러리를 포함하여 다른 Java 라이브러리와 함께 사용할 수 있습니다. 그러나 대부분의 Java 데이터베이스 클라이언트는 블로킹하는 경향이 있으므로 Vert.x 이벤트 루프를 블럭하지 않도록주의해야합니다.

Vert.x 3은 기본적으로 JDBC 인터페이스를 랩핑하고 스레드 풀을 사용하여 호출하고 사용자에게 비동기 인터페이스를 제공하는 비동기 JDBC 클라이언트를 제공하므로 사용자가 직접 래핑하는 것에 대해 걱정할 필요가 없습니다. 분명히 클라이언트는 여전히 JDBC에 대한 내부 호출을 블럭하고 있지만 JDBC가 본질적으로 동기적이고 호출이 일반적으로 네트워크 I / O에서 블럭되기 때문에 우리가 할 수있는 일은별로 없습니다. 이상적인 세계에서 오라클은 공식 비동기 JDBC API를 가져올 것이고 드라이버 공급 업체는 논블럭 버전의 드라이버를 작성할 것입니다.하지만 전통적인 RDBM 벤더는 느린 속도로 보입니다.

NoSQL 공급 업체는 논블럭이 중요하다는 사실을 이해하는 데 훨씬 빠르다. 예를 들어 MongoDB는 Vert.x 3에서 사용하는 Mongo 3.0에서 완전히 비동기식 클라이언트를 가져 왔습니다. 또한 멋진 Mongo Rx 클라이언트가 있습니다.

따라서 데이터 액세스를 블럭하지 않는 옵션이 점점 늘어나고 있습니다. DB 벤더가 수요가 있다는 것을 깨닫고 시간이 지남에 따라 그 수가 증가 할 것으로 기대합니다.

논블럭 데이터베이스 액세스가 중요한 이유는 무엇입니까? 다시 말하지만, 많은 동시성으로 애플리케이션을 확장하는 것에 관한 것입니다.

애플리케이션이 JDBC API를 사용하여 원격 데이터베이스에 대해 데이터베이스 쿼리를 실행해야한다고 가정 해 보겠습니다. 명심하십시오. 이것은 하나의 데이터베이스가 아니며 다른 서버에있는 100 개의 데이터베이스가 될 수 있습니다. 결과를 반환하기 위해 각 쿼리가 평균 1 초가 걸린다고 가정 해 보겠습니다. 그리고 최대 200 (합리적인 숫자처럼 보임) 크기로 이러한 요청을 실행하는 스레드 풀이 있다고 가정 해 보겠습니다.

수학을 사용하면 초당 200 건이 넘는 요청을 처리 할 수 ​​없다는 것을 쉽게 알 수 있습니다. 이것이 시스템의 병목 현상입니다. 원격 서버는 더 많은 부하에 쉽게 대처할 수 있지만 최대 200 개의 스레드가 있기 때문에 결코 빠르게 진행될 수 없습니다. 당연히 DB 서버가 이미 클라이언트에서 비동기를 사용하면 처리량을 향상시키지 않을 것입니다. 그러나이 경우에는 전체 처리량을 제한하는 블로킹 모델의 여유 용량이 있습니다.


InfoQ : Java 8이 Vertx 3에 얼마나 중요합니까? 익명의 내부 클래스를 사용하는 것보다 람다 표현으로 Vertx가 더 매력적이라고 ​​생각하십니까?

Tim : Java 8은 Vert.x 3에서 매우 중요합니다. 아마도 우리에게 가장 중요한 두 가지 기능은 lambdas와 Nashorn입니다. Lambda는 이벤트 스타일 API에 대한 프로그래밍을 훨씬 더 멋지게 만들고 JavaScript 구현에서 Nashorn JavaScript 엔진을 사용합니다.


Vert.x 클러스터링 (여러 머신상에서 vert.x 인스턴스끼리 데이터공유) 을 할때 대해서 정리


1. ClusterManager 를 사용한다.

ClusterManager clusterManager = ((VertxInternal)vertx).clusterManager();
Map map = clusterManager.getSyncMap("mapName"); // shared distributed map


뒤에 Hazelcast IMap 을 사용하게된다. -cluster  파라미터와 헤즐케스트에 대한 설정을 했다고 가정한다.

이것은 내부 API 로서 사용되므로 ,일반적으로 제품에 대해서 추천하지 않는다. 테스트용으로는 괜찮을지도.


2. 헤즐케스트를 적극적으로 사용한다.

Hazelcast 를 워커버티클에서 직접적으로 접근해서 사용하라 Hazelcast 의 정적함수를 이용.

    Set<HazelcastInstance> instances = Hazelcast.getAllHazelcastInstances();
    HazelcastInstance hz = instances.stream().findFirst().get();
    Map map = hz.getMap("mapName"); // shared distributed map


주의 : . 헤즐케스트 API 는 블러킹되기때문에 워커버티클에서만 사용하라


3. Vert.x 2 버전은 클러스터 공유데이터를 지원하지 않는다. Vert.x 3 부터는  헤즐케스트 클러스터 매니저를

감싼 비동기 api 를 통해서 지원한다. (비동기에 유념하라)  


4. 일반적인 공유DB 를 사용하라.


5. 하나의 버티클을 공유맵 인스턴스를 가지고 관리하는 전용으로 만들어라. 다른 버티클들과 이벤트버스를

    통해서  관리하라.  


6. 어플리케이션을 애초에 공유데이터가 필요없게 만들어라.




http://stackoverflow.com/questions/12299132/clustering-and-shared-data-in-vert-x  참고 





순서 

1. Vert.x  설치 및 Hello world !! 

2. 간단히  Vert.x  다루어보기 

3..Vert.x 와 MongoDB 연결 

4. 실시간 통신

5. 모듈개발 

6. 배포 



D3 라이브러리를 이용하여 VIEW 를 그릴것이다.  (d3 라이브러리 좀 쩐다... 이거가지고 할수있는거 무궁무진할듯 ) 

http://d3js.org/

http://using.tistory.com/56


1. client.js 부터 살펴보자 

var eb = new vertx.EventBus(window.location.protocol + '//' +

                            window.location.hostname + ':' +

                            window.location.port + '/eventbus');

eb.onopen = function() {

  var renderListItem = function(mindMap) {

    var li = $('<li>');

    var openMindMap = function() {

      new MindMapEditor(mindMap, eb);    //해당 마인드맵이 클릭되면  MindMapEditor 객체를 만든다. 

      return false;                                      

    };                                                        


    var deleteMindMap = function() {

      eb.send('mindMaps.delete', {id: mindMap._id}, function() {

        li.remove();

      });

      return false;

    };

    // 네임클릭하면 마인드맵 활성화됨

    $('<a>').text(mindMap.name).attr('href', '#').on('click', openMindMap).appendTo(li); 

    $('<button>').text('Delete').on('click', deleteMindMap).appendTo(li);


    li.appendTo('.mind-maps');

  };


  $('.create-form').submit(function() {

    var nameInput = $('[name=name]', this);

    eb.send('mindMaps.save', {name: nameInput.val()}, function(result) {

      renderListItem(result);

      nameInput.val('');

    });

    return false;

  });


  eb.send('mindMaps.list', {}, function(res) {  // 서버와 접속되면 마인드맵 리스트를 얻어옵니다. 

    $.each(res.mindMaps, function() {

      renderListItem(this);  // 각 마인드맵을 인자로 넣어주고 리스트 갱신 

    })

  })

};



2. 다음은 editor.js  이거 새로 만들어서 추가해준다. 

function MindMapEditor(mindMap, eventBus) {   /MindMapEditor 생성자

  this.mindMap = mindMap;

  this.eventBus = eventBus;

  this.registerEventHandlers();      // MindMapEditor 객체당 새로운  이벤트핸들러를 등록해준다.

  this.initVisualization();

  this.renderVisualization();

}


MindMapEditor.width = 1280;

MindMapEditor.height = 800;

MindMapEditor.levelWidth = 150;

MindMapEditor.treeLayout = d3.layout.tree().size([MindMapEditor.height, MindMapEditor.width]);

MindMapEditor.diagonalGenerator = d3.svg.diagonal().projection(function(d) { return [d.y, d.x]; });


MindMapEditor.prototype.registerEventHandlers = function() {

  var self = this;

  

  // 이벤트 핸들러를 등록해준다. 나중에 서버에서 발생한 이벤트를 받을수 있게한다. 

  // 상상해본다. 브라우저에서 => 서버 로  Command 날린다.

 //  서버=> 모든 브라우저로  이벤트 전파한다.  이 시스템은 단지 이 두가지로 이루어진다. 

 // 따라서아래의 핸들러는 서버에서 발생한 이벤트를 브라우저에서 적용시키는 과정이다.  

  this.eventBus.registerHandler('mindMaps.events.'+self.mindMap._id, function(event) { 

    switch (event.event) {

      case 'nodeAdded':   self.onNodeAdded(event);   break;

      case 'nodeRenamed': self.onNodeRenamed(event); break;

      case 'nodeDeleted': self.onNodeDeleted(event); break;

    }

    self.renderVisualization();

  });

}


MindMapEditor.prototype.onNodeAdded = function(event) {

  var parent = findNodeByKey(this.mindMap, event.parentKey);

  if (parent) {

  if (!parent.children) {

  parent.children = [];

  }

  parent.children.push(event.node);

  }

}


MindMapEditor.prototype.onNodeRenamed = function(event) {

  var node = findNodeByKey(this.mindMap, event.key);

  if (node) {

  node.name = event.newName;

  }

}


MindMapEditor.prototype.onNodeDeleted = function(event) {

  var parent = findNodeByKey(this.mindMap, event.parentKey);

  if (parent) {

  for (var i=0 ; i<parent.children.length ; i++) {

   if (parent.children[i].key === event.key) {

    parent.children.splice(i, 1);

    return;

   }

  }

  }

}


MindMapEditor.prototype.addNode = function(parentNode) {

  this.eventBus.send('mindMaps.editor.addNode', {

  mindMapId: this.mindMap._id,

  parentKey: parentNode.key

  });

}


MindMapEditor.prototype.renameNode = function(node, newName) {

  this.eventBus.send('mindMaps.editor.renameNode', {

    mindMapId: this.mindMap._id,

    key: node.key,

    newName: newName

  });

}


MindMapEditor.prototype.deleteNode = function(parentNode, childNode) {

  this.eventBus.send('mindMaps.editor.deleteNode', {

    mindMapId: this.mindMap._id,

    parentKey: parentNode.key,

    key: childNode.key

  });

}


MindMapEditor.prototype.initVisualization = function() {

  this.vis = d3.select(".editor").html('').append("svg:svg")

    .attr("width", MindMapEditor.width)

    .attr("height", MindMapEditor.height)

    .append("svg:g")

      .attr("transform", "translate(10,0)");

}


MindMapEditor.prototype.renderVisualization = function() {

  var self = this;

  var nodes = MindMapEditor.treeLayout.nodes(this.mindMap).reverse();

  nodes.forEach(function(d) { d.y = d.depth * MindMapEditor.levelWidth; });

  

  var node = this.vis.selectAll("g.node")

    .data(nodes, function(d) { return d.key; });


  var nodeEnter = node.enter().append("svg:g")

    .attr("class", "node")

    .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

    .attr("opacity", "0");


  nodeEnter.append("svg:circle")

    .attr("r", 4.5)

    .style("fill", "lightsteelblue")

    .on("click", function(c) { self.addNode(c); });


  nodeEnter.append("svg:text")

    .attr("x", 10)

    .attr("dy", ".35em")

    .text(function(d) { return d.name; })

    .on("click", function(d) {

      var text = prompt('Enter a name for this node', d.name);

      if (text) {

        self.renameNode(d, text);

      }

    });


  node.transition()

    .attr("opacity", "1")

    .attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; })

    .select("text")

      .text(function(d) { return d.name; });


  node.exit().remove();



  var link = this.vis.selectAll("path.link")

    .data(MindMapEditor.treeLayout.links(nodes), function(d) { return d.target.key; });


  link.enter().insert("svg:path", "g")

    .attr("class", "link")

    .attr("opacity", "0")

    .attr("d", MindMapEditor.diagonalGenerator)

    .on('click', function(l) {

      self.deleteNode(l.source, l.target);

    });


  link.transition()

    .attr("d", MindMapEditor.diagonalGenerator)

    .attr("opacity", "1");


  link.exit().remove();

}



3. mindmap_editor.js 만들어서 추가 

var eventBus = require('vertx/event_bus');
var mindMapUtils = require('web/mindmap_utils');

function newNodeKey() {
  return java.util.UUID.randomUUID().toString();// js 파일에서 자바를 쓸수있는건 vert.x 가 JVM 위에서 돌아감.
}


// 중요 함수다.  위에서 말했다시피 이 시스템의 구조는  브라우저 => 서버 로 Command 실행
//  서버에서 => 모든 브라우저로 이벤트 전파이다. 아래함수는 모든 브라우저(선택된 마인드맵 id 의 핸들러가 등록된)
// 로 이벤트를 전파한다. 
function publishMindMapEvent(mindMap, event) {
  eventBus.publish('mindMaps.events.'+mindMap._id, event);
}

// 브라우저 => 서버로의 Command 를 처리해주는 서버 핸들러
eventBus.registerHandler('mindMaps.editor.addNode', function(args) { 
  eventBus.send('mindMaps.find', {_id: args.mindMapId}, function(res) {
  if (res.mindMap) {
 var mindMap = res.mindMap;
 var parent  = mindMapUtils.findNodeByKey(mindMap, args.parentKey);
 var newNode = {key: newNodeKey()};
 if (args.name) {
newNode.name = args.name;
 } else {
newNode.name = 'Click to edit';
   }

 if (!parent.children) {
parent.children = [];
 }
 parent.children.push(newNode);

 eventBus.send('mindMaps.save', mindMap, function() {
   publishMindMapEvent(mindMap, {event: 'nodeAdded', parentKey: args.parentKey, node: newNode});
 });
    }
  });
});

eventBus.registerHandler('mindMaps.editor.renameNode', function(args) {
  eventBus.send('mindMaps.find', {_id: args.mindMapId}, function(res) {
    if (res.mindMap) {
      var mindMap = res.mindMap;
      var node    = mindMapUtils.findNodeByKey(mindMap, args.key);

      if (node) {
      node.name = args.newName;
      eventBus.send('mindMaps.save', mindMap, function(reply) {
          publishMindMapEvent(mindMap, {event: 'nodeRenamed', key: args.key, newName: args.newName});
      });
      }
    }
  });
});

eventBus.registerHandler('mindMaps.editor.deleteNode', function(args) {
  eventBus.send('mindMaps.find', {_id: args.mindMapId}, function(res) {
    if (res.mindMap) {
      var mindMap = res.mindMap;
      var parent  = mindMapUtils.findNodeByKey(mindMap, args.parentKey);

      parent.children.forEach(function(child, index) {
        if (child.key === args.key) {
          parent.children.splice(index, 1);
          eventBus.send('mindMaps.save', mindMap, function(reply) {
            publishMindMapEvent(mindMap, {event: 'nodeDeleted', parentKey: args.parentKey, key: args.key});
          });
        }
      });
    }
  });
});

실행 모습 




여기까지 대략 프로젝트 구성은 이렇다.




순서 

1. Vert.x  설치 및 Hello world !! 

2. 간단히  Vert.x  다루어보기 

3..Vert.x 와 MongoDB 연결 

4. 실시간 통신

5. 모듈개발 

6. 배포 


먼저 MongoDB  를 설치해봅시다. (윈도우에) 

1. https://www.mongodb.org/downloads  요기서 MSI 파일 다운로드후 설치  (더블클릭후 ㄱㄱ 씽) 

2. 아무데나 폴더하나 만듭니다. ( 예:  D:\mongodb\DATA)

3. mongod --dbpath d:\mongodb\DATA   치면 DB 가 실행됩니다. 

4. mongo 치면 가지고 놀수있게 됩니다.  (mongod 는 실행파일 / mongo 는 클라이언트) 



1.  app.js 를 다음과 같이 바꾸어봅니다.

var container = require("vertx/container");

container.deployModule("io.vertx~mod-web-server~2.0.0-final", {

  port: 8080,

  host: "localhost",

  bridge: true,

  inbound_permitted: [

    { address: 'mindMaps.list' },

    { address: 'mindMaps.save' },

    { address: 'mindMaps.delete' }

  ]

});

container.deployModule("io.vertx~mod-mongo-persistor~2.0.0-final", {   // mongo 모듈이 추가되었네요 !

  address: "mindMaps.persistor",    // 모듈을 위한 이벤트 버스 주소 

  db_name: "mind_maps"              //  이 퍼시스터가 사용할 데이타베이스 이름 

});


container.deployVerticle('mindmaps.js');



2. mindmaps.js 를 다음과 같이 실제 db 연결로 바꾸어줍니다. (이전에는 메모리상의 객체를 사용했지요) 

var eventBus = require('vertx/event_bus');

var console  = require('vertx/console');


function sendPersistorEvent(command, callback) {

  eventBus.send('mindMaps.persistor', command, function(reply) {  // mongo 퍼시스터에 명령을 보내고 

    if (reply.status === "ok") {                                                  // 응답을 받은후에 핸들러 호출 

      callback(reply);

    } else {

      console.log(reply.message);

    }

  });

};


eventBus.registerHandler('mindMaps.list', function(args, responder) {

  sendPersistorEvent(

    {action: "find", collection: "mindMaps", matcher: {}},    // mindMaps 에서 모든것을 가져와서 응답~

    function(reply) {

      responder({mindMaps: reply.results}); 

    }

  );

});


eventBus.registerHandler('mindMaps.find', function(args, responder) {

  sendPersistorEvent(

    {action: "findone", collection: "mindMaps", matcher: {_id: args._id}}, //  id 에 대당하는것을 리턴~

    function(reply) {

      responder({mindMap: reply.result}); 

    }

  );  

});


eventBus.registerHandler('mindMaps.save', function(mindMap, responder) {

  sendPersistorEvent(

    {action: "save", collection: "mindMaps", document: mindMap}, //  새 mindMap 을 저장 

    function(reply) {

      mindMap._id = reply._id;

      responder(mindMap);

    }

  );

});


eventBus.registerHandler('mindMaps.delete', function(args, responder) {

  sendPersistorEvent(

    {action: "delete", collection: "mindMaps", matcher: {_id: args.id}},//  id 를 가진 mindMap 객체 삭제    function(reply) {

      responder({});

    }

  );

});




순서 

1. Vert.x  설치 및 Hello world !! 

2. 간단히  Vert.x  다루어보기 

3..Vert.x 와 MongoDB 연결 

4. 실시간 통신

5. 모듈개발 

6. 배포 


먼저 다음 글을 읽고 시작하자. 


버티클 간의 모든 통신은 이벤트 버스를 통해 이루어진다. 이벤트 버스는 버텍스의 중추 신경계이다. 

버티클끼리 직접통신은 불가능하다. 버티클은 이벤트 버스의 주소에 이벤트를 발행해서 다른 버티클에게 메세지를 보내고,

이벤트 버스주소에 이벤트르르 받을 핸들러를 등록하여 다른 버티클이 보낸 메세지를 받는다.  이벤트를 누가 주고,누가 

받는지 버티클은 모른다. 각 이벤트는 주로 JSON 형식으로 사용된다. 


버텍스 이벤트 버스의 3가지 기본 통신패턴


1. 발행/구독  : 한 버티클이 이벤트를 발행하면 등록된 모든 핸들러가 받는다.

2. 일대일      : 한 버티클이 이벤트를 발행하면 하나의 핸들러만 받고, 여러개가 등록되있으면 라운드 로빈방식으로 분배된다.

3. 요청응답   : 일대일 방식의 확장이고, 발송자는 결과를 받을 응답핸들러를 같이보내고, 수신자는 핸들러에서 응답핸들러에 결과를 보내준다.


1편의 프로젝트에 app.js 을 다음과 같이 바꾸어준다.


var container = require("vertx/container");


container.deployModule("io.vertx~mod-web-server~2.0.0-final", {

  port: 8080,

  host: "localhost",

  bridge: true,                               // 브라우저에서 서버측으로 접근을 허용하게 해준다. (이벤트버스브릿지) 

  inbound_permitted: [

    { address: 'mindMaps.list' },       // 외부로 넘길수있는 이벤트 목록 설정 (mindmaps.js 코드에 각각에 대한 

    { address: 'mindMaps.save' },     //                                             핸들러 함수가 존재한다) 

    { address: 'mindMaps.delete' }

  ]

});

container.deployVerticle('mindmaps.js');  // mindmaps.js 버티클을 배포한다. 



1편의 프로젝트에 index.html 을 다음과 같이 바꾸어준다.


<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="utf-8">

</head>

<body>

  <ul class="mind-maps">

  </ul>

  <h2>Create a Mind Map</h2>

  <form class="create-form">

    <input type="text" name="name">

    <input type="submit" value="Create">

  </form>

  // 웹소켓기반으로 양방향 통신 기능을 제공하는 SocketJS , 구 브라우저에서도 동작함. 

  <script src="//cdnjs.cloudflare.com/ajax/libs/sockjs-client/0.3.4/sockjs.min.js"></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/vertx/2.0.0/vertxbus.min.js"></script>

  <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>

  <script src="/client.js"></script>

</body>

</html>



web 폴더에 client.js 파일을 생성한후 다음과 같이 코딩해준다.


var eb = new vertx.EventBus(window.location.protocol + '//' +     // vertx.EventBus 객체 생성 

                            window.location.hostname + ':' +                 // 웹서버가 실행중인 같은 호스트와 포트의

                            window.location.port + '/eventbus');             // /eventbus 경로에 브릿지를 배포 


eb.onopen = function() {      // 이벤트를 송,수신하기전에 이벤트 버스에 연결되면 실행 

  var renderListItem = function(mindMap) {

    var li = $('<li>');

    var deleteMindMap = function() {

      eb.send('mindMaps.delete', {id: mindMap._id}, function() {

        li.remove();

      });

      return false;

    };

    $('<span>').text(mindMap.name).appendTo(li);

    $('<button>').text('Delete').on('click', deleteMindMap).appendTo(li);


    li.appendTo('.mind-maps');    // mind-maps (ul) 클래스에 li 추가 

  };


  $('.create-form').submit(function() {            // submit 시 name  을  인자로 mindMaps.save 호출

    var nameInput = $('[name=name]', this);

    eb.send('mindMaps.save', {name: nameInput.val()}, function(result) { // 응답받은 결과물로 List 갱신 

      renderListItem(result);

      nameInput.val('');

    });

    return false;

  });


  eb.send('mindMaps.list', {}, function(res) {        // 이벤트버스의 mindMaps.list 이벤트 호출 (요청-응답)

    $.each(res.mindMaps, function() {

      renderListItem(this);

    })

  })


};



루트폴더에 maindmmaps.js 를 만들고 다음과 같이 타이핑해준다.


var eventBus = require('vertx/event_bus');


var mindMaps = {};                                  //  빈 mindMaps 객체 생성 


eventBus.registerHandler('mindMaps.list', function(args, responder) {  // mindMaps.list 핸들러 등록 

  responder({"mindMaps": Object.keys(mindMaps).map(function(key) { //호출자에게 키에 해당되는 mindMap                                                                                                       객체를 돌려준다.

  return mindMaps[key];

  })});

});


eventBus.registerHandler('mindMaps.save', function(mindMap, responder) {// mindMaps.list 핸들러 등록 

  if (!mindMap._id) {

  mindMap._id = Math.random();

  }

  mindMaps[mindMap._id] = mindMap;

  responder(mindMap);

});


eventBus.registerHandler('mindMaps.delete', function(args, responder) {// mindMaps.delete핸들러 등록 

  delete mindMaps[args.id];

  responder({});

});



결과 


프로젝트 구조 




간단한 웹 어플리케이션을 만들어 보겠습니다. (http://www.yes24.com/24/goods/14562423?scode=032&OzSrank=1 )

프런트엔드는 AngularJS 고 서버는 Vert.x 로 ~ 마인드맵 어플리케이션을 만들겁니다.


순서 

1. Vert.x  설치 및 Hello world !! 

2. 간단히  Vert.x  다루어보기 

3..Vert.x 와 MongoDB 연결 

4. 실시간 통신

5. 모듈개발 

6. 배포 



1. Vert.x  설치 및 Hello world !!


윈도우 기준 설치 

- JAVA 7 버전을 설치합니다.(http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html)
- JAVA PATH 를 세팅합니다.

CMD 창에서 java -version 치면 java version "1.7. xxx " 뭐 이렇게 나오면 됩니다.

- VERT.X 를 설치합니다.(https://bintray.com/vertx/downloads/distribution/2.1 여기서 zip 다운후 아무데나 압축해제) 
- VERT.X  PATH 를  세팅합니다.


CMD 창에서 vertx version 치면 2.1 나오면 됩니다.

설치 끝 



Hello world 해보기 


아무 편집기에서 hello.js 파일만들봅니다. 개인적으론 서브라임 텍스트 (http://www.sublimetext.com/3) 가 좋더군요.

var console = require("vertx/console");

console.log("hello world");

이거 넣으면 Vert.x 어플리케이션 개발 끝입니다.



vertx run hello.js 를 해당 폴더에서 쳐주면 무슨 모듈같은거 자동으로 다운받고 바로 실행합니다.

"hello world"

가 찍혔네요.


웹서버 

vert.x 만으로 웹서버설정에 필요한 모든게 다 있지만 , 좀더 편하게 사용하기위한 상위레벨 모듈을 사용한다.

1. mindmap  이라는 폴더를 만든다.

2. 내부에 app.js 파일을 만들고 다음과 같이 타이핑한다. 

var container = require("vertx/container");

container.deployModule("io.vertx~mod-web-server~2.0.0-final", {

  port: 8080,

  host: "localhost"

});

3. vertx run app.js 실행시킨다.(폴더에가보면 웹서버 모듈을 다운받은걸 확인할수있다.)

4. 웹브라우저를 열고 http://localhost:8080/ 접속하면 404 에러난다.

5. web 이라는 폴더만들고 내부에 index.html 만든후 다음과 같이 타이핑한다.

<!DOCTYPE html>

<html lang="en">

<head>

  <meta charset="utf-8">

</head>

<body>

      Hello!

</body>

</html>


브라우저에 Hello ! 찍힌것을 확인할수있다. 




http://helloworld.naver.com/helloworld/textyle/163784


vert.x는 현재 가장 뜨겁게 부상하고 있는 서버 프레임워크입니다. 모든 서버 프레임워크가 그렇듯이 고성능과 다양한 프로토콜 지원을 장점으로 내세우고 있습니다. vert.x는 여기서 한 발 더 나아가 서버 네트워크 환경을 구축하고 운영하는 환경까지도 고려하고 있습니다. 즉, vert.x는 하나의 '서버 프로세스 데몬'을 제작하는 것뿐만 아니라, 클러스터링 환경에서 동작하는 여러 서버 프로세스 데몬을 제작하는 방법에 대한 고민까지 담고 있습니다.

그렇기 때문에 vert.x가 어떤 방식으로 고성능을 내고 있는지, 어떤 네트워크 환경을 고려하고 있는지 충분히 시간을 들여 알아볼 가치가 있다고 할 수 있습니다.

vert.x의 철학

vert.x는 Node.js로부터 영향을 받은 프로젝트다. vert.x는 Node.js처럼 Event-based 프로그래밍 모델을 제공하는 서버 프레임워크다. 그렇기 때문에 vert.x의 API는 Node.js와 매우 유사하다. 둘 모두 비동기 형태의 API를 제공한다.

Node.js는 JavaScript로 만들어졌지만, vert.x는 Java로 제작되었다. 하지만 vert.x를 Node.js의 Java 버전이라고 이해하기에는 무리가 있다. vert.x가 Node.js로부터 영향을 받은 것은 사실이지만, vert.x는 Node.js와 다른 고유한 철학을 가지고 있기 때문이다.

대표적인 vert.x의 설계 철학을 정리하면 다음과 같다.

 

참고) Netty와 vert.x의 관계

vert.x의 성능을 논하기 전에 Netty와 vert.x의 관계를 정리할 필요가 있다. vert.x는 Netty를 사용한다. 즉, 다중 I/O 처리에 Netty를 사용한다. 그렇기 때문에 vert.x와 Netty의 성능 차이를 확인하는 것은 무의미하다.
vert.x는 Netty와는 다른 독자적인 API와 기능을 제공하는 다른 목적의 서버 프레임워크다. Netty는 로우레벨 수준의 I/O를 다룰 수 있는 프레임워크고, vert.x는 그보다는 하이레벨 영역을 다룬다.

Node.js와의 성능 비교

vert.x가 제공하는 기능이 Node.js와는 다르더라도 둘 사이의 성능을 비교하는 것은 충분히 의미 있는 일이다. 그림1과 그림2는 vert.x(Java, Ruby, Groovy)와 Node.js의 성능을 비교한 자료다(출처:http://vertxproject.wordpress.com/2012/05/09/vert-x-vs-node-js-simple-http-benchmarks/).

그림 1은 HTTP 서버를 만들고 200/OK 응답만을 주었을 때의 성능을 비교한 결과다. 그림 2는 72 바이트 크기의 정적 HTML 파일을 응답 결과로 제공하는 경우에 성능을 비교한 결과다. vert.x 제작자가 밝힌 성능이고 스스로도 엄격한 환경에서 실시한 테스트가 아니므로 상대적인 성능 격차에만 주목하는 것이 좋을 것으로 보인다.

ad70637d150cff0744f460a6da1bcb00.png

그림 1 200/OK 응답만 주었을 때의 성능 비교

572fdeba65175722f09387935f981788.png

그림 2 72바이트 크기의 정적 파일 제공 성능 비교

주목할만한 점은 Node.js보다 vert.x-JavaScript의 성능이 좋다는 것이다. 이 성능 비교 결과가 신뢰성이 매우 높다고 하더라도, 단순히 Node.js에 비해 vert.x가 낫다고 말하기는 어려울 수 있다. Node.js는 Socket.io와 같은 훌륭한 모델을 제공하고 있을 뿐만 아니라, 많은 레퍼런스를 확보하고 있기 때문이다.

vert.x 용어들

vert.x는 vert.x만의 고유 용어를 정의하거나, 일반적인 용어를 vert.x에서 다시 정의해서 사용하기도 한다. vert.x를 잘 이해하려면 vert.x가 정의한 용어를 잘 이해해야 한다. vert.x에서 사용하는 대표적인 용어를 정리해 보았다.

Verticle

vert.x에서 배치(deploy)의 기본 단위다. Java의 경우라면 main 메서드가 있는 클래스가 된다. Verticle은 또한 main 메서드에서 참조되는 다른 스크립트를 포함할 수 있다. .jar 파일이나 리소스를 포함할 수 있다. 애플리케이션은 하나의 Verticle로 이루어질 수도 있고, event bus를 통해 서로 통신하는 여러 개의 Verticle로 이루어질 수도 있다. Java로 생각하면 독립적으로 실행 가능한 Class 또는 .jar 파일로 이해할 수 있겠다.

vert.x 인스턴스

Verticle은 vert.x 인스턴스 안에서 실행되고, vert.x 인스턴스는 자신의 JVM 인스턴스 안에서 실행된다. 단일 vert.x 인스턴스 안에서는 동시에 실행되는 많은 Verticle이 존재할 수 있다. 각각의 Verticle은 고유의 클래스 로더를 가질 수 있다. 이로 인해 Verticle 간에 스태틱 멤버, 글로벌 변수 등을 통한 직접적인 상호작용을 막을 수 있다. 네트워크상의 여러 호스트에서 동시에 많은 vert.x 인스턴스가 실행될 수 있고 event bus를 형성해서 vert.x 인스턴스 간에 클러스터링되도록 설정할 수 있다.

동시성(concurrency)

Verticle 인스턴스는 항상 동일한 스레드에서 실행됨이 보장된다. 모든 코드를 단일 스레드 동작 형태로 개발할 수 있기 때문에, vert.x를 사용하는 개발자에게 개발하기 편한 환경을 제공하는 것이라 할 수 있다. 게다가 레이스 컨디션이나 데드락이 발생하지 않게 할 수도 있다.

Event-based Programming Model

vert.x는 Node.js 프레임워크와 비슷하게 Event-based 프로그래밍 모델을 제공한다. vert.x로 서버 프로그래밍을 할 때 개발해야 하는 코드의 대부분은 이벤트 핸들러에 관한 것이다. 예를 들어, TCP 소켓으로부터 데이터를 수신하기 위해 핸들러를 설정하는 것이나 데이터가 도착할 때 호출될 핸들러를 제작하는 것이다. 이외에도 'Event bus에서 메시지를 수신할 때', 'HTTP 메시지를 수신할 때', '커넥션이 종료되었을 때', '타이머가 종료 되었을 때' 알림을 받기 원한다면 핸들러를 작성하면 된다.

Event Loops

vert.x 인스턴스는 내부적으로 스레드 풀을 관리한다. vert.x는 가급적 스레드 풀의 개수를 CPU 코어 수와 일치할 수 있게 한다.

그리고 이 각각의 스레드에서는 Event Loop를 실행한다. Event Loop란 확인해야 할 이벤트를 루프(loop)를 돌면서 확인하는 것이다. 가령 소켓에 읽을 데이터가 있거나, 어떤 타이머에 이벤트가 발생했는지 확인하는 것과 같은 것들이다. 루프를 돌다가 처리해야 할 이벤트가 있다면, 해당 핸들러를 호출하는 방식으로 vert.x가 동작한다(물론 이때 핸들러 처리 시간이 길다거나 블로킹 I/O가 있다거나 할 때는 별도의 작업이 필요하다. 다음 게시글에서 소개할 예정이다.).

Message Passing

Verticle 간의 통신은 Event Bus를 이용한다. Verticle을 actor라고 생각하면, Message Passing은 Erlang 프로그래밍 언어에서 유명해진 actor 모델과 유사하다. vert.x 서버에서는 많은 Verticle 인스턴스 생성 및 이들 간의 message passing을 통해 Verticle 코드에 대한 멀티 스레드 실행이 없이도 사용 가능한 코어에 맞게 시스템 확장이 가능하다.

Shared data

Message passing이 매우 유용하긴 하지만 모든 종류의 애플리케이션 동시성 상황에서 최고의 접근 방법은 아니다. 캐시가 대표적인 예다. 어떤 캐시를 어느 하나의 Verticle만 가지고 있다면 매우 비효율적이 된다. 이 캐시가 다른 Verticle에도 필요한 내용이라면 Verticle이 각각 같은 내용의 캐시 데이터를 관리하여야 하기 때문이다.

그렇기 때문에 vert.x는 전역에서 접근할 수 있는 방법을 제공한다. 바로 Shared Map이다. 그리고 Verticle 사이에서는 오직 불변(immutable) 데이터만 공유되게 하고 있다.

vert.x Core

이름 그대로 vert.x의 핵심 기능이다. Verticle에서 직접적으로 호출될 수 있는 기능은 모두 이 Core에 담겨있다. 당연하게 이 Core는 vert.x가 지원하는 프로그래밍 언어 API에서 접근할 수 있다.

vert.x 아키텍처

vert.x의 대략적인 아키텍처는 다음 그림과 같다.

374e032351d88c70dc05736e3079e6ca.png

그림 3 vert.x 아키텍처(원본 출처: http://www.javacodegeeks.com/2012/07/osgi-case-study-modular-vertx.html)

vert.x의 기본 실행 단위는 Verticle이고 동시에 여러 Verticle이 하나의 vert.x 인스턴스에서 실행될 수 있다. Verticle은 Event-Loop 스레드에서 실행된다. 하나의 호스트는 물론 네트워크상의 다른 여러 호스트에서 여러 vert.x 인스턴스가 실행될 수 있는데, 이때 Verticle이나 Module 간에는 Event Bus를 통해 통신할 수 있다.

요약하면, vert.x 애플리케이션은 Verticle 또는 Module 의 조합으로 이루어지며 이들 간의 통신은 Event Bus를 사용한다.

vert.x 프로젝트 구조

다음 그림은 github의 vert.x 페이지에서 소스 코드를 복제(clone)해 Eclipse에서 본 vert.x 프로젝트 구조다.

49e1c3d8d3329e1bd3a1043166caf566.png

그림 4 vert.x 소스 트리

전체적인 구성을 살펴보면 다음과 같다.

  • 핵심 library인 vertx-core
  • 배포 및 라이프사이클을 관리하는 vertx-platform
  • Core Java API를 다른 언어로 노출하는 vert-lang

프로젝트 빌드(build) 시스템으로는 Ant와 Maven의 장점을 갖췄다는 Gradle를 사용한다.

vert.x 설치 및 간단한 예제 실행

vert.x를 사용하려면 반드시 JDK7이 필요하다. vert.x는 JDK7에 있는 invokeDynamic을 사용하기 때문이다.

vert.x는 매우 간단하게 설치할 수 있다. https://github.com/purplefox/vert.x/downloads에서 압축된 설치 파일을 원하는 위치에 다운로드해 압축을 푼 다음, bin 디렉터리를 PATH 환경 변수에 추가하면 설치를 완료할 수 있다. 커맨드 창에서 vertx version을 실행해 버전 정보가 제대로 나오면 설치가 성공한 것이다.

이제는 "Hello World!"를 출력하는 간단한 웹 서버를 JavaScript로 작성하고 실행해 보자. 다음과 같이 코드를 작성한 후 server.js로 저장한다. Node.js 코드와 거의 흡사한 형식이다.

1
2
3
4
5
load('vertx.js');
 
vertx.createHttpServer().requestHandler(function(req) {
    req.response.end("Hello World!");
}).listen(8080, 'localhost');

생성한 server.js 애플리케이션을 다음과 같이 vert.x 명령어로 실행한다.

1
%> vertx run server.js

브라우저를 열고 http://localhost:8080에 접속해 "Hello World!" 메시지를 볼 수 있으면 성공이다.

다른 언어로 작성 된 예제를 살펴보자. 다음은 Java로 작성한 예제다. 정적 파일을 읽어 HTTP 응답으로 제공하는 웹 서버를 작성해 본 것이다.

1
2
3
4
5
6
7
8
Vertx vertx = Vertx.newVertx();
vertx.createHttpServer().requestHandler(new Handler<httpserverrequest>() {
    public void handle(HttpServerRequest req) {
        String file = req.path.equals("/") ? "index.html" : req.path;
        req.response.sendFile("webroot/" + file);
    }
}).listen(8080);
</httpserverrequest>

다음은 Groovy로 작성한 코드로 앞의 Java로 작성한 예제와 같은 기능을 한다.

1
2
3
4
5
def vertx = Vertx.newVertx()
vertx.createHttpServer().requestHandler { req ->
    def file = req.uri == "/" ? "index.html" : req.uri
    req.response.sendFile "webroot/$file"
}.listen(8080)

NHN과 vert.x

NHN의 플랫폼 개발 부서에는 vert.x가 정식으로 릴리스되기 전부터 개발 과정을 지켜보고 있었다. vert.x의 가능성을 높이 샀기 때문이다. 그리고 2012년 6월부터 메인 개발자인 Tim Fox와 교류하여 vert.x를 발전시켜 나갈 수 있도록 논의를 진행하고 있다. 예를 들어, Socket.io는 Node.js에서만 사용할 수 있었는데, 이를 vert.x에서도 Java로 사용할 수 있게 포팅 작업을 진행했고 현재 개발이 완료된 상태다. 다음은 github의 vert.x 레파지토리에 있는 pull request 요청 링크다.

산출물인 socket.io vert.x 모듈은 현재 개발 중인 RTCS 2.0 버전(vert.x + Socket.io)에 사용될 예정이다.

Node.js가 지금처럼 활성화된 것은 Socket.io 덕분이었는데, vert.x에서 Socket.io를 사용할 수 있다면 vert.x 또한 많은 사용 사례가 생길 것으로 예상한다. 또한 이 socket.io vertx 모듈을 임베디드 라이브러리 형태로 사용하면 Java 기반의 애플리케이션에서도 socket.io를 사용할 수 있게 된다는 점에서 의미가 있다 하겠다.

참고) RTCS 란?

RTCS(Real Time Communication System)는 NHN의 Real Time Web 개발 플랫폼으로, 브라우저와 서버 간에 실시간으로 메시지를 전달할 수 있게 도와주는 플랫폼이다. RTCS는 현재 야구9단, 미투데이 채팅, 밴드(BAND) 채팅에 적용되어 있다.

마치며

vert.x는 2012년 5월에 첫 버전이 나왔다. 2009년에 첫 버전이 나온 Node.js에 비하면 역사가 매우 짧다고 할 수 있다. 그렇기 때문에 아직 레퍼런스가 많지 않다. 하지만 vert.x는 VMware의 든든한 후원을 받고 있고 Cloud Foundry에서 구동할 수 있기 때문에, 앞으로 많은 레퍼런스가 확보될 것으로 보인다.

참고 자료


Understanding Vert.x Components

 http://www.cubrid.org/blog/dev-platform/understanding-vertx-architecture-part-2/



Figure 1: Vert.x Architecture (Component) Diagram.

Figure 1 above shows a diagram of Vert.x components. As shown in the figure, in all Vert.x instances (these can be understood as a JVM), a Hazelcast is embedded and runs. The embedded Hazelcast is connected to Hazelcast in other Vert.x instances. Event Bus uses functions of Hazelcast. Hazelcast itself provides a certain level of reliability (because of WAL records and data duplication). So, events can be forwarded with a certain level of reliability.



주요 포인트:

1. HTTP Server / Net Server 를 내장하고 있다.
2. 헤즐케스트끼리 데이터 공유한다. 
3. Event Bus 로 메세지 교환한다.
4. Verticle 객체는 일을 백그라운드 쓰레드에게 전가한다. (블록킹)
    논블럭킹이라면 훨씬 속도면에서 효율적일텐데...상태가 깨지는걸 방지하는 철학?
5. Accept   쓰레드 풀이있다.
6. Verticle 당 하나의 네티의 NioWorker 와 연결된다.
7. 하나의 노드에서 데이타를 공유해서 가지고있으며 서버도 공유한다.

+ Recent posts