처리량이 불규칙한 출금 전송 작업 앞에 ActiveMQ 를 뒀다. 요청을 큐에 넣고 WithdrawWorker 가 자기 속도로 처리하니 몰리는 시간에도 요청이 죽지 않고 쌓이기만 했다.
Table of contents
Open Table of contents
증상 — 길이 절대값이 만든 오탐
처음에는 QueueSize 가 일정 수를 넘으면 알리게 했다. 장 마감 정산 시간마다 그 선을 넘으니 알림이 계속 왔다.
그런데 그렇게 쌓였다가 나중에 처리되는 것이 이 구조의 목적이다. 완충하려고 넣은 장치에 완충이 일어났다고 알리는 조건을 걸어 둔 셈이었다.
판단 기준 — 유입 속도와 처리 속도의 관계
두 가지 방법이 있었다. 임계값을 더 높여 알림 수만 줄이는 것과, 조건을 길이가 일정 시간 계속 증가하는 경우로 바꾸는 것이다. 뒤쪽을 골랐다. 이유는 쌓이는 것은 괜찮고 줄지 않는 것이 문제이기 때문이다. 대신 짧은 폭주는 알림 없이 지나가는 것을 감수했다.
더 정확하게는 EnqueueCount 와 DequeueCount 를 각각 Micrometer 지표로 남기고 비율을 봤다. 유입이 처리보다 큰 상태가 잠깐이면 완충이고 오래 지속되면 용량 부족이므로 절대값이 아니라 관계를 봐야 갈린다.
조치 — 예상 대기 시간이 판단을 쉽게 한다
운영자에게 더 쓸모 있는 지표는 QueueSize 를 초당 처리 수로 나눈 값이었다. 몇 개가 쌓였다는 말보다 몇 분을 기다린다는 말이 판단하기 쉽다.
회원이 실제로 겪는 것도 QueueSize 개수가 아니라 시간이다. 그래서 출금 화면에도 현재 처리 지연을 시간으로 보여 주게 했다.
주의 — 워커를 늘리기 전에 보는 것
몰릴 때 워커를 늘리면 될 것 같지만 병목이 워커가 아닐 수 있다. 워커가 지갑 노드 RPC 를 부르면 노드가 병목이고 워커를 늘리면 노드 연결이 늘어 더 느려진다.
그래서 늘리기 전에 워커 스레드가 포화 상태인지 대기 상태인지를 jstack 으로 봤다. 대기 중이면 뒤쪽이 병목이므로 늘리는 것이 도움이 아니라 해가 된다.
대상별 순서와 실패 큐
여러 워커가 나눠 처리하면 전체 순서가 지켜지지 않는데 대부분은 상관없었다. 다만 같은 회원에 대한 작업은 출금과 취소가 뒤바뀌면 안 되므로 JMSXGroupID 를 회원 단위로 붙여 같은 회원이 같은 소비자로 가게 했다.
전체 순서를 포기하고 회원별 순서만 지키면 병렬 처리가 성립한다. 처리에 실패한 메시지는 몇 번 재시도한 뒤 ActiveMQ.DLQ 로 보냈는데 이 큐는 비어 있는 것이 정상이므로 앞의 큐와 반대로 하나라도 있으면 알리게 했다.
정리
- 완충 목적의 큐는 쌓이는 것이 정상이다
QueueSize절대값에 알림을 걸면 목적대로 동작할 때마다 울린다- 줄지 않는 것이 문제이므로 추세를 본다
EnqueueCount와DequeueCount의 관계로 완충과 용량 부족을 가른다- 길이를 처리 속도로 나눈 대기 시간이 판단하기 쉽다
WithdrawWorker를 늘리기 전에 병목이 워커인지jstack으로 확인한다- 대기 중이면 늘리는 것이 해가 된다
ActiveMQ.DLQ는 비어 있는 것이 정상이라 반대 조건으로 알린다