1. 연동 이유 및 사용했던 부분에 대한 내용 정리
회사 내부의 래거시 시스템인 근태 관리 시스템 고도화 개발중 사용자의 근무 시간 및 근무 정책에 따라서 동적으로 근무 정보를 표현하는 부분이 존재했다.
관리자가 정책 변경 또는 사용자가 근무 시간을 변경 (초과 근무, 휴가, 출장등) 을 신청 하고 결재하게 되면 사용자의 스케줄이 변하게 되고 해당 사용자 별로 각각의 유동적이 스케줄을 가지게 됩니다.
근무 정보를 구할때, 사용자 정보, 신청 정보, 이미 정산이된 근무량 등등(...😱) 너무 많은 데이터를 조합해서 근무 정보를 구해야한다.
고객사의 기준으로 년 단위 조회 또는 구축이 된지 오래된 고객사의 경우는 조회가 너무 느려지는 이슈가 발생을 하면서 방법을 찾기 시작했다. 쿼리 튜닝, DB 인덱스 추가 등등 여러 방법을 시도하고 속도 개선을 했지만 다이나믹 하게 조회 속도가 늘어나지 않았다.
이때 생각했던 방법중에 하나인 messageMQ를 사용하는 방식을 사용했다. 어떻게 도입을 해서 속도 개선을 했는지 한번 내용을 정리 했다.
나는 mssql CDC 기능과 함께 rabbitMQ를 사용했다. mssql CDC 에 대해서도 정리 하겠지만 여기서는 짧게만 설명 하도록 한다. CDC는 변경된 데이터의 상세 정보를 캡처하고 이 정보를 사용하여 다른 시스템에서 변경된 데이터를 추적할 수 있도록 돕는 기능이다.
근태 관리 시스템 기준으로 페이지의 호출이 이뤄질때 마다 스케줄을 만들어서 보여주는것이 아닌 미리 스케줄을 만들어서 테이블에 저장 하고 해당 테이블만 호출해서 보여준다면 속도 개선이 될거라고 판단을 하고 진행을 했다.
근무 도중에 스케줄이 변경이 됬다면 변경된 테이블을 캐치해서 rabbitMQ에 메시지를 보내고 스케줄 테이블을 변경한다.
해당 방법을 통해서 페이지에서 호출시 기존 1년치를 조회 했을때 7~8분 정도의 시간 소요가 있었다면 개선 작업을 통해서 스케줄 테이블만 조회시, 1분 내의 속도 개선의 효과를 볼수 있었다. 물론 구조를 변경한다거나 DB 개선 등 다른 방법 또한 존재 한다.
시간 대비 효율적인 측면을 고려 했을때 최선의 방법이라고 판단을 했고 진행 하고 마무리 하게 되었다.
2. 개요
먼저, MQ (Message Queue) 란 메시지 기반의 미들웨어로 메시지를 이용하여 여러 어플리케이션, 시스템, 서비스들을 연결해주는 솔루션이다. MOM(Message Oriented Middleware) 를 구현한 솔루션으로 비동기 메시지를 사용하는 서비스들 사이에서 데이터를 교환해주는 역할을 한다. MQ를 오픈소스로하여 표준 프로토콜이 AMQP이고 그 중 하나가 RabbitMQ 이다.
⚡RabbitMQ의 구성 요소
RabbitMQ는 크게 다음과 같은 구성 요소로 구성된다.
- Producer: 메시지를 생성하는 어플리케이션
- Consumer: 메시지를 소비하는 어플리케이션
- Exchange: 메시지를 받아서 큐(Queue)로 라우팅하는 역할
- Queue: 메시지를 저장하는 공간
- Binding: Exchange와 Queue 사이의 라우팅 규칙을 정의
⚡RabbitMQ의 동작 방식
RabbitMQ에서 메시지 전달은 다음과 같은 순서로 이루어진다.
- Producer가 메시지를 생성하고, Exchange로 전달한다.
- Exchange는 메시지를 받아서 해당 메시지가 전달될 큐를 찾는다.
- 큐는 해당 메시지를 저장한다.
- Consumer는 큐에서 메시지를 가져와서 처리한다.
⚡RabbitMQ의 장단점
RabbitMQ의 장점
- 다양한 언어를 지원하는 클라이언트 라이브러리 제공
- AMQP 프로토콜을 사용하여 안정성과 확장성이 뛰어남
- 메시지 라우팅, 메시지 딜리버리 보장, 메시지 우선순위 처리 등 다양한 기능 제공
- 복잡한 분산 시스템에서 어플리케이션 간의 결합도를 낮출 수 있음
RabbitMQ의 단점
- 대량의 메시지를 처리하는데 있어서 다른 메시지 지향 미들웨어에 비해 성능이 떨어지는 경우가 있음
- RabbitMQ 서버의 장애 발생 시 메시지 전달이 중단될 수 있음
3. 설치
RabbitMQ를 설치하기 위해서는 공식 사이트에서 다운로드 받을 수 있다. 다운로드 후, 설치 파일을 실행하여 RabbitMQ를 설치한다. 리녹스, Window, Mac, Docker 등 여러 환경에서 설치가 가능한 가이드가 존재한다.
설치의 경우 별로 가이드할 내용이 없다. 공식 사이트 및 공식 문서 기준으로 설명이 잘 되어있다.
설치가 완료된 경우 웹 관리 콘솔로 접근이 가능하다.
- http://localhost:15672/
- (기본 계정 guest / guest)
관리자 계정을 등록 하는 방법
- 기본 계정으로 로그인.
- Admin > Users 메뉴로 이동 및 계정 등록.
- 참고 사항으로 모든 권한을 주려면 Tags 선택시 Admin을 선택해야 한다.
여기까지 완료가 됬다면 rabbitMQ 설치가 완료 되었으며, 사용하기까지 절반은 온것이다. 이제 중요한 부분이다.
위에서 짧게 적어 놓은 rabbitMQ의 구성 요소에 대해서 공부를 해야될 필요성이 존재한다.
해당 구성 요소중 내가 rabbitMQ에서 설정한 부분에 대해서 내용을 정리 하도록 하겠다.
⚡Exchange 부분
- 사용할 명칭과 타입을 설정한뒤에 Exchanges를 생성한다.
⚡Queues 부분
- Exchanges와 동일하게 사용할 타입과 명칭, Arguements를 넣고 생성한다.
⚡Exchanges 바인딩
- Exchanges에서 Queues에서 생성한 내용을 바인딩 해주면 구축은 끝이난다.
⚡프로젝트 설명
총 프로젝트를 두개를 생성하고 관리 하고 있다.
- 변경된 스케줄 테이블 내용을 체크해서 메시지를 송수신 하는 프로젝트
- 테이블이 변경되면 받아서 스케줄을 만들고 관리 하는 프로젝트
위와 같이 두개의 프로젝트를 구성했으며, 간략하게 프로젝트를 정리하도록 하겠다.
- java 17
- Spring boot 2.6.6
- gradle 7.4
🤝설정
build.gralde(dependencies)
// 1번 프로젝트
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-amqp'
implementation "org.springframework.boot:spring-boot-starter-log4j2"
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'com.microsoft.sqlserver:mssql-jdbc:10.2.0.jre17'
implementation 'io.debezium:debezium-embedded:1.8.1.Final'
implementation 'io.debezium:debezium-connector-sqlserver:1.8.1.Final'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
// 2번 프로젝트
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation "org.springframework.boot:spring-boot-starter-log4j2"
implementation 'org.springframework.amqp:spring-rabbit:2.4.2'
implementation 'io.projectreactor.rabbitmq:reactor-rabbitmq:1.5.4'
implementation 'io.r2dbc:r2dbc-mssql:0.8.8.RELEASE'
implementation 'org.springframework.boot:spring-boot-starter-data-r2dbc'
implementation 'org.apache.commons:commons-text:1.9'
compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
위의 내용을 보면 아마 rabbitMQ 구축하기를 찾아봤다면 대략적인 라이브러리는 한번씩 봤다고 생각을 한다.
개발 했던 내용중 체크하고 넘어가야 되는 라이브러리에 대해서 몇가지 정리를 하도록 하겠다.
org.springframework.boot:spring-boot-starter-webflux
io.debezium:debezium-embedded:1.8.1.Final
io.debezium:debezium-connector-sqlserver:1.8.1.Final
개념적인 설명은 따로 하지않고 왜 사용 했는지만 적고 넘어가도록 하겠다.
Webflux 프레임 워크를 사용한 이유는 비동기로 동작 하기때문에 스케줄이 사용자 100명을 기준으로 1년치 스케줄을 만든다고 했을때 비동기로 서비스가 동작 하기 때문에 속도적인 측면에서 유리하다고 판단했고 사용하게 되었다.
💀 non-blicking 코드에 익숙하지 않고 이해하지 않고 코딩을 한다면 알수 없는 오류도 발생하기 쉽고, 디버깅이 어렵기 떄문에 비추천 한다.
debezium 라이브러리는 cdc사용 하여 데이터 베이스의 변경 사항을 수집해준다. 개념적인 부분과 동작 방식에 대해서는 나중에 한번 정리 하도록 하겠다.
🤝개발 내용 정리
위의 라이브러리들을 추가한 뒤 기본적으로 config File을 생성해서 세부 설정 작업 및 실시간으로 변경된 테이블을 체크해서 rabbitMQ에 전달하는 소스 개발을 진행 했다.
- rabbitMQ Config 관련 설정
- Debezium Config 관련 설정
- cdc check 및 메시지 전달 로직 구현.
- queue에 담긴 메시지를 consume 해서 스케줄을 만드는 로직 구현.
rabbitMQ Config 설정에서는 exchange(), queue(), binding() 함수를 구현 했으며 기본적인 설정 작업만 해놓았다.
Debezium Config 설정에서는 db 정보, cdc 커넥터를 생성해서 캡처할 테이블을 선언하고, 세부 설정 작업을 진행했다.
개발된 소스에서는 변경된 테이블을 기준으로 삭제된 데이터는 제외하고, 테이블에 세부 정보를 실시간으로 메시지로 전달하는 로직을 구현하고, Webflux 프레임 워크로 구축한 프로젝트에서는 queue에 들어온 테이블 정보를 기반으로 consume 을 통해 메시지를 받아서 스케줄을 만드는 로직을 구현 했다. 개발된 소스에는 내부 정보가 존재해서 공개를 할수는 없다. 테스트 소스를 따로 작성해서 gitHub에 공유 하도록 하겠다.
✍마무리 내용 정리
고객사에 적용을 하고 실제 운영중에 있으면 추가적으로 문제가 발생했던 내용에 대해서 정리를 하고 마무리를 하려고 한다.
서버를 내렸다 올리는 경우 rabbitMQ 서버, 개발된 프로젝트 서비스가 올라갈때 서버보다 프로젝트 서비스가 먼저 올라가는 경우 서비스가 서버의 상태를 체크하지 않고 그대로 서비스가 죽어버리는 이슈가 발생했다. 이부분의 경우 Config 설정 하는 부분에서 연결 시도에 대한 실패가 발생했을 경우 일정 시간마다 재 시도를 하도록 추가적인 예외처리 작업을 진행 하였다.또한 Webflux를 사용했을때, 퍼포먼스 측면에서는 엄청난 성능을 보여주었다. 하지만 근무 도중에 스케줄 데이터가 대량으 변경되는 경우 cpu를 전부 잡아 먹어서 다른 서비스들이 동작하지 않는 이슈가 발생 했었다. rabbitMQ 에서 메시지를 consume 할때 라이브러리 내부에서 지원해주는 함수도 찾아보고 한번에 처리하는량, 딜레이, timeOut 등 여러 옵션을 넣고 테스트 했지만 최적화를 찾을수가 없었다. (cpu를 개선하면 속도가 저하되고... 개발 서버에서는 발생하지도 않고... 😭😭) 결론적으로 고객사의 서버에서는 속도는 유지하고, 작업 관리자 > 세부 정보 > 우선 순위 설정을 통해서 다른 서비스가 동작하고 쉴때를 해당 서비스가 진행이 되도록 진행을 했다. 해당 이슈 말고도 다른 이슈도 많았지만 특이한 케이스 이슈라고 판단을 해서 내용을 적어놓게 되었다.
'Spring' 카테고리의 다른 글
[Spring Boot] 콘솔에 출력된 배너 변경하기 (0) | 2023.02.12 |
---|---|
[JPA] QueryDSL에서 서브 쿼리 사용 정리 (0) | 2023.01.29 |
Spring Boot SPQR 개념 및 사용 방법 정리 (0) | 2022.09.15 |
Spring Boot JPA 사용 방법 정리 (0) | 2022.08.04 |
Spring Data JPA에 대한 개념 및 사용 방법 정리 (0) | 2022.06.02 |