메시지 필터링 전체 시스템 구조 #아키텍처 #시퀀스 다이어그램 #시계열 데이터베이스 #NoSQL

조현승
saraminlab
Published in
8 min readNov 17, 2021

들어가며

앞선 장에서 비속어 필터 방법과 모델에 대해서 소개를 했다면 이번 장에서는 사람인 플랫폼에 서비스 되고 있는 메시지 필터링의 시스템 구조와 서버 구성에 대한 내용을 정리해봤습니다.

현재 서비스 중 필터 대상의 메시지가 적게는 한 자릿수의 텍스트 많게는 수천자 이상의 텍스트가 실시간으로 유입됩니다.
메시지 필터링의 시스템 구조는 유입된 데이터에 대해 타임 딜레이 구간이 최대한 발생하지 않고 빠른 응답을 할 수 있는 구조에 초점을 두고 아키텍처링을 진행했습니다.

메시지 필터링 플랫폼

< 메시지 필터링 어플리케이션 간 기능 정의 >

위의 이미지는 플랫폼 전체 기능 구조에 대해 물리적으로 표현해봤습니다.
설계 초반 당시에는 API의 빠른 응답 속도 보장에 대한 확신이 없어 기능적 퍼포먼스에 대한 걱정이 많았지만 배포 용이성과 자동화 설정에 유용한 어플리케이션 형태의 지속적인 동작을 목표로 두었고 < 메시지 필터링 어플리케이션 간 기능 정의 > 그림과 같은 구조가 완성됐습니다.

또 다른 고민은 인터페이스 간의 통신 방법이었습니다.
REST의요청/응답이 완료되어야 프로토콜 통신이 완료되는 구조 대신 Subscribe와 Publish 포인트를 별도로 가져감으로써 분석 시간을 일부 확보할 수 있는 RabbitMQ를 이용한 MQTT 구조를 고려했었습니다.
하지만 분석 모듈의 퍼포먼스는 상당한 수준이었고 많은 조언에 따라 심플한 구조로 구성했습니다.

위 그림에서 목적분류기와 분석모듈의 인터페이스를 나눈 목적은 서로의
역할에 충실하여 속도 저하에 대한 리스크의 최소화에 의미를 두었습니다.

예를 들면, 총무 담당자가 인사 담당자의 업무를 맡게 된다면 인사담당자
만큼의 업무 효율이 나오지 않아 업무 처리속도에 영향을 줄 것 입니다.
하지만 본래의 자신의 업무를 맡게 된다면 효율적인 일처리가 가능할 것 입니다. 이와 같이 역할 분담을 철저히하여 처리 속도에 지장이 가지 않도록 각각의 인터페이스에서의 유리한 기능들을 분배하여 개발을 진행했습니다.

인터페이스 분리의 필요성: 목적분류기

앞 장에서 분석 방법에 대한 언급을 많이 했으니 이번엔 분석 모듈을 제외한 전체 구조에 더 중점을 두고 설명을 하려 합니다.
앞 장 내용이 궁금하시다면 아래 링크를 참고부탁드립니다.

1장 : 언어학 기반의 욕설 확장형 패턴 구축 # 음성학 #음운론 #욕설 필터링
2장 : 메세지 필터링 : 사전 기반에서 딥러닝까지 #욕설필터링 #사전기반 #딥러닝

< 인터페이스 역할 >

위 그림 < 인터페이스 역할 >은 이해를 돕기 위해 목적분류기 API를 간단하게 표현해봤습니다.
그림에서는 목적분류기가 서비스코드/필터유형 식별 후 분석 요청 기능에 대해서만 표현되어 있지만 이것 외에 DB 커넥션, 데이터 적재, 로그 기록, 백오피스와의 자동화 처리 등 분석을 제외한 모든 기능을 담당하여 분석 모듈에 과중한 업무가 스택처럼 쌓이지 않도록 하는 역할을 하고 있습니다.

서버 구성

< 메시지 필터링 서버 구성도 >

현재 서버 구조는 API 서버가 확장될 수 있고 Admin 서버는 1개의 구조로 되어 있지만 해당 서버에 포함된 DB는 n개가 될 수 있다는 가정하에 확정성을 고려하였으며 서버가 증설되더라도 되도록 백오피스에서 모든 제어가 가능하도록 부분 자동화 기능을 구현했습니다.

< 자동 배포 예시 >

API 서버에서의 모든 DB 커넥션은 목적분류기에서 진행되지만 예외적으로 분석 모듈에서도 DB 커넥션을 통해 데이터를 조회하게 됩니다.
필수 설정값(패턴 사전, 서비스코드, 필터유형)의 변경으로 어플리케이션의 메모리 업데이트가 필요한 경우 MongoDB에서 필수 설정값들을 조회하게 됩니다.

API 아키텍처

위 그림에 표시된 저장소중 MongoDB와 InfluxDB의 사용 목적에 대해 짧게나마 설명하겠습니다.

첫번째 InfluxDB..
많은 양의 실시간 시계열성 테이터에 대한 모니터링이 필요하다 생각했고 선택한 저장소가 InfluxDB입니다. Time-series 데이터 베이스 중 가장 많이 사용되고 있는 저장소 입니다.
처음 사용해보는 사용자 입장에서 후기에 대해 짧게 얘기하자면 쿼리 작성법이 sql문과 거의 동일하여 사용 방법을 익히는 것에 대해 크게 어려움이 없었고 NoSQL로 만들어져 스키마 구조에 대한 의존성이 없어 구조 확장에 용이하다고 느꼈습니다.
또 time series query의 처리 속도가 상당히 빠릅니다.
MySQL과의 처리 속도를 테스트한 결과값이 명시되어 있는 사이트가 있는데 아래 URL을 참고 바랍니다.

