Nyílt forráskódunk története: hogyan hoztunk létre és tettünk nyilvánosan elérhetővé egy analitikai szolgáltatást a Go-ban

Jelenleg a világon szinte minden vállalat gyűjt statisztikát a webes erőforrásokon végzett felhasználói műveletekről. A motiváció egyértelmű – a cégek tudni akarják, hogyan használják termékeiket/webhelyüket, és jobban meg akarják érteni felhasználóikat. Természetesen számos eszköz létezik a piacon a probléma megoldására – az elemző rendszerektől kezdve, amelyek műszerfalak és grafikonok formájában szolgáltatnak adatokat (pl. Google Analytics.) a Customer Data Platformra, amely lehetővé teszi, hogy különböző forrásokból származó adatokat gyűjtsön és összesítsen bármely raktárban (pl. Szegmens).

De találtunk egy problémát, amelyet még nem sikerült megoldani. Így született EventNative — nyílt forráskódú elemző szolgáltatás. Olvassa el, miért döntöttünk úgy, hogy saját szolgáltatást fejlesztünk, mit adott nekünk, és mi lett a végeredmény (kóddarabokkal).

Nyílt forráskódunk története: hogyan hoztunk létre és tettünk nyilvánosan elérhetővé egy analitikai szolgáltatást a Go-ban

Miért érdemes saját szolgáltatásunkat fejlesztenünk?

A kilencvenes évek voltak, túléltük, ahogy tudtuk. 2019-ben kifejlesztettük az API First Customer Data Platformot kSense, amely lehetővé tette a különböző forrásokból (Facebook hirdetések, Stripe, Salesforce, Google play, Google Analytics stb.) származó adatok összesítését a kényelmesebb adatelemzés, függőségek azonosítása stb. Észrevettük, hogy sok felhasználó adatelemzésre használja platformunkat, kifejezetten a Google Analytics-et (a továbbiakban GA). Néhány felhasználóval beszélgettünk, és rájöttünk, hogy szükségük van a termékükhöz kapcsolódó analitikai adatokra, amelyeket a GA használatával kapnak, de A Google adatmintákat vesz és sokak számára a GA felhasználói felület nem a kényelem mércéje. Eleget beszélgettünk a felhasználóinkkal, és rájöttünk, hogy sokan a Segment platformot is használják (ami egyébként éppen a minap 3.2 milliárd dollárért kelt el).

Telepítettek egy Segment javascript pixelt a webes erőforrásukra, és a felhasználók viselkedésére vonatkozó adatok betöltésre kerültek a megadott adatbázisba (például Postgres). De a szegmensnek megvan a hátránya is – az ár. Például, ha egy webes erőforrás 90,000 1,000 MTU-val rendelkezik (havi nyomon követett felhasználók), akkor havonta ~XNUMX XNUMX USD-t kell fizetnie a pénztárosnak. Volt egy harmadik probléma is – egyes böngészőbővítmények (például az AdBlock) blokkolták az elemzések gyűjtését, mert... A böngésző http-kérelmei elküldésre kerültek a GA és a Segment tartományokba. Ügyfeleink kívánságai alapján olyan elemző szolgáltatást hoztunk létre, amely teljes adathalmazt gyűjt (mintavétel nélkül), ingyenes és saját infrastruktúránkon is működik.

Hogyan működik a szolgáltatás

A szolgáltatás három részből áll: egy javascript pixelből (amit később gépírással átírtunk), a szerver rész GO nyelven van implementálva, valamint a Redshift és a BigQuery házon belüli adatbázisként való felhasználását tervezték (később kiegészítették a Postgres, ClickHouse és Snowflake).

Úgy döntöttek, hogy a GA és a szegmens események szerkezetét változatlanul hagyják. Mindössze annyi kellett, hogy a webes erőforrásból, ahol a pixel telepítve van, minden eseményt lemásoljunk a háttérrendszerünkre. Mint kiderült, ezt nem nehéz megtenni. A Javascript pixel felülírta az eredeti GA-könyvtár metódust egy újjal, ami megkettőzte az eseményt a rendszerünkbe.

//'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);
        });
    });
}

A Segment pixellel minden egyszerűbb, köztes szoftveres módszerek vannak, amelyek közül az egyiket használtuk.


//'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.');
}

Az események másolása mellett tetszőleges json küldésének lehetőségét is hozzáadtuk:


//Отправка событий с произвольным 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'
});

Ezután beszéljünk a szerver részről. A háttérprogramnak el kell fogadnia a http kéréseket, és ki kell töltenie azokat további információkkal, például földrajzi adatokkal (köszönöm maxmind ehhez) és rögzítse az adatbázisban. A szolgáltatást a lehető legkényelmesebbé akartuk tenni, hogy minimális konfigurációval lehessen használni. Megvalósítottuk az adatséma meghatározásának funkcióját a bejövő json esemény szerkezete alapján. Az adattípusokat értékek határozzák meg. A beágyazott objektumok szétbontásra és lapos szerkezetre redukálódnak:

