ההיסטוריה של הקוד הפתוח שלנו: איך יצרנו שירות ניתוח ב-Go והפכנו אותו לזמין לציבור

נכון לעכשיו, כמעט כל חברה בעולם אוספת נתונים סטטיסטיים על פעולות המשתמש במשאב אינטרנט. המוטיבציה ברורה – חברות רוצות לדעת כיצד נעשה שימוש במוצר/אתר שלהן ולהבין טוב יותר את המשתמשים שלהן. כמובן שקיימים בשוק מספר רב של כלים לפתור בעיה זו - החל ממערכות אנליטיקה המספקות נתונים בצורת לוחות מחוונים וגרפים (למשל Google Analytics) לפלטפורמת נתוני לקוחות, המאפשרת לך לאסוף ולצבור נתונים ממקורות שונים בכל מחסן (לדוגמה מגזר).

אבל מצאנו בעיה שעדיין לא נפתרה. כך נולד EventNative - שירות ניתוח קוד פתוח. קראו מדוע החלטנו לפתח שירות משלנו, מה הוא נתן לנו ומה הייתה התוצאה הסופית (עם פיסות קוד).

ההיסטוריה של הקוד הפתוח שלנו: איך יצרנו שירות ניתוח ב-Go והפכנו אותו לזמין לציבור

מדוע עלינו לפתח שירות משלנו?

