2018 아이펀팩토리 데브데이 서버위더스
(iFunFactory DevDay Server,Withus)
제목 : 분산 환경을 위한 ORM 개발 경험 공유
발표자 : 남승현 시니어 프로그래머
일정 : 2018년 03월 28일
개요 : ORM의 모든 것! ORM이 분산 환경에서 작동하기 위한 조건들과 ORM을 위한 캐시, 분산 락 구현에 대해 소개합니다.
아래링크를 통해 아이펀팩토리의 더 많은 정보를 얻으실 수 있습니다.
*아이펀팩토리 홈페이지 : https://ifunfactory.com/
*아이펀팩토리 기술 블로그 : https://blog.ifunfactory.com/
2. 2018 iFunFactory Dev Day
Contents. 01 발표 소개
02 분산 환경을 위한 ORM
04 구현: 코드 자동 생성
05 구현: 이벤트 시스템
06 구현: 비동기 I/O
07 구현: 캐시
08 구현: 잠금(Lock)
09 구현: 분산 환경 – 캐시와 잠금
03 구현: 일반화된 오브젝트
8. 2018 iFunFactory Dev Day
개발 동기
• 단순 반복 Database 작업
1. Table
2. Stored Procedure/SQL
3. SQL 호출 코드
게임 콘텐츠 로직만 구현할 수 없을까?
• 게임 서버 개발 생산성 향상
분산 환경을 위한 ORM
22. 2018 iFunFactory Dev Day
class Model
SQL 을 런타임에 자동 생성 가능하게 함
CREATE TABLE {object-name}
{attribute-name} {type},
{attribute-name} {type},
…
“Character”
“Name CHAR(12)”
“Level INT”
UPDATE {object-name} SET {attribute-name} = {value} WHERE object_id = {object-id}
구현: 일반화된 오브젝트
23. 2018 iFunFactory Dev Day
class Model
SQL 을 런타임에 자동 생성 가능하게 함
구현: 일반화된 오브젝트
CREATE TABLE {object-name}
{attribute-name} {type},
{attribute-name} {type},
…
UPDATE {object-name} SET {attribute-name} = {value} WHERE object_id = {object-id}
“Character”
“Level INT”
“Name CHAR(12)”
24. 2018 iFunFactory Dev Day
class Object, class Model 은 자동 생성되는 코드에서 사용
구현: 일반화된 오브젝트
25. 2018 iFunFactory Dev Day
04 SQL 작업 자동화
SQL 처리 자동화 코드 및
인터페이스 코드 생성
구현: 일반화된 오브젝트
26. 2018 iFunFactory Dev Day
코드 자동 생성
코드 생성 스크립트
모델(JSON) 을 입력 받아 C++ 코드 생성
JSON
코드 생성기
(Python)
Model 생성
인터페이스
Class
<입력>
<출력>
27. 2018 iFunFactory Dev Day
• JSON 으로 표현
• 오브젝트 이름
• 오브젝트의 어트리뷰트와 타입
• Key 라고 부르는 특별한 어트리뷰트 존재
• Key 는 SQL 의 PRIMARY KEY 와 같다
• Key 를 이용하여 오브젝트를 읽을 수 있다
코드 자동 생성
입력: 모델 정의
32. 2018 iFunFactory Dev Day
코드 자동 생성
출력: C++ 코드 - 인터페이스 Class
• 프로그래머 사용하는 유일한 인터페이스
• 오브젝트 생성 함수 – Create()
• 오브젝트를 불러오는 함수 – Fetch()
DB I/O, 잠금, 캐시 등의 처리를 내부에서 수행
• 오브젝트의 각 어트리뷰트를 읽고, 쓰는 함수 – Get()/Set()
SQL 쿼리 생성 및 호출을 내부에서 수행
37. 2018 iFunFactory Dev Day
구현: 이벤트 시스템
이벤트 상태
• 실행 준비: 실행하기 위해 스레드를 기다림
• 실행: 스레드를 할당 받아 실행 중
• 중단(롤백): 오브젝트를 즉시 사용할 수 없는 경우 대기
• DB 에서 읽어와야 할 때
• 잠금을 얻을 수 없을 때
38. 2018 iFunFactory Dev Day
구현: 이벤트 시스템
중단(롤백) 구현
C++ Exception Throw
오브젝트를 사용할 수 있을 때 다시 실행
43. 2018 iFunFactory Dev Day
구현: 비동기 I/O
비동기 I/O 발생
• 오브젝트를 접근할 때 Database 에서 읽어와야 할 때
• I/O 를 블록킹 방식으로 구현하면 처리량 급감
44. 2018 iFunFactory Dev Day
구현: 비동기 I/O
비동기 I/O 구현
• C++ Exception 을 Throw 하여 이벤트 중단(롤백)
• Exception 객체에 I/O 작업을 저장
• I/O 완료 후 이벤트 함수 재실행
이벤트 시스템으로 구현됨
45. 2018 iFunFactory Dev Day
구현: 비동기 I/O
문제
• 오브젝트를 접근하려면 매번 중단(롤백) 발생
• Exception Throw
• I/O 처리
-> 캐시로 해결
50. 2018 iFunFactory Dev Day
캐시에 오브젝트 입력 구현 예)
Character 오브젝트(Name: “devday”, Object-Id: 1234)
Key 로 지정된 “Name” 어트리뷰트
Name 이 “devday” 인 오브젝트의 Object ID
51. 2018 iFunFactory Dev Day
캐시에 검색 구현 예)
Character 오브젝트(Name: “devday”, Object-Id: 1234)
Key 로 지정된 “Name” 어트리뷰트
Name 이 “devday” 인 오브젝트의 Object ID
52. 2018 iFunFactory Dev Day
구현: 캐시
자료구조 - 단점
범위 검색 불가능
-> ordered_map(std::map) 으로 해결 가능
성능 하락
53. 2018 iFunFactory Dev Day
구현: 캐시
제거 타이머
• 오브젝트를 캐시에서 접근 시 타이머 설정
• 모든 오브젝트에 타이머를 두면 성능 문제 발생
• 일정 시간 모아서 처리
55. 2018 iFunFactory Dev Day
구현: 잠금
잠금 구현이 필요한 이유
• 한 오브젝트를 여러 이벤트에서 동시 수정 발생
• 캐시에 로드된 오브젝트는 SQL 의 잠금(Lock) 불가
• SQL 잠금은 DB 에 로직 구현 필요
• Database 샤딩 시 SQL 잠금 사용 어려움
56. 2018 iFunFactory Dev Day
구현: 잠금
잠금의 종류
잠금은 이벤트 함수가 끝날 때까지 유효
• 복사본 읽기
• 읽기
• 쓰기
잠금을 획득할 수 없으면 이벤트 중단(롤백)
58. 2018 iFunFactory Dev Day
구현: 잠금
다음 4 조건이 모두 만족되면 데드락 발생
• 상호배제
프로세스들이 필요로 하는 자원에 대해 배타적인 통제권을 요구한다.
• 점유대기
프로세스가 할당된 자원을 가진 상태에서 다른 자원을 기다린다.
• 비선점
프로세스가 어떤 자원의 사용을 끝낼 때까지 그 자원을 뺏을 수 없다.
• 순환대기
각 프로세스는 순환적으로 다음 프로세스가 요구하는 자원을 가지고 있다.
59. 2018 iFunFactory Dev Day
구현: 잠금
데드락 회피를 위해 두 가지 방법 시도
• 타이머 이용 – 점유대기 방지
• 선점형 잠금 – 비선점 방지
60. 2018 iFunFactory Dev Day
구현: 잠금
타이머 이용
데드락 조건 4 가지 중 점유 대기(Hold and wait) 방지
잠금을 획득할 수 없는 경우
• 획득한 모든 잠금 해제하고 이벤트 중단
• 일정시간 후 잠금이 풀렸기를 기대하며 이벤트 재호출
일정시간 = Exponental backoff 알고리즘
61. 2018 iFunFactory Dev Day
구현: 잠금
타이머 이용 - 장점
구현이 쉬움
타이머 이용 - 단점
경합하는 오브젝트 수가 많으면 해결 불가
62. 2018 iFunFactory Dev Day
구현: 잠금
섬점형 잠금
데드락 조건 4 가지 중 비선점(No preemption) 방지
• 모든 이벤트는 발생 시간에 의한 우선순위를 갖는다
• 먼저 발생된 이벤트는 늦게 발생된 이벤트가 중단 상태이면
잠금을 해제할 수 있다
63. 2018 iFunFactory Dev Day
구현: 잠금
섬점형 잠금 – 데드락 회피 예)
이벤트1, 2 가 Object A, B 를 교차하여 쓰기 잠금 획득
64. 2018 iFunFactory Dev Day
0
1
2
3
4
5
6
7
Time
이벤트1 생성 및 실행
ObjectA 잠금 획득 시도: 성공
Sleep 1
ObjectB 잠금 획득 시도: 실패
-> 이벤트2 의 ObjectB 잠금 해제 요청
중단(롤백)
재실행, ObjectA, B 잠금 획득 시도: 성공
처리 완료
이벤트2 생성 및 실행
ObjectB 잠금 획득 시도: 성공
Sleep 1
ObjectA 잠금 획득 시도: 실패
-> 잠금 해제 불가
중단(롤백), ObjectB 잠금 해제
재실행, ObjectB, A 잠금 획득 시도: 성공, 처리 완료
이벤트1 이벤트2
이벤트1, 2 가 Object A, B 를 교차하여 쓰기 잠금 획득
65. 2018 iFunFactory Dev Day
구현: 잠금
선점형 잠금 - 장점
경합 빈도에 상관 없이 빠르고 일정한 성능
선점형 잠금 - 단점
경합이 없을 땐 타이머 방식 보다 느리다
상대적으로 구현이 어렵다
67. 2018 iFunFactory Dev Day
구현: 분산환경 – 캐시와 잠금
Zookeeper
• 오픈소스 분산 코디네이션 서버
• 클러스터를 구성하여 SPoF(Single point of failure) 를
만들지 않음
• 파일 시스템처럼 사용
경로를 지정하여 데이터 저장
• 오브젝트 소유권 관리
68. 2018 iFunFactory Dev Day
구현: 분산환경 – 캐시와 잠금
Zookeeper 를 이용한 오브젝트 소유권 관리
오브젝트 소유권 = 캐싱
오브젝트를 캐시에 입력하기 전
/objects/{object-id} 파일에 서버ID 저장
해당 경로에 이미 파일이 있으면 해당 서버의 캐시와 잠금 기능을
RPC 로 접근
69. 2018 iFunFactory Dev Day
구현: 분산환경 – 캐시와 잠금
Lease RPC
빌려올 오브젝트 ID 를 인자로 전달
수신한 서버는
• 해당 오브젝트 잠금 처리 + 타임아웃 설정
• 해당 오브젝트를 캐시에서 읽어 응답으로 전달
• 반납 RPC 수신 시 변경 사항이 있으면 반영
70. 2018 iFunFactory Dev Day
구현: 분산환경 – 캐시와 잠금
/objects/
┗1234 => “ServerA”
ID: 1234
Name: “devday”
Zookeeper
ServerA
Cache
ServerA 가 Character(Name:”devday”, Object-Id:1234) 캐싱
72. 2018 iFunFactory Dev Day
구현: 분산환경 – 캐시와 잠금
캐시 적중률 문제
• Lease RPC 는 무겁다
• 오브젝트는 최대한 많이 접근하는 서버가 소유
해결
• 캐시는 각 오브젝트의 서버 별 접근 빈도 계산
• 더 높은 빈도의 서버 발견 시 타이머와 상관 없이 캐시에서 내린다
73. Q&A
2018 iFunFactory Dev Day
2018 iFunFactory Dev Day
Thank You!
경기도 성남시 분당구 대왕판교로 660, 유스페이스1 B동 606호 아이펀팩토리
info@ifunfactory.com +82-70-4923-6566 www.ifunfactory.com