티스토리 뷰

일상

2025 게으른 개발자 컨퍼런스

애쿠 2025. 4. 20. 16:19

2024.01.29 - [일상] - [컨퍼런스] 게으른 개발자 컨퍼런스 후기

 

작년에 꽤 재밌게 봤던 컨퍼런스라 올해도 지원해서 왔다. 올해 첫 컨퍼런스 참석인데, 시작으로는 나쁘지 않은 듯하다.

 

 

작년에 비해 세션이 많이 간소화 됐다. 작년에는 듣고 싶은 세션에따라 장소를 계속 옮겨야했고 참여 인원이 너무너무 많았어서 굉장히 혼란스러웠다. 올해는 5천원이지만 유료세션으로 변경되었고, 장소도 한곳에서 진행되어 듣는 입장에서 편하게 들을 수 있었다.

 

발표 자료 : https://github.com/lazyconf-dev/2025-lazydevconf

1. 24시간 끊기지 않는 잔고 서비스 개발

작년에 재밌게 봤던 거래소에서 신규 서비스를 개발하면서 발생했던 문제에 대해 발표하셨던 분이 다시한번 세션을 준비해주셨다. 코빗에서 근무 중이심.

 

잔고 서비스란? 거래소 전체에 대한 자산 변동을 관리함

 

요청 자체가 대량 실시간으로 계속 들어오기 때문에 데이터가 틀어지게 된다면 엄청난 데이터가 오염되기 시작, 자산 종류도 여러가지.... 틀어지기 시작부터는 데이터 추적 및 원인 파악이 힘들어짐

 

단일 인스턴스 구조로 확장할 수 없어 뜯어 엎기로 함

250 TPS를 최대한 늘려보자.

kafka를 이용한 비동기 메시지처리 및 병렬처리

record lock 제거 및 배치 처리 적용

 

Spring starter kafka와 JPA 를 활용해서 개발 > 꽤 의외였음 속도와 정합성을 위해 Spring 서버를 사용하는 경우가 많지 않다고 알고 있어서...

 

Protobuf -> JSON보다 작은 포맷이라 직렬화/역질렬화 속도가 빠름. Kafka 내에서 일관된 메시지 포맷 유지 가능

대량의 데이터를 주고 받을 때 확연한 차이가 발생, 특정 플랫폼에 종속x, 일관된 메시지 포맷 유지 가능하다는 장점

코드 자동 생성 지원?

 

왜 메시지 브로커를 사용했을까?

서비스간 결합도를 낮추고 유연성 확보 - HTTP 대신 이벤트 기반 메시지 전송

key에 사용자 id를 요청 -> 컨슈머 그룹 내 컨슈머는 고유사용자만 처리

 

실패 전략

입력 메시지 타입 엄격히 검사 -> 잘못된 데이터 입력 방지

처리 실패 시 제한적 재시도 후 즉시 중단

잔고 반영 순서 중요 -> DLQ 사용 x

 

kafka 컨슈머 성능 최적화 설정(kafka의 세부 config를 조정함)

자동 커밋 off -> 수동 ack으로 정확성보장

 

응답 메시지 유실이 너무나 큰 이슈임 -> outbox 패턴을 통해 해결

카프카에서 응답이 나가면 outbox 테이블에서 로우를 삭제하는 식으로 응답 메시지가 유실되지 않도록 처리?

 

비관적 record lock 제거, 배치 처리 최적화

특정 서비스에서만 요청을 받도록하면 비관적락이 필요없도록 만들 수 있음

jdbc 옵션을 지정해 배치 최적화.

메모리에서 한 트랜잭션에 데이터를 독립적으로 처리하려고 함. 메모리로 처리되었던 모든 결과를 아웃박스에 저장

트랜잭션 수를 줄이기 위해 하나의 요청을 배치처리 한 것

 

마이그레이션은 미리 준비 -> 스케줄러로 데이터를 꾸준히 옮겨옴

 

배포 고려 사항 Recreate 전략 - 기존 서비스 전체종료 후 새버전을 동시에 배포

롤링 업데이트는 무중단 배포가 가능하지만 버전 혼재 가능성이 존재함. 서비스 특성상 명확한 버전 고나리가 필요했음

2-5분 정도의 셧다운이 있어서 거래소에겐 부적합한 배포방식이지만 데이터 정합성이 중요해서 잠시 셧다운 후 배포

 

변경 후 250TPS -> 655TPS의 성능향상. 요청량이 늘어나면 유연하게 대응 가능하게 변경

기존 구조는 데드락이 생기는 경우가 많아서 수동으로 서비스를 재시작하는 경우가 잦았으나 