[1] InfluxDB and MySQL comparison test
- https://www.programmersought.com/article/29054573172/

그리고 InfluxDB를 선택한 가장 결정적인 이유이기도한 데이터 보존 기간을 설정할 수 있다는 점이 매우 매력적입니다.
RetentionPolicy 옵션을 설정할 수 있는데 해당 옵션을 년, 월, 일 기준으로 설정할 수 있습니다.

CREATE RETENTION POLICY {retention명} ON {적용대상 series명} DURATION {설정기간} REPLICATION 1 DEFAULT;

CREATE RETENTION POLICY retention_31d ON forsythia DURATION 31d REPLICATION 1 DEFAULT

보관 주기의 설정만 해준다면 기존처럼 데이터가 일정 기간 쌓여 수동으로 삭제하거나 스크립트와 스케줄러를 이용하여 지워야하는 번거로운 작업을 건너뛸 수 있습니다. 해당 기능은 일회성 모니터링 대상 데이터에 한하여 아주 잘 사용하고 있고 안정적으로 운영중에 있습니다.

두번째 MongoDB..
해당 저장소는 메인 데이터베이스로 사용중입니다.
API 로그, 분석결과, 히스토리성 로그, 각종 설정 값 등 모든 데이터를 적재하는 용도로 사용중입니다. InfluxDB가 알 수 없는 오류로 인해 동작을 하지 않는다면 대체 저장소로 사용이 가능하게끔 설계되어 있습니다.

세번째 ..
저장소의 오류는 조치를 완벽히 했다고 한들 예상치 못한 오류가 닥치기 마련입니다. 이 예상치 못한 오류로 인해 서비스가 중단되는 사태를 방지하기 위한 방법을 고민하다가 2가지의 DB를 이용하여 클러스터링 구성을 구현을 해봤습니다.
말인 즉슨, 오류로 인해 일시적으로 MongoDB에 데이터가 적재가 되지 않는 경우에 데이터 구조의 제약이 없는 NoSQL의 장점을 살려 InfluxDB의 데이터로 대체가 가능하도록 구성했고 반대로도 대체 가능하도록 구현했습니다.

다시 아키텍처 설명으로 돌아와서

기존 아키텍처 구조 위에 시퀀스 다이어그램을 착안하여 위의 그림처럼 표현해보았습니다. INPUT(요청) -> OUPUT(응답) 순으로 flow가 그려지며 그림에 작은 빨간 네모 박스를 이용해 기능들을 나열해봤습니다.

우선 첫과정은 코드에 대한 유효성 검사과정을 통과하면 분석 API와의 통신 과정이 있는데 프로퍼티스에 설정된 분석 모듈의 서버 리스트를 대상으로 통신을 진행하게 됩니다. 이때 대표 분석 모듈에 대한 통신 타임아웃 발생 시 서브 분석모듈에 요청을 순차적으로 진행하여 응답이 있는 분석 모듈의 결과를 받아옵니다.

로그 기록같은 경우에는 역할마다 다르게 적재를 하고 있으며 제공되는 로깅 시스템을 이용하면 속도 측면에서는 물론 좋겠지만 사용에 제약이 많아 자체적으로 핸들링이 가능하도록 기능을 직접 만들어 관리하고 있습니다.

API 캐싱처리 같은 경우 interceptor에서 관리를 해주는데 일반적인 반복된 결과에 따른 캐싱처리를 하고 있습니다. 그러나 아쉽게도.. 설정된 캐싱 유효 시간동안에 반복된 필터링 대상의 메시지의 요청수가 사실상 많지 않기 때문에 활용도는 낮습니다.

Dispatcher-servlet에서 Controller로 엑세스 전 과정을 interceptor로 처리했다면 Dispatcher-servlet로 엑세스 전 과정은 Servlet filter를 이용하여 기능을 구현했습니다. 서블릿 요청에 대한 컨테이너 전처리&후처리 과정은 분석 결과 로그, DB 데이터 적재 기능을 담당하고 있습니다. Servlet filter에서 진행되는 과정에 대해서는 오류가 발생하더라도 응답값에는 전혀 지장이 가지 않도록 처리해놨으며 진행 과정에서도 서로의 오류에 영향을 미치지 않도록 개발을 진행했습니다.

나가며

전반적인 구성도는 전혀 어렵지 않습니다.
인터페이스간 통신은 REST로 구성되어 있으며 was를 이용하여 어플리케이션을 운용하고 NoSQL을 통해 데이터를 저장합니다.
지금은 이렇게 심플한 구조이지만 설계 초기 단계에는 확장 용이성, 속도, 관리 편의성 등에 대한 퍼포먼스 수준의 고민과 걱정이 많았었습니다.
그러나 소개한 시스템 구조는 진행 과정, 결과물이 기대 이상 수준이었고 무탈하게 서비스중인 메시지 필터링 플랫폼에 감사하고 있습니다.
부족한 부분에 대한 리팩토링을 기약하며…

좋은 미들웨어와 많은 기술들을 사용하는 것이 능사가 아닌 인터페이스 간의 장점을 잘 살려 통합시스템을 구축한다면 최선책이 될 수 있다는 것을 다시 한번 느끼며 마무리하겠습니다.

감사합니다.

--

--