Историята на нашия отворен код: как направихме услуга за анализ в Go и я направихме публично достъпна

В момента почти всяка компания в света събира статистика за действията на потребителите на уеб ресурс. Мотивацията е ясна – компаниите искат да знаят как се използва техният продукт/уебсайт и да разбират по-добре своите потребители. Разбира се, на пазара има голям брой инструменти за решаване на този проблем - от системи за анализ, които предоставят данни под формата на табла и графики (напр. Google Analytics) към платформата за данни на клиента, което ви позволява да събирате и обобщавате данни от различни източници във всеки склад (напр. Сегмент).

Но открихме проблем, който все още не е решен. Така се роди EventNative — услуга за анализ с отворен код. Прочетете защо решихме да разработим наша собствена услуга, какво ни даде тя и какъв беше крайният резултат (с части от код).

Историята на нашия отворен код: как направихме услуга за анализ в Go и я направихме публично достъпна

Защо трябва да развиваме собствена услуга?

Беше деветдесетте години, оцеляхме както можахме. 2019 разработихме API First Customer Data Platform kSense, което направи възможно агрегирането на данни от различни източници (реклами във Facebook, Stripe, Salesforce, Google play, Google Analytics и др.) за по-удобен анализ на данни, идентифициране на зависимости и др. Забелязахме, че много потребители използват нашата платформа за анализ на данни, по-специално Google Analytics (наричана по-долу GA). Разговаряхме с някои потребители и разбрахме, че те се нуждаят от аналитични данни за своя продукт, които получават с помощта на GA, но Google взема проби от данни и за мнозина потребителският интерфейс на GA не е стандартът за удобство. Проведохме достатъчно разговори с нашите потребители и разбрахме, че много от тях също използват платформата Segment (което, между другото, беше само онзи ден продаден за 3.2 милиарда долара).

Те инсталираха Segment javascript пиксел на своя уеб ресурс и данните за поведението на техните потребители бяха заредени в посочената база данни (например Postgres). Но Segment има и своя недостатък - цената. Например, ако даден уеб ресурс има 90,000 1,000 MTU (месечно проследявани потребители), тогава трябва да плащате ~XNUMX $ на месец на касата. Имаше и трети проблем - някои разширения на браузъра (като AdBlock) блокираха събирането на анализи, защото... http заявките от браузъра бяха изпратени до домейните GA и Segment. Въз основа на желанията на нашите клиенти създадохме аналитична услуга, която събира пълен набор от данни (без извадка), безплатна е и може да работи върху нашата собствена инфраструктура.

Как работи услугата

Услугата се състои от три части: javascript пиксел (който по-късно пренаписахме на машинопис), сървърната част е внедрена на езика GO и беше планирано да се използват Redshift и BigQuery като вътрешна база данни (по-късно те добавиха поддръжка за Postgres, ClickHouse и Snowflake).

Беше решено структурата на събитията на GA и сегмента да остане непроменена. Всичко, което беше необходимо, беше да дублираме всички събития от уеб ресурса, където е инсталиран пикселът, в нашия бекенд. Както се оказа, това не е трудно да се направи. Пикселът на 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 pixel всичко е по-просто; той има мидълуерни методи, един от които използвахме.


//'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 поле съдържа низ с клеймо за време (поле_3_под_поле_1_под_под_поле_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 заявката за корекция:

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

архитектура

Историята на нашия отворен код: как направихме услуга за анализ в Go и я направихме публично достъпна

Защо трябва да записвате събития във файловата система, а не просто да ги записвате директно в базата данни? Базите данни не винаги се представят добре, когато работят с голям брой вмъквания (Препоръки на Postgres). За да направи това, Logger записва входящите събития във файл и в отделна goroutine (нишка) File Reader чете файла, след което данните се конвертират и определят. След като мениджърът на таблици се увери, че схемата на таблицата е актуална, данните ще бъдат записани в базата данни в един пакет. Впоследствие добавихме възможност за запис на данни директно в базата данни, но използваме този режим за събития, които не са многобройни - например конверсии.

Open Source и планове за бъдещето

В един момент услугата започна да изглежда като пълноценен продукт и решихме да я пуснем с отворен код. В момента са внедрени интеграции с Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Всички интеграции поддържат както пакетен, така и поточен режим на зареждане на данни. Добавена е поддръжка за заявки чрез API.

Сегашната интеграционна схема изглежда така:

Историята на нашия отворен код: как направихме услуга за анализ в Go и я направихме публично достъпна

Въпреки че услугата може да се използва независимо (например с помощта на Docker), ние също имаме хоствана версия, в който можете да настроите интеграция с хранилище за данни, да добавите CNAME към вашия домейн и да видите статистика за броя на събитията. Нашите непосредствени планове са да добавим възможност за агрегиране не само на статистически данни от уеб ресурс, но и на данни от външни източници на данни и запазването им във всяко хранилище по ваш избор!

→ GitHub
→ Документация
→ Застой

Ще се радваме, ако EventNative помогне за разрешаването на вашите проблеми!

В анкетата могат да участват само регистрирани потребители. Впиши се, Моля те.

Каква система за събиране на статистически данни се използва във вашата компания?

  • 48,0%Google Analytics12

  • 4,0%Сегмент1

  • 16,0%Друг (пишете в коментарите)4

  • 32,0%Внедри вашата услуга8

25 потребители гласуваха. 6 потребители се въздържаха.

Източник: www.habr.com

Добавяне на нов коментар