//входящий 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"
}

A tömböket azonban jelenleg egyszerűen karakterláncokká alakítják, mert Nem minden relációs adatbázis támogatja az ismételt mezőket. Lehetőség van a mezőnevek megváltoztatására vagy törlésére is az opcionális leképezési szabályok segítségével. Lehetővé teszik az adatséma módosítását, ha szükséges, vagy az egyik adattípust egy másikra konvertálják. Például, ha egy json mező tartalmaz egy karakterláncot időbélyeggel (field_3_sub_field_1_sub_sub_field_1 a fenti példából), akkor egy időbélyeg típusú mező létrehozásához az adatbázisban egy leképezési szabályt kell írni a konfigurációban. Más szavakkal, a mező adattípusát először a json-érték határozza meg, majd a típus-casting szabályt (ha be van állítva) alkalmazza a rendszer. Négy fő adattípust azonosítottunk: STRING, FLOAT4, INT64 és TIMESTAMP. A leképezési és típusöntési szabályok így néznek ki:

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

Algoritmus az adattípus meghatározásához:

  • konvertálja a json-struktúrát lapos struktúrává
  • a mezők adattípusának meghatározása értékek alapján
  • leképezési és típusöntési szabályok alkalmazása

Ezután a bejövő json szerkezetből:

{
    "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"
    }
}

az adatsémát megkapjuk:

"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

Arra is gondoltunk, hogy a felhasználónak képesnek kell lennie az adatbázisban lévő particionálás konfigurálására vagy adatok felosztására más kritériumok szerint, és megvalósítottuk azt a lehetőséget, hogy a tábla nevét konstans ill. kifejezés a konfigurációban. Az alábbi példában az esemény egy táblázatba kerül mentésre, amelynek neve a product_type és _timestamp mező értékei alapján kerül kiszámításra (pl. kellékek_2020_10):

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

A bejövő események szerkezete azonban futás közben változhat. Megvalósítottunk egy algoritmust, amellyel ellenőrizhetjük a különbséget egy meglévő tábla és egy bejövő esemény szerkezete között. Ha eltérést talál, a táblázat új mezőkkel frissül. Ehhez használja a patch SQL lekérdezést:

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

építészet

Nyílt forráskódunk története: hogyan hoztunk létre és tettünk nyilvánosan elérhetővé egy analitikai szolgáltatást a Go-ban

Miért kell az eseményeket a fájlrendszerbe írni, és nem csak közvetlenül az adatbázisba írni? Az adatbázisok nem mindig teljesítenek jól, ha nagyszámú beszúrással (Postgres ajánlások). Ennek érdekében a Logger egy fájlba írja a beérkező eseményeket, és egy külön gorutinban (szál) a fájlolvasó beolvassa a fájlt, majd az adatokat konvertálja és meghatározza. Miután a Table Manager megbizonyosodott arról, hogy a táblaséma naprakész, az adatok egy kötegben kerülnek az adatbázisba. Ezt követően hozzáadtuk az adatok közvetlen adatbázisba való írásának lehetőségét, de ezt a módot használjuk olyan eseményekhez, amelyek nem sokak - például konverziók.

Nyílt forráskód és a jövőre vonatkozó tervek

Egy bizonyos ponton a szolgáltatás kezdett teljes értékű terméknek tűnni, és úgy döntöttünk, hogy kiadjuk nyílt forráskódúnak. Jelenleg a Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake integrációkat hajtották végre. Minden integráció támogatja az adatbetöltés kötegelt és streaming módját egyaránt. Támogatás hozzáadva az API-n keresztüli kérésekhez.

A jelenlegi integrációs séma így néz ki:

Nyílt forráskódunk története: hogyan hoztunk létre és tettünk nyilvánosan elérhetővé egy analitikai szolgáltatást a Go-ban

Bár a szolgáltatás önállóan is használható (például Docker segítségével), nálunk is van hosztolt verzió, amelyben beállíthatja az integrációt egy adattárházzal, hozzáadhat egy CNAME-t a domainhez, és megtekintheti az események számáról szóló statisztikákat. Közvetlen terveink között szerepel, hogy ne csak egy webes forrásból származó statisztikákat, hanem külső adatforrásokból származó adatokat is összesíthessünk, és azokat tetszőleges tárhelyre elmentsük!

→ GitHub
→ dokumentáció
→ Laza

Örülünk, ha az EventNative segít megoldani problémáit!

A felmérésben csak regisztrált felhasználók vehetnek részt. Bejelentkezés, kérem.

Milyen statisztikai adatgyűjtési rendszert használnak az Ön cégében?

  • 48,0%Google Analytics 12

  • 4,0%1. szegmens

  • 16,0%Egy másik (írd meg kommentben)4

  • 32,0%Megvalósította szolgáltatását8

25 felhasználó szavazott. 6 felhasználó tartózkodott.

Forrás: will.com

Hozzászólás