זה היה שנות התשעים, שרדנו כמיטב יכולתנו. בשנת 2019, פיתחנו את פלטפורמת נתוני הלקוחות הראשונה של API kSense, מה שאפשר לצבור נתונים ממקורות שונים (מודעות Facebook, Stripe, Salesforce, Google play, Google Analytics וכו') לניתוח נתונים נוח יותר, זיהוי תלות וכו'. שמנו לב שמשתמשים רבים משתמשים בפלטפורמה שלנו לניתוח נתונים במיוחד ב-Google Analytics (להלן GA). דיברנו עם כמה משתמשים וגילינו שהם צריכים את נתוני הניתוח עבור המוצר שלהם שהם מקבלים באמצעות GA, אבל גוגל דוגמת נתונים ועבור רבים, ממשק המשתמש של GA אינו סטנדרט הנוחות. היו לנו מספיק שיחות עם המשתמשים שלנו והבנו שרבים משתמשים גם בפלטפורמת Segment (שדרך אגב, היה רק ​​לפני כמה ימים נמכר ב-3.2 מיליארד דולר).

הם התקינו פיקסל Javascript של Segment על משאב האינטרנט שלהם ונתונים על התנהגות המשתמשים שלהם נטענו למסד הנתונים שצוין (לדוגמה Postgres). אבל לסגמנט יש גם החיסרון שלו - המחיר. לדוגמה, אם למשאב אינטרנט יש 90,000 MTU (משתמשים במעקב חודשי), אז אתה צריך לשלם ~1,000 $ לחודש לקופאי. הייתה גם בעיה שלישית - כמה תוספי דפדפן (כגון AdBlock) חסמו את אוסף הניתוחים בגלל... בקשות http מהדפדפן נשלחו לדומיינים של GA ו-Sport. בהתבסס על רצונות הלקוחות שלנו, יצרנו שירות ניתוח שאוסף סט שלם של נתונים (ללא דגימה), הוא בחינם ויכול לעבוד על התשתית שלנו.

איך השירות עובד

השירות מורכב משלושה חלקים: פיקסל javascript (שאחר כך שכתבנו אותו בכתב טיפוס), חלק השרת מיושם בשפת GO, ותוכנן להשתמש ב-Redshift וב-BigQuery כמסד נתונים פנימי (מאוחר יותר הוסיפו תמיכה עבור Postgres, ClickHouse ו-Snowflake).

הוחלט להשאיר את מבנה אירועי ה-GA והסגמנט ללא שינוי. כל מה שהיה צריך היה לשכפל את כל האירועים ממשאב האינטרנט שבו הפיקסל מותקן ל-backend שלנו. כפי שמתברר, זה לא קשה לעשות. פיקסל ה-Javascript דרס את שיטת ספריית ה-GA המקורית בשיטת חדשה, ששכפלה את האירוע למערכת שלנו.

//'ga' - стандартное название переменной Google Analytics
if (window.ga) {
    ga(tracker => {
        var originalSendHitTask = tracker.get('sendHitTask');
        tracker.set('sendHitTask', (model) => {
            var payLoad = model.get('hitPayload');
            //отправка оригинального события в GA
            originalSendHitTask(model);
            let jsonPayload = this.parseQuery(payLoad);
            //отправка события в наш сервис
            this.send3p('ga', jsonPayload);
        });
    });
}

עם פיקסל Segment הכל פשוט יותר; יש לו שיטות תווך, באחת מהן השתמשנו.


//'analytics' - стандартное название переменной Segment
if (window.analytics) {
    if (window.analytics.addSourceMiddleware) {
        window.analytics.addSourceMiddleware(chain => {
            try {
		//дублирование события в наш сервис
                this.send3p('ajs', chain.payload);
            } catch (e) {
                LOG.warn('Failed to send an event', e)
            }
	    //отправка оригинального события в Segment
            chain.next(chain.payload);
        });
    } else {
        LOG.warn("Invalid interceptor state. Analytics js initialized, but not completely");
    }
} else {
    LOG.warn('Analytics.js listener is not set.');
}

בנוסף להעתקת אירועים, הוספנו את היכולת לשלוח json שרירותי:


//Отправка событий с произвольным json объектом
eventN.track('product_page_view', {
    product_id: '1e48fb70-ef12-4ea9-ab10-fd0b910c49ce',
    product_price: 399.99,
    price_currency: 'USD'
    product_release_start: '2020-09-25T12:38:27.763000Z'
});

לאחר מכן, בואו נדבר על חלק השרת. הקצה העורפי צריך לקבל בקשות http, למלא אותן במידע נוסף, למשל נתונים גיאוגרפיים (תודה מקסמינד עבור זה) ורשום אותו במסד הנתונים. רצינו להפוך את השירות לנוח ככל האפשר כדי שניתן יהיה להשתמש בו בתצורה מינימלית. יישמנו את הפונקציונליות של קביעת סכימת הנתונים על סמך המבנה של אירוע ה-json הנכנס. סוגי נתונים מוגדרים לפי ערכים. עצמים מקוננים מפורקים ומצטמצמים למבנה שטוח:

//входящий json
{
  "field_1":  {
    "sub_field_1": "text1",
    "sub_field_2": 100
  },
  "field_2": "text2",
  "field_3": {
    "sub_field_1": {
      "sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
    }
  }
}

//результат
{
  "field_1_sub_field_1":  "text1",
  "field_1_sub_field_2":  100,
  "field_2": "text2",
  "field_3_sub_field_1_sub_sub_field_1": "2020-09-25T12:38:27.763000Z"
}

עם זאת, מערכים מומרים כעת פשוט למחרוזות מכיוון לא כל מסדי הנתונים היחסיים תומכים בשדות חוזרים. אפשר גם לשנות שמות שדות או למחוק אותם באמצעות כללי מיפוי אופציונליים. הם מאפשרים לך לשנות את סכימת הנתונים במידת הצורך או להמיר סוג נתונים אחד לאחר. לדוגמה, אם שדה json מכיל מחרוזת עם חותמת זמן (field_3_sub_field_1_sub_sub_field_1 מהדוגמה למעלה), אז כדי ליצור שדה במסד הנתונים עם סוג חותמת הזמן, עליך לכתוב כלל מיפוי בתצורה. במילים אחרות, סוג הנתונים של השדה נקבע תחילה על ידי ערך json, ולאחר מכן מוחל כלל הליהוק של הסוג (אם מוגדר). זיהינו 4 סוגי נתונים עיקריים: STRING, FLOAT64, INT64 ו-TIMESTAMP. כללי המיפוי והטיפוס נראים כך:

rules:
  - "/field_1/subfield_1 -> " #правило удаления поля
  - "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
  - "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа

אלגוריתם לקביעת סוג הנתונים:

  • המרת מבנה json למבנה שטוח
  • קביעת סוג הנתונים של שדות לפי ערכים
  • יישום כללי מיפוי ויציקת סוג

ואז ממבנה ה-json הנכנס:

{
    "product_id":  "1e48fb70-ef12-4ea9-ab10-fd0b910c49ce",
    "product_price": 399.99,
    "price_currency": "USD",
    "product_type": "supplies",
    "product_release_start": "2020-09-25T12:38:27.763000Z",
    "images": {
      "main": "picture1",
      "sub":  "picture2"
    }
}

סכימת הנתונים תתקבל:

"product_id" character varying,
"product_price" numeric (38,18),
"price_currency" character varying,
"product_type" character varying,
"product_release_start" timestamp,
"images_main" character varying,
"images_sub" character varying

חשבנו גם שהמשתמש צריך להיות מסוגל להגדיר חלוקה או חלוקת נתונים במסד הנתונים לפי קריטריונים אחרים ויישמנו את היכולת להגדיר את שם הטבלה עם קבוע או ביטוי בתצורה. בדוגמה למטה, האירוע יישמר בטבלה עם שם מחושב על סמך הערכים של השדות product_type ו_timestamp (לדוגמה supplies_2020_10):

tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'

עם זאת, המבנה של אירועים נכנסים יכול להשתנות בזמן הריצה. הטמענו אלגוריתם לבדיקת ההבדל בין מבנה טבלה קיימת למבנה של אירוע נכנס. אם נמצא הבדל, הטבלה תעודכן בשדות חדשים. לשם כך, השתמש בשאילתת התיקון של SQL:

#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying

אדריכלות

ההיסטוריה של הקוד הפתוח שלנו: איך יצרנו שירות ניתוח ב-Go והפכנו אותו לזמין לציבור

למה צריך לכתוב אירועים למערכת הקבצים, ולא רק לכתוב אותם ישירות למסד הנתונים? מסדי נתונים לא תמיד מתפקדים היטב כאשר מתמודדים עם מספר גדול של הוספות (המלצות Postgres). לשם כך, Logger כותב אירועים נכנסים לקובץ וב-goroutine נפרד (thread) קורא הקבצים קורא את הקובץ, ואז הנתונים מומרים ונקבעים. לאחר שמנהל הטבלה מוודא שסכימת הטבלה מעודכנת, הנתונים ייכתבו לבסיס הנתונים באצווה אחת. בהמשך, הוספנו את היכולת לכתוב נתונים ישירות למסד הנתונים, אך אנו משתמשים במצב זה לאירועים שאינם רבים - למשל, המרות.

קוד פתוח ותוכניות לעתיד

בשלב מסוים, השירות החל להיראות כמו מוצר מן המניין והחלטנו לשחרר אותו לקוד פתוח. נכון לעכשיו, הוטמעו אינטגרציות עם Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. כל האינטגרציות תומכות הן במצבי אצווה והן במצבי סטרימינג של טעינת נתונים. נוספה תמיכה לבקשות דרך API.

תכנית האינטגרציה הנוכחית נראית כך:

ההיסטוריה של הקוד הפתוח שלנו: איך יצרנו שירות ניתוח ב-Go והפכנו אותו לזמין לציבור

למרות שניתן להשתמש בשירות באופן עצמאי (למשל באמצעות Docker), יש לנו גם גרסה מתארחת, שבו אתה יכול להגדיר אינטגרציה עם מחסן נתונים, להוסיף CNAME לדומיין שלך ולהציג נתונים סטטיסטיים על מספר האירועים. התוכניות המיידיות שלנו הן להוסיף את היכולת לצבור לא רק נתונים סטטיסטיים ממשאב אינטרנט, אלא גם נתונים ממקורות נתונים חיצוניים ולשמור אותם בכל אחסון שתבחר!

→ GitHub
→ רשומות
→ רפוי

נשמח אם EventNative יעזור לפתור את הבעיות שלך!

רק משתמשים רשומים יכולים להשתתף בסקר. להתחברבבקשה.

באיזו מערכת לאיסוף נתונים סטטיסטיים משתמשים בחברה שלך?

  • 48,0%גוגל אנליטיקס12

  • 4,0%פלח1

  • 16,0%עוד (כתוב בתגובות)4

  • 32,0%הטמיע את השירות שלך8

25 משתמשים הצביעו. 6 משתמשים נמנעו.

מקור: www.habr.com

הוספת תגובה