تاریخچه منبع باز ما: چگونه یک سرویس تجزیه و تحلیل را در Go ایجاد کردیم و آن را در دسترس عموم قرار دادیم

در حال حاضر، تقریباً هر شرکتی در جهان آماری در مورد اقدامات کاربر در یک منبع وب جمع آوری می کند. انگیزه واضح است - شرکت‌ها می‌خواهند بدانند محصول/وب‌سایتشان چگونه استفاده می‌شود و کاربران خود را بهتر درک کنند. البته، تعداد زیادی ابزار در بازار برای حل این مشکل وجود دارد - از سیستم های تحلیلی که داده ها را در قالب داشبورد و نمودار ارائه می کنند (به عنوان مثال گوگل آنالیز) به پلتفرم داده های مشتری، که به شما امکان می دهد داده ها را از منابع مختلف در هر انبار جمع آوری و جمع آوری کنید (به عنوان مثال بخش).

اما مشکلی پیدا کردیم که هنوز حل نشده است. بدین ترتیب متولد شد EventNative - سرویس تجزیه و تحلیل منبع باز. در مورد اینکه چرا تصمیم گرفتیم سرویس خودمان را توسعه دهیم، چه چیزی به ما داد و نتیجه نهایی چه بود (با تکه‌های کد) بخوانید.

تاریخچه منبع باز ما: چگونه یک سرویس تجزیه و تحلیل را در Go ایجاد کردیم و آن را در دسترس عموم قرار دادیم

چرا باید خدمات خود را توسعه دهیم؟

دهه نود بود، ما به بهترین شکل ممکن زنده ماندیم. در سال 2019، پلتفرم داده‌های مشتری اول API را توسعه دادیم kSense، که امکان جمع آوری داده ها از منابع مختلف (تبلیغات فیس بوک، Stripe، Salesforce، Google play، Google Analytics و غیره) را برای تجزیه و تحلیل راحت تر داده ها، شناسایی وابستگی ها و غیره فراهم می کند. ما متوجه شده ایم که بسیاری از کاربران از پلتفرم ما برای تجزیه و تحلیل داده ها به ویژه Google Analytics (از این پس GA) استفاده می کنند. ما با برخی از کاربران صحبت کردیم و متوجه شدیم که آنها به داده های تحلیلی محصول خود نیاز دارند که با استفاده از GA دریافت می کنند، اما گوگل داده ها را نمونه برداری می کند و برای بسیاری، رابط کاربری GA استاندارد راحتی نیست. ما مکالمه کافی با کاربران خود داشتیم و متوجه شدیم که بسیاری از پلتفرم Segment نیز استفاده می کنند (که اتفاقاً همین روز گذشته بود. 3.2 میلیارد دلار فروخته شد).

آنها یک پیکسل جاوا اسکریپت Segment را بر روی منبع وب خود نصب کردند و داده های مربوط به رفتار کاربران آنها در پایگاه داده مشخص شده بارگذاری شد (به عنوان مثال Postgres). اما Segment جنبه منفی خود را نیز دارد - قیمت. به عنوان مثال، اگر یک منبع وب دارای 90,000 MTU (کاربران ردیابی ماهانه) باشد، باید 1,000 دلار در ماه به صندوقدار پرداخت کنید. همچنین یک مشکل سوم وجود داشت - برخی از افزونه های مرورگر (مانند AdBlock) مجموعه تجزیه و تحلیل را مسدود کردند زیرا ... درخواست‌های http از مرورگر به دامنه‌های GA و Segment ارسال شد. بر اساس خواسته های مشتریان خود، ما یک سرویس تجزیه و تحلیل ایجاد کرده ایم که مجموعه کاملی از داده ها را (بدون نمونه برداری) جمع آوری می کند، رایگان است و می تواند روی زیرساخت های خودمان کار کند.

نحوه عملکرد سرویس

این سرویس از سه بخش تشکیل شده است: یک پیکسل جاوا اسکریپت (که بعداً آن را در تایپ اسکریپت بازنویسی کردیم)، بخش سرور به زبان GO پیاده سازی شده است، و قرار بود از Redshift و BigQuery به عنوان یک پایگاه داده داخلی استفاده شود (بعدها پشتیبانی از آن را اضافه کردند. Postgres، ClickHouse و Snowflake).

