A look at how Tencent Games built service architecture based on CQRS and event sourcing patterns with Pulsar and ScyllaDB. 텐센트 인터랙티브 엔터테인먼트 그룹 글로벌(IEG Global)의 일원으로서 프록시마 베타는 팀과 스튜디오를 지원하여 전 세계 수백만 명의 플레이어에게 독특하고 흥미로운 게임을 제공합니다. Level Infinite의 팀은 우리의 비즈니스에 대한 다양한 위험을 관리하기 위해 책임이 있습니다 – 예를 들어, 사기 활동과 해로운 콘텐츠. 이 블로그에서 우리는 이 실시간 이벤트 기반 분석 시스템을 구축하는 경험을 공유합니다.첫째, 왜 우리는 명령 및 쿼리 책임 분리를 기반으로 서비스 아키텍처를 구축했는지 탐구 할 것입니다 ( ) 및 이벤트 sourcing patterns with 그리고 ScyllaDB. 다음으로, 우리는 ScyllaDB를 사용하여 많은 게임 플레이 세션에 이벤트를 전송하는 문제를 해결하는 방법을 살펴볼 것입니다. 마지막으로, 우리는 ScyllaDB 키 스페이스와 데이터 복제를 사용하여 글로벌 데이터 관리를 단순화하는 방법을 다룰 것입니다. CQR 아파치 Pulsar A Peek at the Use Case: Tencent 게임의 위험에 대처하기 우리가 일하고있는 것과 우리가 직면하는 도전에 대한 실제 사례로 시작합시다. 이것은 3D 액션 롤 플레이 게임인 탑 오브 판타지 (Tower of Fantasy)의 스크린 샷입니다. 플레이어는 여러 가지 이유로 다른 플레이어에 대한 보고서를 제출하기 위해이 대화 상자를 사용할 수 있습니다. 만약 당신이 전형적인 CRUD 시스템을 사용한다면, 그 기록을 추적하기 위해 어떻게 보관하겠습니까? 첫 번째 도전은 이 양식을 저장하기 위해 어떤 팀이 데이터베이스를 소유할 것인지를 결정하는 것입니다.이 보고서를 작성하는 다른 이유가 있습니다 (다른 이름의 옵션을 포함하여), 그래서 사례가 다른 기능 팀에 의해 처리 될 수 있습니다. 따라서 “사례를 보고”와 같은 사건으로 이 사건을 캡처하는 것이 자연스러운 선택입니다.이 사건에서 모든 정보가 캡처됩니다.모든 기능 팀은 이 사건에 가입하고 자신의 필터링을 할 필요가 있습니다.이 사건이 그들의 도메인에 해당한다고 생각한다면, 그들은 단지 그것을 캡처하고 추가 작업을 시작할 수 있습니다. CQRS 및 이벤트 소싱 이 예제의 서비스 아키텍처는 CQRS 및 이벤트 소스 패턴을 기반으로합니다.이 용어가 당신에게 새로운 경우, 걱정하지 마십시오!이 개요의 끝에, 당신은 이러한 개념에 대한 견고한 이해를해야합니다.그리고 그 시점에서 더 자세한 내용을 원한다면, 우리의 . 이 주제에 헌신하는 블로그 여기서 이해해야 할 첫 번째 개념은 이벤트 소싱입니다. 이벤트 소싱 뒤의 핵심 아이디어는 시스템 상태에 대한 모든 변화가 이벤트 개체에서 캡처되고 이러한 이벤트 개체는 시스템 상태에 적용 된 순서로 저장됩니다. 다른 말로하면 현재 상태를 저장하는 대신에, 우리는 단지 첨부 스토어를 사용하여 해당 상태에 수행 된 전체 시리즈의 작업을 기록합니다.이 개념은 모든 작업을 나타내는 이벤트가 기록되므로 이벤트를 통해 시스템을 설명하는 가능한 모델을 구축 할 수 있도록 간단하지만 강력합니다. 다음 개념은 명령 쿼리 책임 분리(Command Query Responsibility Segregation)을 의미하는 CQRS입니다. CQRS는 10년 전 Greg Young에 의해 발명되었으며 명령 및 쿼리 분리 원칙에서 유래했습니다. 기본적인 아이디어는 두 가지 목적을 위해 동일한 모델을 사용하는 대신 읽기 및 쓰기에 대한 별도의 데이터 모델을 만드는 것입니다. CQRS 패턴을 따르면서 각 API는 작업을 수행하는 명령 또는 호출자에게 데이터를 반환하는 쿼리이되어야합니다. 예를 들어, 우리는 비용 효율성을 최적화하기 위해 독립적으로 쓰기 및 읽기 용량을 확장할 수 있습니다.Teamwork의 관점에서, 다른 팀은 적은 충돌로 동일한 데이터의 다른 뷰를 만들 수 있습니다. 글쓰기 측의 높은 수준의 워크플로우는 다음과 같이 요약할 수 있습니다: 수많은 게임 세션에서 발생하는 이벤트는 제한된 수의 이벤트 프로세서에 전달됩니다. 이 구현은 또한 간단하며, 일반적으로 Pulsar, Kafka와 같은 메시지 버스 또는 이벤트 스토어로 작용하는 간단한 차례 시스템을 포함합니다. 클라이언트의 이벤트는 이벤트 스토어에서 주제별로 지속되고 이벤트 프로세서는 주제에 가입하여 이벤트를 소비합니다. . 이전에 언급한 블로그 꼬리 같은 시스템은 일반적으로 한 방향으로 흐르는 트래픽 (예: 팬 인)을 처리하는 데 효율적이지만, 반대 방향으로 흐르는 트래픽 (예: 팬 아웃)을 처리하는 데 효과적이지 않을 수 있습니다. 우리의 시나리오에서는 게임 세션의 수가 크고, 일반적인 꼬리 시스템은 이벤트 팬 아웃을 위해 최적화 된 또 다른 꼬리 같은 이벤트 스토어를 구축하기 위해 ScyllaDB를 사용하는 이유입니다. 앞으로 나아가기 전에, 여기에 우리의 서비스 아키텍처의 요약입니다. 글쓰기 측면에서부터 게임 서버는 명령 엔드포인트를 통해 시스템에 이벤트를 계속 전송하며 각 이벤트는 게임 세션에서 발생한 특정 유형의 활동을 나타냅니다. 이벤트 프로세서는 각 게임 세션의 이벤트 스트림에 대한 결과 또는 지표를 생성하고 두 측면 사이의 다리 역할을합니다. Time Series 이벤트를 위한 분산된 Queue-Like Event Store 이제 ScyllaDB를 사용하여 수많은 게임 세션에 이벤트를 전송하는 문제를 해결하는 방법을 살펴보자. 그건 그렇고, Google에서 "Cassandra"와 "queue"를 검색하면, Cassandra를 차례로 사용하는 것이 안티 패턴이라고 주장하는 10 년 전의 기사를 만날 수 있습니다. 각 게임 세션에 이벤트의 배송을 지원하기 위해, 우리는 세션 ID를 파티션 키로 사용하여 각 게임 세션에는 자체 파티션이 있고 특정 게임 세션에 속하는 이벤트는 세션 ID에 의해 효율적으로 위치할 수 있습니다. 각 이벤트에는 동일한 파티션 내의 레코드가 클러스터 키에 의해 정렬되기 때문에, 이벤트 ID는 차례에 위치 ID로 사용할 수 있습니다. 마지막으로, ScyllaDB 클라이언트는 가장 최근에 수신된 이벤트의 이벤트 ID를 추적하여 새로 도착한 이벤트를 효율적으로 검색할 수 있습니다. 이 접근법을 사용할 때 주의해야 할 한 가지 사항은 일관성 문제입니다.최신 이벤트 ID를 추적함으로써 새로운 이벤트를 검색하는 것은 더 작은 ID가 있는 이벤트가 미래에 발생하지 않을 것이라는 가정에 의존합니다.그러나 이 가정은 항상 사실이 아닐 수 있습니다.예를 들어, 두 노드가 두 개의 이벤트 식별자를 동시에 생성하면, 더 큰 ID가 있는 이벤트보다 더 나중에 작은 ID가 있는 이벤트가 삽입될 수 있습니다. 이 문제는 "영혼 읽기"라고 부르는 SQL 세계의 현상과 유사하며, 같은 쿼리를 반복하면 다른 트랜잭션에 의한 의지하지 않은 변경으로 인해 다른 결과를 얻을 수 있습니다.그러나, 문제의 근본 원인은 우리의 경우는 다릅니다.그것은 이벤트 ID에 의해 지정된 순서에서 ScyllaDB에 이벤트가 맡겨질 때 발생합니다. 이 문제를 해결하는 몇 가지 방법이 있습니다. 하나의 솔루션은 클러스터 전체 상태를 유지하는 것입니다.이 문제는 모든 이벤트 프로세서 중 움직이는 타임스탬프의 가장 작은 값을 바탕으로 "pseudo now"라고 부릅니다.Every event processor should also ensure that all future events have an event id greater than its current timestamp. 또 다른 중요한 고려 사항은 TimeWindowCompactionStrategy를 허용하는 것이며, 이는 무덤 돌에 의한 부정적인 성능 영향을 제거합니다. 무덤 돌의 축적은 TimeWindowCompactionStrategy가 사용할 수 있기 전에 Cassandra를 차례로 사용하지 못하게 한 주요 문제였습니다. 이제 ScyllaDB를 배송 차례로 사용하는 것 이외의 다른 혜택에 대해 논의해 보자. 복잡한 글로벌 데이터 배포 문제를 단순화 전 세계 고객에게 서비스를 제공하기 위해 다중 텐션 시스템을 구축하고 있기 때문에 다른 지역의 클러스터에 걸쳐 고객 구성이 일관되도록하는 것이 필수적입니다. 우리는 단순히 모든 데이터 센터에서 키 스페이스에 데이터 복제를 허용함으로써이 문제를 해결했습니다.이것은 하나의 데이터 센터에서 수행 된 모든 변경이 결국 다른 데이터 센터로 전파됩니다.ScyllaDB, DynamoDB 및 Cassandra,이 도전적인 문제를 사소하게 보이게 만드는 무거운 리프팅에 감사드립니다. 대부분의 데이터베이스가 데이터 복제를 지원하기 때문에 일반적인 RDBMS를 사용하면 동일한 결과를 얻을 수 있다고 생각할 수 있습니다.이것은 특정 영역에서 실행되는 제어판의 한 인스턴스 만 있으면 사실입니다. 전형적인 기본/복제 아키텍처에서는 기본 노드만 읽기/쓰기를 지원하며 복제 노드만 읽을 수 있습니다.그러나 일반적인 기본/복제 아키텍처를 사용하여 이 작업을 구현하는 것이 훨씬 더 어렵습니다. AWS DynamoDB를 사용한 경우, 앱이 로컬로 읽고 쓰고 전 세계적으로 데이터에 액세스할 수 있도록 하는 글로벌 테이블(Global Table)이라고 불리는 기능에 익숙해질 수 있습니다.ScyllaDB를 사용하여 키 스페이스에 대한 복제 기능을 사용하면 비슷한 기능을 제공하지만 공급자 잠금 없이 글로벌 테이블을 다중 클라우드 환경에 쉽게 확장할 수 있습니다. 데이터 컨테이너 (Keyspace as Data Containers) 다음으로, 우리는 키 스페이스를 데이터 컨테이너로 사용하여 글로벌 데이터 배포의 투명성을 향상시키는 방법을 살펴보자. 아래 차트를 살펴보자.이 차트는 데이터 보호법에 의해 부과되는 전형적인 데이터 배포 문제에 대한 해결책을 보여줍니다.예를 들어, 지역 A는 원본 복사본이 해당 지역에 보관되는 한 해당 지역 외부에서 특정 유형의 데이터를 처리할 수 있다고 가정하십시오.제품 소유자로서 모든 응용 프로그램이이 규정을 준수하도록 어떻게 보장할 수 있습니까? * 는 * 는 하나의 잠재적인 해결책은 응용 프로그램이 예상대로 올바른 지역으로 올바른 데이터를 올바르게 전송할 수 있도록 엔드 투 엔드 (E2E) 테스트를 수행하는 것입니다.이 접근 방식은 응용 프로그램 개발자가 데이터 배포를 올바르게 구현하는 것에 대한 전적인 책임을 져야합니다.그러나 응용 프로그램의 수가 증가함에 따라 각 응용 프로그램이이 문제를 개별적으로 처리하는 것이 불가능해지고 E2E 테스트는 시간과 비용 모두에서 점점 더 비니다. 키 스페이스에 데이터 복제를 허용함으로써 데이터를 올바르게 배포하는 책임을 두 가지 작업으로 나눌 수 있습니다: 1) 데이터 유형을 식별하고 목적지를 선언하며, 2) 데이터를 복사하거나 예상 위치로 이동합니다. 이 두 작업을 분리함으로써 우리는 복잡한 구성 및 규정을 애플리케이션에서 추출할 수 있습니다.이것은 데이터를 다른 지역으로 전송하는 과정이 종종 네트워크 경계를 통과하고 트래픽을 올바르게 암호화하고 중단을 처리하는 것과 같은 가장 복잡한 부분이기 때문입니다. 이 두 작업을 분리한 후, 응용 프로그램은 첫 번째 단계를 올바르게 수행해야 하며, 이는 개발주기의 초기 단계에서 테스트를 통해 확인하기가 훨씬 쉽습니다. 비슷한 길을 걷는 다른 사람들을 위한 팁 결론적으로, 우리는 우리가 배운 중요한 교훈을 남기고, 당신이 우리와 유사한 경로를 취할 경우 적용하는 것이 좋습니다 : ScyllaDB를 사용하여 시간 시리즈 데이터를 처리할 때, 예를 들어 이벤트 배송 차례로 사용하는 경우, Time-Window Compaction Strategy를 사용하는 것을 기억하십시오. 키 스페이스를 데이터 컨테이너로 사용하여 데이터 배포의 책임을 분리하는 것을 고려하십시오.This can make complex data distribution problems much easier to manage. Tech Talks On-Demand에 대한 리뷰 보기 이 기사는 ScyllaDB Summit 2023에서 발표 된 기술 토론을 바탕으로합니다.이 토론은 Discord, Epic Games, Disney, Strava, ShareChat 및 기타 엔지니어들의 토론과 함께 요청에 따라 시청됩니다. Tech Talks on Demand에 대한 리뷰 보기