변경하고나서는  변경 후 지연이나 중단이 된적이 없음!

 

Q&A

왜 Kafka? 사내엔 Kafka에 대한 이해도가 이미 있어서의 이유가 가장 큰 듯

왜 blue/green을 안했을까? 나도 이게 궁금하긴 했음. 카프카 재연결 하는데 문제가 있었던 것 같음

왜 protobuf? 에이브로가 무슨 이슈가 있었음... 기억이 안나신다함..

protobuf에는 금융 데이터를 주기엔 불편한 점이 많은데 어떻게 해결했나...? 부동소수에 대한 문제가 있어서 나눠서 잘 처리해야함..

엄청나게 큰 변화인데 테스트는 어떻게 했나? 운영과정과 동일하게 테스트를 하고 넘어감.. 엔진을 두 개로 유지하면서 거래쌍들을 차근차근 넘김


 

개인적으로 이런 사례 기반 세션을 좋아한다. 어떤 문제가 있었고 해결하기 위해 어떤 기술을 선택했고....

 

가장 좋은건 여러가지 대안들을 비교하면서 의사선택 과정까지 있었으면 좋았겠지만 이렇게하면 시스템 규모가 커서 발표 시간이 더 필요했을 것 같다.

 

Kafka 책에서 본 세부옵션들을 직접 조정한 것과 에이브로 외의 데이터 컨버터를 선택한 케이스가 신기했다.

그리고 실패 전략, 트랜잭션 관리를 위한 인메모리 배치전략이 굉장히 인상 깊었다. 

 

그리고 Q&A 마지막 질문을 하신 분의 질문이 굉장히 날카로웠는데, 운영 서버의 적용 과정과 protobuf의 문제 등을 짚어준게 신기했다. 그리고 서버가 일시적으로 셧다운 되는 recreate 배포 전략은 나도 거래소에서 함부로 써도 되는건가 싶었다.. 카프카에서 이벤트처리가 graceful하게 처리되지 않아서가 아닐까? 란 생각이 들었음

 

2. Go 서버 메모리 누수 문제 개선기

최근 신규 서버 개발에서 OOM이 발생한 케이스가 있었다. 이것도 사례 기반 발표라 기대가 됐음. 언어가 Go라 나랑은 조금 다른 분야긴했다.

 

실시간 오디오 스트리밍이 필요한 서비스. WebRTC기반으로 websocket을 이용해 시그널링 서버와 미디어 서버를 연결함

 

시그널링 서버가 피어가 webRTC 연결이 될 준비가 됐다는 신호를 주고 받아 p2p 연결을 도와줌 -> Go 언어로 개발

 

고루틴과 채널에 대한 내용이 다뤄질 것 같음

kafka 느낌의 프로듀서와 컨슈머 고루틴이 있고 채널을 통해 데이터를 주고 받음. 채널에 데이터가 저장될 수 있는데 이런건 버퍼드 채널임. 이를 통해 동시성 제어를 편하게 할 수 있음

 

시그널링 서버가 메모리가 80% 이상 치솟는 알림이 옴, 특정 인스턴스만 메모리가 스파이크가 나는 현상이 발생

 

채널 무한 블로킹이 발생한게 문제였음

- 도식화, pprof, 테스트 코드로 재현 시도를 통해 디버깅을 시도함

 

GC가 작동하는데 왜 메모리가 발생하는거지? 특정조건이 발생하면 몇개의 인스턴스만 튀는데 그 조건을 찾아가는 과정

 

Room, Session, 메시지데이터를 삭제하는 역할을 하는 고루틴이 무한 블로킹이되어 메모리에 쌓이기만 함... 버퍼드 채널에 데이터가 꽉차있을 경우 블록이 되는 현상이 있긴한데... 슬로우 컨슈머가 데이터를 천천히 받다가 컨슈머가 사라지는 경우 그냥 blocking이 되버림

 

세션상태를 sdk에서 매번 체크하는거보다 pub/sub 방식으로 개선해야하는게 최선같아보임... 어플리케이션 내부에서 사용하기 위해 MQ보다는 Watermil 라이브러리를 활용하기로 함

 

좀비 고루틴으로 인해 레디스 커넥션과 메모리를 계속 잡아먹는 현상이 남아있었음. 타임 아웃 에러날떄 언버퍼드 채널이 블록되면서, 메모리 누수가 발생함... 타임아웃시 적절한 응답을 넣어서 해결함


 

언젠가 고루틴을 쓰게 된다면 여기서 이야기해 준 고루틴 채널 처리 팁은 잘 숙지하면 좋을 것 같다.

 

