분산락으로 Go 마이크로서비스의 경쟁 조건 방지
카테고리
프로그래밍/소프트웨어 개발
서브카테고리
DevOps
대상자
Go 마이크로서비스 개발자, 분산 시스템 설계자
- 중급 이상의 Go 언어 이해도와 Redis 사용 경험 필요
- 분산락 구현과 병렬 처리 문제 해결에 관심 있는 개발자
핵심 요약
- 분산락은 여러 노드가 공유 자원에 동시에 접근하는 것을 방지하는 메커니즘
- Redsync는 Redis 기반으로 구현된 Redlock 알고리즘을 사용한 Go 라이브러리
- lockmanager는 Redsync의 반복 로직을 추상화하여 사용 편의성 향상
- TTL 설정과 컨텍스트 타임아웃은 잠금 실패 시 복구 전략의 핵심
섹션별 세부 요약
1. 분산락의 정의와 목적
- 경쟁 조건은 여러 서비스가 동일한 공유 자원에 동시 접근할 때 발생
- 분산락은 mutex의 분산 버전으로, 여러 프로세스/노드 간 동기화 가능
- 주요 사용 사례: 티켓 예약 중복 방지, 큐 메시지 처리, 코드 실행 시리얼라이징
2. Redsync 라이브러리 사용 예제
- go-redis와 redsync 패키지 사용 예시:
```go
client := goredislib.NewClient(&goredislib.Options{Addr: "localhost:6379"})
pool := goredis.NewPool(client)
rs := redsync.New(pool)
mutex := rs.NewMutex("my-global-mutex")
mutex.Lock() // 잠금 획득
mutex.Unlock() // 잠금 해제
```
- Redsync의 장점: 간단한 구현, 프로덕션 환경 검증된 신뢰성
3. lockmanager 패키지의 활용
- go-common의 lockmanager는 Redsync의 반복 로직 추상화:
```go
locker := redsyncLocker.NewRedsyncLockManager(client, redsyncLocker.WithTokenGenerator(...))
token, err := locker.Acquire(ctx, "demo-lock-key", 2*time.Second)
```
- 주요 기능:
- 표준 LockManager
인터페이스 제공
- 토큰 생성기와 재시도 로직 플러그인 가능
- 단위 테스트를 위한 모킹 지원
4. 분산락 사용 시 주의 사항
- TTL 설정의 중요성:
- 짧은 TTL: 잠금 해제 전에 작업 완료 불가 → 병렬 실행 가능성
- 긴 TTL: 노드 장애 시 잠금 블록 가능
- 컨텍스트 타임아웃 사용 권장: 잠금 실패 시 시스템 복구 가능
- 잠금은 소유권이 아닌 조율 목적: 영구 상태 저장은 피해야 함
결론
- TTL과 컨텍스트 타임아웃을 반드시 설정하여 잠금 실패 시 복구 가능하도록 설계
- lockmanager 사용으로 Redsync의 반복 로직을 추상화하여 구현 편의성 향상
- Redis 기반 분산락은 마이크로서비스에서 자원 충돌 방지에 핵심적인 역할을 수행