01중계 프록시 세션이 주기적으로 끊김
녹취 업로드 중 중계망 프록시에서 idle timeout이 처리되지 않아 세션이 주기적으로 끊겼습니다.
유휴 연결에 대한 timeout 처리가 없어, 잠시 데이터가 없으면 세션이 종료됐습니다.
태블릿 때 만든 Proxy를 수정해 timeout을 처리하고, ping-pong으로 유휴 세션을 유지했습니다.
업로드 세션이 주기적으로 끊기지 않게 됐습니다.
경력기술서 · 2024.01 – 2024.07 · 퓨렌스
태블릿 녹취는 마이크가 하나라 화자를 가를 수 없어, 내부망 GPU에서 MP3를 RX·TX PCM으로 나눈 뒤 기존 PC 녹취의 STT·TA로 이어집니다. 녹취 엔진의 화자분리 라우팅, 분리 음원 STT 전송, 라이선스용 Kafka 토픽을 개발했습니다.
개발 관점
외부망 태블릿이 내부 녹취 엔진에 바로 붙지 않도록 중계 Proxy를 두고, 화자분리는 내부 GPU 서버 2대로 라우팅하게 개발했습니다. 웹뷰의 라이선스·STT·TA는 녹취 엔진 WebSocket에서 내려주게 했습니다.
기존 Proxy 개선 · ping-pong
엔진은 직접 구현 · 모델은 연동
직접 구현
개발 관점
마이크가 하나라 채널 분리는 엔진에서 하지 않고, 완본 저장 후 별도 스레드로 GPU 서버에 보내게 개발했습니다. 돌아온 PCM부터는 기존 PC 녹취와 같이 TCP로 STT에 넘기고, 결과는 Kafka로 받게 했습니다.
태블릿 프로젝트에서 만든 중계망 Proxy를 이 업로드 구간에 맞게 수정했습니다. 녹취 파일은 Netty TCP로 주고받고, 유휴 세션은 ping-pong으로 유지합니다.
화자분리 서버는 2대이고, 앞단은 Round Robin입니다. 서버 안에서 WebSocket으로 Python 모델을 호출해 MP3를 RX·TX PCM으로 나눕니다. 모델은 서버에 연동했고, 파일 하나의 연결이 그 서버에 머물기 때문에 Round Robin으로 나눕니다.
녹취 엔진은 STT와 TCP로 start · end · connection을 주고받은 뒤 음성 패킷을 보냅니다. STT 결과는 Kafka로 돌아와, 이 구간부터 기존 PC 녹취 코드를 그대로 씁니다.
웹뷰는 중계망 JEUS를 거쳐 녹취 엔진 WebSocket 서버에 붙습니다. 라이선스 발급·검증과 STT·TA 결과를 이 연결로 받습니다.
개발 관점
엔진 1·2가 서로 다른 서버라 메모리를 합쳐도 라이선스 절대 개수가 되지 않습니다. 발급·반납을 Kafka 토픽으로 모으고, 같은 컨슈머 그룹에서 클러스터 전체 개수를 세도록 개발했습니다.
평시 · 파티션을 하나씩
한 대가 내려간 뒤
서버마다 세면 그 서버 개수만 나옵니다. 그룹 하나에서 메시지를 나눠 받으면 클러스터 전체의 절대 개수가 되고, 같은 건은 한 엔진만 처리합니다.
건수와 메시지 크기가 작아 파티션은 엔진 대수와 같게 2입니다. 엔진마다 파티션 하나를 맡아 두 대가 함께 처리합니다.
브로커가 1·2로 이중화되어 있어 복제도 2입니다. 파티션이 양쪽 브로커에 남아, 브로커 한 대가 내려가도 토픽이 유지됩니다.
녹취 업로드 중 중계망 프록시에서 idle timeout이 처리되지 않아 세션이 주기적으로 끊겼습니다.
유휴 연결에 대한 timeout 처리가 없어, 잠시 데이터가 없으면 세션이 종료됐습니다.
태블릿 때 만든 Proxy를 수정해 timeout을 처리하고, ping-pong으로 유휴 세션을 유지했습니다.
업로드 세션이 주기적으로 끊기지 않게 됐습니다.
멀티스레드로 TCP·WebSocket 세션을 함께 관리하면서, 객체 참조와 정리되지 않은 세션 때문에 OOM이 자주 발생했습니다.
연결이 끝난 세션과 버퍼 참조가 스레드마다 남아 힙이 계속 늘었습니다. Netty 버퍼는 release하지 않으면 회수되지 않습니다.
세션 보관소를 싱글톤으로 두고, 채널이 닫힐 때 세션을 빼도록 구조를 다시 짰습니다. ChannelGroup이 닫힌 채널을 스스로 제거하고, 받은 버퍼는 release로 참조를 놓습니다. JVM 힙도 함께 늘렸습니다.
힙을 늘린 상태에서, 참조가 쌓이던 기존 OOM은 다시 발생하지 않았습니다.
태블릿 때 만든 Proxy에 idle timeout 처리와 ping-pong을 넣었습니다.
녹취 업로드 중 데이터가 잠시 없어도 세션이 주기적으로 끊기지 않습니다.
세션 보관소를 싱글톤으로 두고, 닫힌 채널은 ChannelGroup이 빼고 버퍼는 release하게 했습니다. JVM 힙도 함께 늘렸습니다.
끝난 TCP·WebSocket 세션이 힙에 쌓이던 기존 OOM은 다시 발생하지 않았습니다.
엔진 1·2의 발급·반납을 같은 컨슈머 그룹의 Kafka 토픽으로 모았습니다.
서버가 달라도 클러스터 전체의 라이선스 절대 개수를 한곳에서 셉니다.