딱히 의문점이 없던 세션이었던게 대부분 시나리오가 제대로 잡히지 않은 케이스를 sdk와 연동하면서 채널 블록이 발생한 거였다. 그래도 디버깅 과정은 힘들었을 것 같긴한데 그나마 Go는 고루틴 디버깅 툴이 되게 잘 되어있다는 것으로 알고 있는데도 메모리릭은 잡기 힘든 것 같다.

 

JVM에서 저런 일이 발생했으면 좀 끔찍했을 것 같긴하다. 사실 문제는 고루틴 하나의 역할이 너무 큰거 같아보이기도 하고, 채널 간 데이터를 지속적으로 주고 받는게 문제 같아보여서 자바 멀티쓰레드에서는 저렇게 구현하긴 어려울 것 같다. 최대한 서비스 로직에서 처리하도록 만들었을 듯? > 이 내용이 Q&A에서 나오긴 했다.

 

3. 실시간 광고 사용자 ID 매핑 시스템 구축

네이버 홈페이지 전면에는 광고가 많이 노출되고 있다. 사용자의 일련의 과정들이 이벤트로 기록되고 로그를 남기고 있음. 그리고 사용자의 ID와 어떻게 이 이벤트를 매핑했는가를 다룸

 

ad_id : 광고를 본 사용자를 식별하기 위한 아이디임 > 사용자를 가장 잘 대표하는 ID

 

다기종에 부여된 ID를 묶어서(한 사용자가 사용하는 다기종) 하나의 Group ID로 사용하게끔 만들었음

 

초기에는 배치로 처리해서 광고 이벤트에 사용자 ID를 부여했었음 그리고 필요로하는 DB에 저장하는 구조

> 일괄 처리기 때문에 실시간으로 처리되지 않아 지연이 발생하고 확장성이 어려움

 

ID 매핑 트리 알고리즘

조건문을 나열하기보다는 id를 연결하는 과정을 그래프 구조로 표현하면 조금 더 좋은 구조로 관리할 수 있을 것 같음. 그러나 사이클이 생기지 않도록 유의해야해서.... ID에 우선순위를 지정하고 사용... 시간순으로 들어오지 않아도 멱등성을 유지할 수 있도록 진행해야 함

 

모든 모듈이 분산처리를 위해서 분리되어 있음...

 

Spark Structured Streaming

앞의 알고리즘이 어디에 적용되어 있는가를 정리해준거 같은데, 이전 것들이 완벽히 기억하고 있지 못하다보니 잘 이해가 안됐음...

 

유연한 대처가 가능해졌다

id 타입이 추가될 경우


어떤 걸 했는지는 알겠는데, 알고리즘에 대해 너무 깊이 설명해서 앞에 설명한 왜?를 잘 기억하지 못하게 됐다.

 

나는 대충 내용을 쓰면서 들어서 알고리즘 적용 과정이 이벤트 id를 그룹핑해서 스트리밍 저장으로 변경하기 위함을 기억할 수 있었지만, 다른 사람들은 이걸 왜 했느냐라는 목적을 중간에 놓칠 것 같다. 알고리즘과 적용한 인프라 부분을 바로바로 보여줬으면 이해하기 쉬웠을지도?

 

알고리즘도 복잡했는데, 인프라 구조도 복잡했다... 너무 로우한 레벨에서의 설명이라 다들 잘 이해하지 못한 것 같았다...  공기가 무거워짐을 느꼈음

 

그리고 스파크와 하둡으로 처리 한다는 건 광고 이벤트 데이터의 규모가 엄청났다는 건데, 이 부분이 잘 전달되지 않았다.

 

학회에서 논문 발표를 보는 느낌을 받았다. 뭔가 논문 발표한 내용을 요약해서 들고 나온 느낌?

 

발표자 분이 긴장을 많이 하신 것 같았다

 

마치며

첫 두 세션은 같은 분야 사람이 아니어도 이해할 수 있도록 잘 추상화해서 발표해주셨다. 두 번째 세션의 경우 나는 Go를 거의 모르지만 어느 정도 이해할 수 있었다.

 

그런 점에서 마지막 세션이 조금 아쉬웠다.

 

발표 들으러 온 사람 중에는 학생도 종종 있어보였는데, 이해하기 어려울 것 같았다. 나도 반정도만 이해함 ㅠ

 

그래도 이런 컨퍼런스를 꾸준히 해주는 조직이 거의 없는데 이렇게 좋은 세션을 열어주셔서 참 감사하단 생각이 들었다

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함