تصمیم گرفته شد که ساختار رویدادهای GA و Segment بدون تغییر باقی بماند. تنها چیزی که مورد نیاز بود این بود که همه رویدادها را از منبع وب که پیکسل در آن نصب شده است به باطن ما کپی کنیم. همانطور که معلوم است، انجام این کار دشوار نیست. پیکسل جاوا اسکریپت روش کتابخانه اصلی 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 را بپذیرد، آنها را با اطلاعات اضافی پر کند، به عنوان مثال، داده های جغرافیایی (با تشکر maxmind برای این) و آن را در پایگاه داده ثبت کنید. ما می خواستیم سرویس را تا حد امکان راحت کنیم تا بتوان از آن با حداقل تنظیمات استفاده کرد. ما عملکرد تعیین طرح داده را بر اساس ساختار رویداد 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 از مثال بالا)، سپس برای ایجاد یک فیلد در پایگاه داده با نوع timestamp، باید یک قانون نگاشت در پیکربندی بنویسید. به عبارت دیگر، نوع داده فیلد ابتدا با مقدار 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 ذخیره می شود (به عنوان مثال لوازم_2020_10):

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

با این حال، ساختار رویدادهای ورودی می تواند در زمان اجرا تغییر کند. ما یک الگوریتم برای بررسی تفاوت بین ساختار یک جدول موجود و ساختار یک رویداد ورودی پیاده‌سازی کرده‌ایم. در صورت مشاهده تفاوت، جدول با فیلدهای جدید به روز می شود. برای این کار از پچ SQL Query استفاده کنید:

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

معماری

تاریخچه منبع باز ما: چگونه یک سرویس تجزیه و تحلیل را در Go ایجاد کردیم و آن را در دسترس عموم قرار دادیم

چرا باید رویدادها را در سیستم فایل بنویسید و فقط آنها را مستقیماً در پایگاه داده بنویسید؟ هنگامی که با تعداد زیادی از درج ها سروکار دارید، پایگاه های داده همیشه عملکرد خوبی ندارند (توصیه های Postgres). برای انجام این کار، Logger رویدادهای دریافتی را در یک فایل می نویسد و در یک گوروتین (رشته) جداگانه فایل خوان فایل را می خواند، سپس داده ها تبدیل و تعیین می شود. پس از اینکه مدیر جدول مطمئن شد که طرح جدول به روز است، داده ها در یک دسته در پایگاه داده نوشته می شوند. متعاقباً، ما توانایی نوشتن مستقیم داده‌ها را به پایگاه داده اضافه کردیم، اما از این حالت برای رویدادهایی استفاده می‌کنیم که زیاد نیستند - به عنوان مثال، تبدیل‌ها.

منبع باز و برنامه هایی برای آینده

در مقطعی، این سرویس مانند یک محصول تمام عیار به نظر می رسید و ما تصمیم گرفتیم آن را به منبع باز عرضه کنیم. در حال حاضر، ادغام با Postgres، ClickHouse، BigQuery، Redshift، S3، Snowflake پیاده سازی شده است. همه ادغام ها از هر دو حالت دسته ای و جریانی بارگذاری داده پشتیبانی می کنند. پشتیبانی از درخواست ها از طریق API اضافه شده است.

طرح ادغام فعلی به این صورت است:

تاریخچه منبع باز ما: چگونه یک سرویس تجزیه و تحلیل را در Go ایجاد کردیم و آن را در دسترس عموم قرار دادیم

اگرچه می توان از این سرویس به طور مستقل استفاده کرد (به عنوان مثال با استفاده از Docker)، ما نیز داریم نسخه میزبانی شده، که در آن می توانید یکپارچه سازی با یک انبار داده راه اندازی کنید، یک CNAME به دامنه خود اضافه کنید و آمار تعداد رویدادها را مشاهده کنید. برنامه‌های فوری ما اضافه کردن توانایی جمع‌آوری نه تنها آمار از یک منبع وب، بلکه داده‌ها از منابع داده خارجی و ذخیره آنها در هر فضای ذخیره‌سازی مورد نظر شما است!

→ GitHub
→ اسناد
→ شل

اگر EventNative به حل مشکلات شما کمک کند، خوشحال خواهیم شد!

فقط کاربران ثبت نام شده می توانند در نظرسنجی شرکت کنند. ورود، لطفا.

چه سیستم جمع آوری آمار در شرکت شما استفاده می شود؟

  • ٪۱۰۰گوگل آنالیتیکس 12

  • ٪۱۰۰بخش 1

  • ٪۱۰۰دیگری (در نظرات بنویسید)4

  • ٪۱۰۰سرویس شما 8 را پیاده سازی کرد

25 کاربر رای دادند. 6 کاربر رای ممتنع دادند.

منبع: www.habr.com

اضافه کردن نظر