A look at how Tencent Games built service architecture based on CQRS and event sourcing patterns with Pulsar and ScyllaDB. כחלק מ-Tencent Interactive Entertainment Group Global (IEG Global), Proxima Beta מחויבת לתמוך בצוותים ובסטודיו שלנו כדי להביא משחקים ייחודיים ומרגשים למיליוני שחקנים ברחבי העולם. הצוות שלנו ב-Level Infinite (החברה העולמית לפרסום) אחראי לניהול מגוון רחב של סיכונים לעסק שלנו – לדוגמה, פעילויות הונאה ותוכן מזיק. בבלוג הזה, אנו חולקים את הניסיון שלנו בבניית מערכת האנליטיקה המבוססת על אירועים בזמן אמת, ראשית, נבחן מדוע נבנה ארכיטקטורת השירות שלנו המבוססת על פירוק אחריות פקודה וקריירה ( (הופנה מהדף Event Sourcing Patterns with לאחר מכן, נסתכל על האופן שבו אנו משתמשים ב- ScyllaDB כדי לפתור את הבעיה של שליחת אירועים למפגשים רבים של המשחק. CQRs אפאצ'ה Pulsar מבט על מקרה השימוש: פתרון סיכונים במשחקים של טנסנט בואו נתחיל עם דוגמה בעולם האמיתי של מה שאנחנו עובדים עם ואתגרים אנו מתמודדים. זהו צילומי מסך של מגדל פנטזיה, משחק תפקידים של פעולה 3D. שחקנים יכולים להשתמש בדיאלוג הזה כדי להגיש דו"ח נגד שחקן אחר מסיבות שונות. האתגר הראשון יהיה לקבוע איזו צוות תהיה הבעלים של מסד הנתונים כדי לאחסן את הטופס הזה.ישנן סיבות שונות ליצירת דו"ח (כולל אפשרות בשם "אחרים"), כך שאפשר להתמודד עם מקרה על ידי צוותים פונקציונליים שונים. לכן, זהו בחירה טבעית עבורנו לתפוס את המקרה הזה כאירוע, כמו "דווח על מקרה."כל המידע נלקח באירוע זה כפי שהוא. CQRS ו-Event Sourcing ארכיטקטורת השירות שמאחורי דוגמה זו מבוססת על דפוסי CQRS והזנת אירועים. אם תנאים אלה הם חדשים עבורך, אל תדאגו! בסוף סקירה זו, עליך להבין היטב את המושגים האלה. . הבלוג המוקדש לנושא זה הרעיון העיקרי מאחורי הזנת אירועים הוא שכל שינוי במצב המערכת נלקח אובייקט אירוע, ואובייקטים אלה מאוחסנים בסדר שבו הם הועלו למצב המערכת, במילים אחרות, במקום פשוט לאחסן את המצב הנוכחי, אנו משתמשים בחנות רק בתוספת כדי להקליט את כל סדרת הפעולות שנקטו במצב זה. הרעיון הבא הוא CQRS, אשר מופיע בשם Command Query Responsibility Segregation. CQRS הומצא על ידי גרג יאנג לפני יותר מעשור ומקורו בפסיכולוגיה של הפרדה בין פקודות פקודות. הרעיון הבסיסי הוא ליצור מודלים נתונים נפרדים לקריאה ולכתוב, במקום להשתמש במודל זהה לשני מטרות. על ידי עוקב אחר דפוס CQRS, כל API צריך להיות או פקודה המבצעת פעולה, או שאילתה שמחזירה נתונים למקרא - אבל לא לשניהם. הפרדה זו מציעה יתרונות רבים.לדוגמה, אנו יכולים להגדיל באופן עצמאי את יכולת הכתיבה והקריאה כדי לאופטימיזציה של יעילות עלויות. זרימת העבודה ברמה גבוהה של צד הכתיבה ניתן לסכם כדלקמן: אירועים המתרחשים במפגשים רבים של המשחק מועברים למספר מוגבל של מעבדי אירועים. היישום הוא גם פשוט, בדרך כלל כולל אוטובוס הודעות כגון Pulsar, Kafka, או מערכת ציון פשוטה יותר הפועלת כחנות אירועים. אירועים מלקוחות מתמשכים באירוע על פי נושא ומעבדי אירועים צורכים אירועים על ידי מנוי לנושאים. אם אתה מעוניין למה בחרנו Apache Pulsar על פני מערכות אחרות, תוכל למצוא מידע נוסף ב . הבלוג שהוזכר בעבר למרות שמערכות דומות לקו הן בדרך כלל יעילות בניהול התנועה המובילה בכיוון אחד (לדוגמה, תנועת מעריצים), הן עשויות לא להיות יעילות בניהול התנועה המובילה בכיוון ההפוך (לדוגמה, תנועת מעריצים). בתרחיש שלנו, מספר הפגישות של המשחק יהיה גדול, ומערכת קווית טיפוסית אינה מתאימה היטב מאחר שאיננו יכולים להרשות לעצמנו ליצור שורה מיועדת עבור כל פגישה של המשחק. לפני שנמשיך, הנה סיכום של ארכיטקטורת השירות שלנו. החל מהצד של הכתיבה, שרתי המשחק ממשיכים לשלוח אירועים למערכת שלנו דרך נקודות הקצה של הפקודה, וכל אירוע מייצג סוג מסוים של פעילות שקרה בפגישה של המשחק. מעבדי אירוע מייצרים ממצאים או מדדים נגד זרימי האירועים של כל פגישה של המשחק ומגיבים כגשר בין שני צדדים. חנות אירועים מסוג Queue-Like המופצת לאירועים של סדרת הזמן עכשיו בואו נסתכל על איך אנו משתמשים ב-ScyllaDB כדי לפתור את הבעיה של שליחת אירועים למפגשים רבים של המשחק. אגב, אם תגלו את "קסנדרה" ואת "צוואה", ייתכן שתתקלו במאמר מלפני יותר מעשור שאומר כי השימוש בקסנדרה כצוואה הוא אנטי דפוס. בעוד שזה יכול היה להיות נכון באותו זמן, הייתי טוען שזה נכון רק חלקית היום. כדי לתמוך בהפצה של אירועים לכל פגישה של המשחק, אנו משתמשים ב- Session ID ככפתור המקטעים, כך שלכל פגישה של המשחק יש מקטע משלה, והאירועים שייכים לפגישה מסוימת של המשחק יכולים להיות ממוקמים ביעילות על-ידי ה- Session ID. לכל אירוע יש גם מזהה אירוע ייחודי, שהוא UUID בזמן, כפתור קבוצה. מכיוון שהקלטות בתוך אותו מחלקה מסווגות על ידי מפתח קבוצה, ניתן להשתמש בזהה אירוע כזהה המיקום בצירוף. לבסוף, לקוחות ScyllaDB יכולים לקבל ביעילות את האירועים שהגיעו לאחרונה על ידי מעקב אחר מזהה אירוע של האירוע האחרון שהתקבל. יש אזהרה אחת שיש לזכור כאשר משתמשים בגישה זו: הבעיה של עקביות.חשיפה של אירועים חדשים על ידי מעקב אחר זהות האירוע האחרונה מבוססת על ההנחה כי שום אירוע עם זהות קטנה יותר לא יתקיים בעתיד.עם זאת, ההנחה הזאת עשויה לא תמיד להיות נכונה.לדוגמה, אם שני כפתורים מייצרים שני מזהים לאירוע בו-זמנית, אירוע עם זהות קטנה יותר עשוי להיכנס מאוחר יותר מאשר אירוע עם זהות גדולה יותר. בעיה זו, שאני מתייחס אליה כ"קריאת פנטום", דומה לתופעה בעולם SQL שבה חזור על אותה השאילתה יכול להביא לתוצאות שונות עקב שינויים לא מחויבים שנעשו על ידי עסקה אחרת. ישנן מספר דרכים להתמודד עם בעיה זו.פתרון אחד הוא לשמור על מצב בקבוצות, שאני קורא לזה "pseudo now", בהתבסס על הערך הקטן ביותר של תווי זמן זזים בין כל מעבדי האירועים. שיקול חשוב נוסף הוא האפשרות TimeWindowCompactionStrategy, אשר מסיר את ההשפעה השלילית על הביצועים שנגרמת על ידי קברים. עכשיו בואו נעבור לדיון על יתרונות אחרים מעבר לשימוש ScyllaDB כסיבוב משלוח. הפחתת האתגרים המורכבים של חלוקת הנתונים העולמית מאחר שאנחנו בונים מערכת מרובה דירות כדי לשרת לקוחות ברחבי העולם, חשוב להבטיח כי תצורות הלקוחות הן עקביות בין קבוצות באזורים שונים. פתחנו את הבעיה הזאת על-ידי האפשרות פשוטה של העתקה של נתונים במרחב מפתח בכל מרכזי הנתונים, כלומר כל שינוי שנעשה במרכז נתונים אחד יתרחש בסופו של דבר לאחרים.תודה ל-ScyllaDB, כמו גם ל-DynamoDB ו-Cassandra, על ההעלאה הכבדה שהופכת את הבעיה המאתגרת הזו לבלתי אפשרית. ייתכן שאתם חושבים ששימוש בכל RDBMS טיפוסי יכול להשיג את אותו התוצאה, שכן רוב מסדי הנתונים תומכים גם בפירוט נתונים. זה נכון אם יש רק דוגמה אחת של לוח הבקרה פועל באזור נתון. בארכיטקטורה ראשונית/רפיקה טיפוסית, רק המנוף העיקרי תומך בקריאה/כתבה בעוד ערכי הרפיקה הם קריאה בלבד. עם זאת, כאשר אתה צריך להפעיל דוגמאות מרובות של לוח הבקרה בין אזורים שונים – לדוגמה, לכל שוכר יש לוח הבקרה פועל באזור הבית שלו, או אפילו כל אזור יש לוח הבקרה פועל עבור צוותים מקומיים – זה הופך להיות הרבה יותר קשה ליישם זאת באמצעות ארכיטקטורה ראשונית/רפיקה טיפוסית. אם השתמשת ב-AWS DynamoDB, ייתכן שאתה מכיר תכונה בשם Global Table, המאפשרת ליישומים לקרוא ולכתוב באופן מקומי ולגשת לנתונים ברחבי העולם. Keyspaces כמספקי נתונים לאחר מכן, בואו נסתכל על האופן שבו אנו משתמשים במרחבי מפתח כמכולות נתונים כדי לשפר את השקיפות של הפצת הנתונים הגלובלית. בואו נסתכל על הדיאגרמה הבאה.היא מציגה פתרון לבעיה טיפוסית של הפצה של נתונים שנקבעה על-ידי חוקי הגנת נתונים.לדוגמה, נניח כי אזור A מאפשר עיבוד סוגים מסוימים של נתונים מחוץ לגבולותיה כל עוד עותק מקורי נשמר באזור. * • * • פתרון פוטנציאלי אחד הוא ביצוע בדיקות מסוף לקצה (E2E) כדי להבטיח כי יישומים שולחים נכון את הנתונים הנכונים לאזור הנכון כצפוי. גישה זו דורשת מפתחי יישומים לקחת אחריות מלאה על יישום ההפצה של הנתונים כראוי. על-ידי האפשרות להכפיל נתונים על מרחבי מפתח, אנו יכולים לחלק את האחריות להפצה נכונה של נתונים לשתי משימות: 1) זיהוי סוגי נתונים וצהירת היעד שלהם, ו- 2) העתקה או העברת נתונים למקומות הצפויים. על ידי הפרדה בין שתי המשימות האלה, אנו יכולים להפריד הגדרות מורכבות ותקנות מן היישומים.זה בגלל התהליך של העברת נתונים לאזור אחר הוא לעתים קרובות החלק המורכב ביותר להתמודד עם, כגון מעבר גבולות הרשת, הצפנה נכונה של התנועה, וניהול הפרעות. לאחר הפרידה בין שני המשימות האלה, יישומים נדרשים רק לבצע נכון את הצעד הראשון, אשר הרבה יותר קל לאמת באמצעות בדיקות בשלבים מוקדמים של מחזור הפיתוח. טיפים לאחרים המשתתפים במסלול דומה לסיום, נשאיר לכם שיעורים חשובים שלמדנו, ואנחנו ממליצים לך להגיש אותם אם תצטרכו ללכת בדרך דומה לנו: כאשר אתה משתמש ב- ScyllaDB כדי לטפל בנתוני סדרת זמן, כגון שימוש בו כשמירה על אירוע, זכור להשתמש באסטרטגיה Time-Window Compaction. שים לב להשתמש במרחבי מפתח כקניונים לנתונים כדי להפריד את האחריות של הפצת נתונים.זה יכול להפוך את בעיות הפצה של נתונים מורכבים הרבה יותר קל לניהול. עקבו אחרי Tech Talks On-Demand מאמר זה מבוסס על שיחה טכנולוגית שהוצגה ב-ScyllaDB Summit 2023.You watch this talk – as well as talks by engineers from Discord, Epic Games, Disney, Strava, ShareChat and more – on demand. צפו בשיחות טכנולוגיות על פי דרישה