최근 Spring에서 반응형 코딩이 가능하다는 이야기를 들었고 팀원끼리 학습을 해보기로 하여 rx.js를 통해 컨셉을 이해해보고 비동기, 동기~ 블로킹이니 단어나 개념들부터 차근차근 잡고 학습을 진행해보았습니다. 여전히 개념은 잡히지 않고 단순 html파싱조차도 헤매고 있는 상황이지만 기록과 공유를 위해 일단 학습한 내용부분까지 적어보고자 합니다.
일단 가장 헷갈렸던 부분으로 Blocking, Non-Blocking / Synchronous, Asynchronous 용어였습니다.
2:2 매트릭스 형태의 그림을 가장 많이 발견 할 수 있는데, 간단하게 정리해보겠습니다.
용어정리
Blocking - Synchronous
블로킹이면서 싱크로나이즈인 경우는 일반적인 동기식 프로그램 scanf() 메소드, file.read(), jdbc연결의 select 등등이 있을것입니다.
Non-Blocking - Asynchronous
논블로킹방식이면서 비동기 방식으로 콜백을 던져주고 완료되면 콜백에게 이벤트가 끝났다고 알리는 방식으로 입력과 출력간에 기다리는 상황이 없기때문에, 자원을 효율적으로 사용할 수 있습니다. 사용 예로는 Node.js, ajax, 이번에 다뤄볼 WebFlux가 있습니다.
Non-Blocking - Synchronous
논블로킹이면서 동기식으로 폴링(Polling)방식으로 불리우며, 특정 시간마다 데이터가 준비되었는지 상태를 확인하는 방식입니다. 계속 확인을 하다가 완료가 되면 종료됩니다.
Blocking - Asynchronous
굳이 다루지 않겠습니다...
Spring MVC와 Spring Webflux의 차이
Spring MVC 패턴은 HTTP요청이 들어오면 큐를 통해 들어오는데 Thread pool이 수용할 수 있는 수의 요청까지만 동시적으로 작업이 처리되고, 그 이상의 수가 들어오면 큐에서 대기를 합니다. 즉 한개의 요청은 한개의 Thread를 사용합니다.
Thread를 생성하는 비용이 크기때문에 미리 생성해뒀다가 재사용하면서 효율적으로 사용하며, 서버의 성능에 맞게 제한이 됩니다. (톰캣은 기본 Thread가 200개) 여기서 문제는 이 Thread pool의 개수가 넘어간 경우인데, 큐에 쌓이기 시작하면 처리속도가 급격하게 느려지면서 많은 지연시간이 발생됩니다.
이러한 문제를 타파하고자 만들어진 컨셉은 Webflux이며 Event만 담당하는 적은 수의 Thread로 비동기식 처리를 하여 효율적인 프로그램을 생성합니다. 하지만 제한점이 역시 존재하는데 중간에 블로킹이 존재하면 해당 방식은 안하느니만 못하게 됩니다. core가 2개짜리인 cpu에서는 Thread가 2개가 생성되는데, 블로킹이 걸리는 작업이 존재한다면 100개의 요청이 들어온 경우 98개의 요청은 마냥 기다리고 있어야 하기 때문입니다. 일반 MVC패턴이라면 200개가 처리하므로 더 효율적일거 같네요...
아직 논블로킹 방식의 지원하는 라이브러리가 많지는 않은 상황이고 NoSql인 몽고DB나, redis가 지원중이고 RDB는 아직 사용은 안해봤지만 MS-Sql, Postgresql정도가 지원한다고 합니다.
<html>
<link rel="stylesheet" href="./src/style.css" />
<script src="main_bundle.js"></script>
<h1>RxJS GitHub User Map</h1>
<p>Please enter the name of the user you want to search for...</p>
<div class="autocomplete">
<input
type="text"
id="searchId"
placeholder="Please enter your ID..."
/>
<ul id="suggestLayer"></ul>
<div id="loading">
<i class="fas fa-spinner fa-pulse"></i>
</div>
</div>
</html>
처음에 웹 소켓을 시작하면서 서버단을 구성할 때, 구현체부분인 SockHandler부분을 구성할때 TextWebSocketHandler을 상속받고 메시지 타입에 따라 handleBinaryMessage 또는 handleTextMessage가 실행된다고 했는데, 지금까지 JSON형태의 String메시지만 전송하다보니 바이너리메시지는 다룰일이 없었습니다. 이번 장에서는 바이너리메시지를 사용하여 파일을 받고 서버에 저장도 하고 채팅방에 전송된 이미지를 표출하는부분까지 구성해보겠습니다.
Server단
@Override
public void handleBinaryMessage(WebSocketSession session, BinaryMessage message) {
}
SocketHandler에 추가할 메소드입니다. BinaryMessage의 데이터가 들어오면 해당 메소드가 실행됩니다.
2. 세션을 저장할때, 현재 접근한 방의 정보가 있는지 체크하고 존재하지 않으면 방의 번호를 입력 후 세션들을 담아주는 로직으로 변경되었습니다.
3. 마찬가지로 종료시에도 list컬랙션을 순회하면서 해당 키값의 세션들을 삭제하도록 변경되었습니다.
4. 메시지를 발송하는 handleTextMessage메소드에서는 현재의 방번호를 가져오고 방정보+세션정보를 관리하는 rls리스트 컬랙션에서 데이터를 조회한 후에 해당 Hashmap을 임시 맵에 파싱하여 roomNumber의 키값을 제외한 모든 세션키값들을 웹소켓을 통해 메시지를 보내줍니다.
말로 정리가 어려운데, 결국 4번에서 하는 처리로 인해 방구분을 하고 해당 방에 존재하는 session값들에게만 메시지를 발송하여 구분이 됩니다.
현재 프로젝트는 DB에 데이터를 담아놓거나, 파일등에 저장하는게 아니므로 방의 정보를 담아둘 List<Room>컬랙션을 생성하였고, 메소드에 따라 방을 생성하거나 방의 정보를 가져오도록 처리하였습니다.
나중에 찾아볼 room.jsp에서는 접속버튼에 따라 채팅방을 이동시킬 예정인데, moveChating()메소드에서는 전달받은 파라미터값으로 방이 생성되었는지 필터함수로 체크후 존재하는 방이면 해당방으로 이동시켜주고, 강제로 url을 바꾼 값이면 이동하지 못하도록 처리하였습니다.