장문의 글을 올리기 보다는, 바로 어떤 결과가 나오는 짧은 팁만 올리고, 댓글로 보충 내용을 올리는 쪽으로 해보겠습니다. 방송 형태가 적합하겠지만, 할 줄을 몰라서...(컴맹입니다)
------------------------------------------------------------------------------------------------------
사실 꽤나 글을 새로 못올리고 있었는데, 찾는 분이 없는걸 보니...재미 없기만 한 내용인건 사실인가 봅니다. 그래도 칼을 빼들었으니...5회 정도까지는 글을 적어볼까 합니다.
이번에는, 드디어 Vert.x 의 꽃이라고 할 수 있는 EventBus 를 다뤄볼까 합니다.
EventBus 을 가장 쉽게 이해할 수 있는 방법은...Vert.x 을 위한 비동기 Method...정도로 정의를 내릴 수 있겠네요. 실제 Method 처럼 선언을 하고 사용을 합니다. 단지...좀 사용상의 제약이 있고 비동기로 처리를 한다는 정도의 차이가 있겠네요.
백문이 불여일타...코드를 보면서 설명을 드리겠습니다.
EventBus eventBus = vertx.eventBus();
eventBus.consumer("Test.Login", (Message<JsonObject> message) -> {
JsonObject param = message.body();
System.out.println(param.getString("METHOD") + " Start!!!");
message.reply(param.getString("METHOD") + " End");
});
먼저 EventBus 을 사용하기 위해서는 vertx 개체로부터 eventBus() 을 이용해 개체를 얻어와야 합니다. 그리고 Method 선언과 마찬가지의 작업인 consumer() 을 통해 하나의 EventBus 을 등록하게 됩니다.
첫번째 매개변수는 Method 의 이름과 같은...이름, 그리고 다음 매개변수는 실제 코드 부분입니다. Message 개체 안에 매개변수를 하나의 Bean 처럼 담아서 가져올 수 있는데, 하나의 개체만 받아올 수 있기 때문에 보통 JsonObject 에 필요한 정보를 입력해서 보냅니다. 이렇게 받아온 Message 개체는 body() 을 이용해서 실제 값을 받아올 수 있습니다.
그럼 이번에는 실제 호출 부분을 보겠습니다.
eventBus.send("Test." + method, param, (AsyncResult<Message<String>> result) -> {
if (result.succeeded()) {
String returnValue = result.result().body();
request.response().putHeader("content-type", "application/json");
request.response().end("{\"METHOD\": \"" + method + "\", \"RESULT\": \"" + returnValue + "\"}");
} else {
request.response().putHeader("content-type", "application/json");
request.response().end("{\"METHOD\": \"" + method + "\", \"ERROR\": \"" + result.cause().getMessage() + "\"}");
result.cause().printStackTrace();
}
});
예전 코드에서 조금 확장을 해봤는데, 저는 주로 호출된 URL 을 이용해서 consumer() 로 선언된 EventBus 을 자동으로 호출하고, 결과를 분기해서 처리하는 방법으로 개발합니다. 생각하지 않고 그냥 이렇게 만들어두는게 개인적으로는 편하더군요.
위의 코드들이 모두 적용된 소스는 아래 깃헙에 올라가 있습니다.
이 코드를 실행해 보시면, http://localhost:8080/Login 와 같이 호출을 할 경우 Test.Login 을 호출해서 콘솔에 메세지를 출력하고, 사용자의 브라우저에는 METHOD 와 RESULT 키가 포함된 JSON 구문이 출력될 것입니다. 물론 METHOD 부분의 글자를 조금 바꿔서 보내면...RESULT 대신에 ERROR 가 포함된 JSON 구문이 출력될 것입니다. 아쉽게도 에러 메세지는 부정확하거나 null 이 포함되어 있을 확률이 큽니다. 아직 Vert.x 의 라이브러리의 완성도가 높은 편은 아니거든요. (그렇다고 실제 서비스에 사용하지 못할 정도의 버그가 있는 건 아닙니다. 완성도가 좀 낮을 뿐...)
다음으로 아래 예제를 통해 node.js 와 마찬가지로 Vert.x 가 가지는 특징을 알아볼 수 있습니다.
Sleep 이라는 METHOD 가 추가되었는데, 실제 두 개의 브라우저를 실행한 뒤, Sleep 을 연달아 각각 호출해보면, 첫 Sleep 의 응답이 10 초 걸릴 동안, 두번째 Sleep 의 호출이 10 + a 초가 걸린다는 것을 알 수 있을 겁니다. a 초는 첫번째 호출 후 두번째 호출을 누를 때까지의 시간을 10초에서 뺀 시간입니다. 같은 EventBus 는 하나의 처리가 끝날 때까지 다음 처리를 기다려야 한다는 조건이 있기 때문입니다. 그리고, 비동기 처리에 익숙하지 않은 분을 위한 또 하나의 팁. 86 라인의 "Send End!!!" 는 첫번째 호출 시 즉시 출력이 됩니다. EventBus 을 호출한 뒤 결과 반환을 기다리지 않고 바로 다음 문장을 실행하는 비동기 프로그래밍의 전형적인 특징입니다. 역시나 node.js 나 javascript 의 callback 등을 많이 다뤄본 분들이라면 너무나 당연한 내용을 것입니다.
문제는 이 파일에서 Sleep 을 실행한 뒤 Login 을 실행해보면, 의도와 다르게 Sleep 을 두 번 실행했을 때와 마찬가지로 앞의 Sleep 이 끝나기 전까지 Login 이 처리되지 않는 것을 알 수 있습니다.
이를 해결하기 위해서는 Verticle 이라는 것을 쪼개서 처리해야 합니다. Test3_2.java 역시 하나의 Verticle 이기 때문에 이를 나누는 것이죠.
Test3_3.java 파일이 기본 파일이고, Test3_4.java, Test3_5.java 파일은 일종의 include 된 파일이라고 생각하셔도 좋습니다. 단지 include 을 하는 것과 달리 별도의 Verticle 로 돌아가기 때문에 Thread 을 쓰는 것과 같은 효과를 볼 수 있습니다.
Test3_3.java 에서 다른 두 파일을 선언하는 것을 Deploy 라고 하고, vertx.deployVerticle() 로 작업을 수행할 수 있습니다.
vertx.deployVerticle("Test3_4.java");
vertx.deployVerticle("Test3_5.java");
그리고 Method 선언과 동일하다고 말씀드린 .consumer() 을 각 파일로 이동시키는 것입니다.
그리고 vertx run Test3_3.java 을 실행한 뒤 Sleep 을 실행한 뒤 바로 Login 을 실행하면 Sleep 와 상관없이 Login 이 바로 실행되는 것을 알 수 있습니다.
그런데, Sleep 을 실행해보면 이상한 메세지들이 출력되는 것을 보실 수 있습니다. 대충 내용을 봐도 뭔가 시간이 오래 걸린다고 불평을 하는 것이라는 걸 알 수 있습니다. Vert.x 의 EventBus 는 하나의 처리에 1 초가 넘어가게 될 경우 메세지가 콘솔로 출력됩니다. 이렇게 장시간의 처리가 필요한 것은 적합하지 않기 때문에 개선을 하라는 의미입니다. 하지만, 또다른 방법으로 오랜 시간이 걸리는 작업은 EventBus 가 아닌 별도로 준비된 Worker Verticle 을 이용해서 처리하면 메세지 출력 없이 처리가 됩니다.
이를 위해 vertx.deployVerticle() 시 두번째 매개변수로 DeploymentOptions 개체를 통해 옵션값을 지정해줘야 합니다.
DeploymentOptions options = new DeploymentOptions().setWorker(true);
vertx.deployVerticle("Test3_5.java", options);
그리고, 하나의 Verticle 은 하나의 Instance 만 생성해서 처리하기 때문에 위의 예제에서는 Sleep 이 하나가 처리되기 전까지 다음 Sleep 은 앞의 Sleep 이 끝날 때까지 기다려야 하는 문제가 발생합니다. node.js 는 이를 여러개를 직접 실행하는 방법으로 약간은 무식(?)하게 해결하곤 하는데, Vert.x 에서는 그냥 Instance 수를 여러개 지정해서 띄워버리면 동시 처리개수를 늘려버릴 수 있습니다. 이는 JVM 을 사용하기 때문에 가질 수 있는 이점이기도 합니다
DeploymentOptions options = new DeploymentOptions().setWorker(true).setInstances(2);
vertx.deployVerticle("Test3_5.java", options);
이번에는 Vert.x 의 핵심이라 볼 수 있는 EventBus 의 기본적인 사용법과 간단한 특성을 잠깐 살펴봤습니다. 저번 글을 적고 난 뒤에 코드가 좀 길어질 것 같고 실수하는게 많아져서 아예 깃헙에 코드를 올리는 쪽으로 생각을 바꿨습니다. 설명이 자세하지 않더라도 소스를 실행해보시면 크게 어렵지는 않으실 겁니다. 질문 사항이 있으시면 언제든지 댓글로 올려주십시오.
오늘은 여기까지...
이 글은 제 개인 블로그(http://zepinos.blogspot.kr)와 okky(http://okky.kr)에만 공개되는 글입니다. 퍼 가는 것은 금해주시고, 링크로 대신해주시기 바랍니다. 당연히 상업적 용도로 이용하시면...저랑 경찰서에서 정모하셔야 합니다. ^^;;;
위에 작성한 코드 등은 실제 컴파일한 것이 아니라 제가 글을 적으면서 키보드 코딩(?...손 코딩의 친구) 한 것이므로, 오류가 있다면 저에게 알려주시면 고맙겠습니다.
DeploymentOptions options = new DeploymentOptions().setWorker(true).setInstances(2);
답글삭제vertx.deployVerticle("Test3_5.java", options);
를 해줬는데 브라우저 2개로 테스트해보면
동시처리가 안되고
처음 브라우저 10초 기다린후 , 다음브라우저 10초 기다립니다..
버전은 3.4.2 사용중 입니다
vert.x 안쓴지 몇 년 지나서...가물가물 한데요...잘 처리가 안된다면 해당 내용은 삭제하던지 하겠습니다.
삭제그런데, 브라우져 두 개를 켜놓고 호출하신 건가요? 인스턴스가 두 개 떠있는지 확인도 해보셨는지 궁금합니다.