Die geskiedenis van ons oopbron: hoe ons 'n ontledingsdiens in Go gemaak het en dit publiek beskikbaar gemaak het

Tans versamel byna elke maatskappy in die wêreld statistieke oor gebruikersaksies op 'n webhulpbron. Die motivering is duidelik – maatskappye wil weet hoe hul produk/webwerf gebruik word en hul gebruikers beter verstaan. Natuurlik is daar 'n groot aantal instrumente op die mark om hierdie probleem op te los - van ontledingstelsels wat data in die vorm van dashboards en grafieke verskaf (byvoorbeeld Google Analytics) na Kliëntdataplatform, wat jou in staat stel om data van verskillende bronne in enige pakhuis te versamel en saam te voeg (byvoorbeeld segment).

Maar ons het 'n probleem gevind wat nog nie opgelos is nie. So is gebore Gebeurtenis Native - oopbron-ontledingsdiens. Lees oor hoekom ons besluit het om ons eie diens te ontwikkel, wat dit vir ons gegee het en wat die eindresultaat was (met stukkies kode).

Die geskiedenis van ons oopbron: hoe ons 'n ontledingsdiens in Go gemaak het en dit publiek beskikbaar gemaak het

Hoekom moet ons ons eie diens ontwikkel?

Dit was die negentigerjare, ons het oorleef so goed ons kon. 2019 het ons die API First Customer Data Platform ontwikkel kSense, wat dit moontlik gemaak het om data uit verskillende bronne (Facebook-advertensies, Stripe, Salesforce, Google play, Google Analytics, ens.) te versamel vir meer gerieflike data-analise, identifisering van afhanklikhede, ens. Ons het opgemerk dat baie gebruikers ons platform gebruik vir data-analise spesifiek Google Analytics (hierna GA). Ons het met 'n paar gebruikers gepraat en uitgevind dat hulle die ontledingsdata benodig vir hul produk wat hulle met GA ontvang, maar Google monster data en vir baie is die GA-gebruikerskoppelvlak nie die standaard van gerief nie. Ons het genoeg gesprekke met ons gebruikers gehad en besef dat baie ook die Segment-platform gebruik (wat, terloops, net die ander dag was verkoop vir $3.2 miljard).

Hulle het 'n Segment-javascript-pixel op hul webhulpbron geïnstalleer en data oor die gedrag van hul gebruikers is in die gespesifiseerde databasis gelaai (byvoorbeeld Postgres). Maar Segment het ook sy nadeel - die prys. Byvoorbeeld, as 'n webhulpbron 90,000 1,000 MTU (maandelikse nagespoorde gebruikers) het, moet jy ~XNUMX XNUMX $ per maand aan die kassier betaal. Daar was ook 'n derde probleem - sommige blaaieruitbreidings (soos AdBlock) het die versameling van ontledings geblokkeer omdat... http-versoeke vanaf die blaaier is na die GA- en Segment-domeine gestuur. Gebaseer op die wense van ons kliënte, het ons 'n ontledingsdiens geskep wat 'n volledige stel data insamel (sonder steekproefneming), gratis is en op ons eie infrastruktuur kan werk.

Hoe die diens werk

Die diens bestaan ​​uit drie dele: 'n javascript-pixel (wat ons later in tikskrif herskryf het), die bedienergedeelte is in die GO-taal geïmplementeer, en daar is beplan om Redshift en BigQuery as 'n interne databasis te gebruik (later het hulle ondersteuning bygevoeg vir Postgres, ClickHouse en Snowflake).

Daar is besluit om die struktuur van die GA en Segment-gebeure onveranderd te laat. Al wat nodig was, was om alle gebeure van die webhulpbron waar die pixel geïnstalleer is na ons agterkant te dupliseer. Soos dit blyk, is dit nie moeilik om te doen nie. Die Javascript-pixel het die oorspronklike GA-biblioteekmetode met 'n nuwe een vervang, wat die gebeurtenis in ons stelsel gedupliseer het.

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

Met die Segment-pixel is alles eenvoudiger; dit het middelware-metodes, waarvan ons een gebruik het.


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

Benewens die kopiëring van gebeure, het ons die vermoë bygevoeg om arbitrêre json te stuur:


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

Kom ons praat dan oor die bedienerdeel. Die agterkant moet http-versoeke aanvaar, vul dit met bykomende inligting, byvoorbeeld geo-data (dankie maxmind hiervoor) en teken dit in die databasis aan. Ons wou die diens so gerieflik moontlik maak sodat dit met minimale konfigurasie gebruik kan word. Ons het die funksionaliteit geïmplementeer om die dataskema te bepaal gebaseer op die struktuur van die inkomende json-gebeurtenis. Datatipes word deur waardes gedefinieer. Geneste voorwerpe word ontbind en gereduseer tot 'n plat struktuur:

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

Skikkings word egter tans bloot na snare omgeskakel omdat Nie alle relasionele databasisse ondersteun herhaalde velde nie. Dit is ook moontlik om veldname te verander of dit uit te vee deur opsionele karteringreëls te gebruik. Hulle laat jou toe om die dataskema te verander indien nodig of een datatipe na 'n ander om te skakel. Byvoorbeeld, as 'n json-veld 'n string met tydstempel (field_3_sub_field_1_sub_sub_field_1 uit die voorbeeld hierbo), dan moet jy 'n karteringreël in die konfigurasie skryf om 'n veld in die databasis met die tydstempeltipe te skep. Met ander woorde, die datatipe van die veld word eers deur die json-waarde bepaal, en dan word die tipe-uitsaaireël (indien opgestel) toegepas. Ons het 4 hoofdatatipes geïdentifiseer: STRING, FLOAT64, INT64 en TIMESTAMP. Die kartering en tipe uitsaaireëls lyk soos volg:

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

Algoritme vir die bepaling van die datatipe:

  • omskep json-struktuur na plat struktuur
  • bepaling van die datatipe velde deur waardes
  • die toepassing van kartering en tipe gietreëls

Dan vanaf die inkomende json-struktuur:

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

die dataskema sal verkry word:

"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

Ons het ook gedink dat die gebruiker in staat moet wees om partisionering te konfigureer of data in die databasis te verdeel volgens ander kriteria en het die vermoë geïmplementeer om die tabelnaam met 'n konstante of uitdrukking in die konfigurasie. In die voorbeeld hieronder sal die gebeurtenis in 'n tabel gestoor word met 'n naam wat bereken word op grond van die waardes van die produk_tipe en _tydstempel velde (byvoorbeeld voorrade_2020_10):

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

Die struktuur van inkomende gebeurtenisse kan egter tydens looptyd verander. Ons het 'n algoritme geïmplementeer om die verskil tussen die struktuur van 'n bestaande tabel en die struktuur van 'n inkomende gebeurtenis na te gaan. As 'n verskil gevind word, sal die tabel opgedateer word met nuwe velde. Om dit te doen, gebruik die pleister SQL-navraag:

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

Argitektuur

Die geskiedenis van ons oopbron: hoe ons 'n ontledingsdiens in Go gemaak het en dit publiek beskikbaar gemaak het

Hoekom moet jy gebeure na die lêerstelsel skryf, en nie net direk na die databasis skryf nie? Databasisse presteer nie altyd goed wanneer groot getalle insetsels hanteer word nie (Postgres aanbevelings). Om dit te doen, skryf Logger inkomende gebeurtenisse na 'n lêer en in 'n aparte goroutine (draad) lees Lêerleser die lêer, dan word die data omgeskakel en bepaal. Nadat die tabelbestuurder seker gemaak het dat die tabelskema op datum is, sal die data in een bondel na die databasis geskryf word. Daarna het ons die vermoë bygevoeg om data direk na die databasis te skryf, maar ons gebruik hierdie modus vir gebeure wat nie baie is nie - byvoorbeeld omskakelings.

Oopbron en planne vir die toekoms

Op 'n stadium het die diens soos 'n volwaardige produk begin lyk en ons het besluit om dit na Open Source vry te stel. Tans is integrasies met Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake geïmplementeer. Alle integrasies ondersteun beide bondel- en stroommodusse van data-laai. Bygevoeg ondersteuning vir versoeke via API.

Die huidige integrasieskema lyk soos volg:

Die geskiedenis van ons oopbron: hoe ons 'n ontledingsdiens in Go gemaak het en dit publiek beskikbaar gemaak het

Alhoewel die diens onafhanklik gebruik kan word (byvoorbeeld met behulp van Docker), het ons ook gasheer weergawe, waarin jy integrasie met 'n datapakhuis kan opstel, 'n CNAME by jou domein kan voeg en statistieke oor die aantal gebeurtenisse kan sien. Ons onmiddellike planne is om die vermoë by te voeg om nie net statistieke van 'n webhulpbron te versamel nie, maar ook data van eksterne databronne en dit te stoor in enige berging van jou keuse!

→ GitHub
→ Dokumentasie
→ Slack

Ons sal bly wees as EventNative jou probleme help oplos!

Slegs geregistreerde gebruikers kan aan die opname deelneem. Meld aan, asseblief.

Watter statistiek-insamelingstelsel word in jou maatskappy gebruik?

  • 48,0%Google Analytics 12

  • 4,0%Segment 1

  • 16,0%Nog een (skryf in die kommentaar)4

  • 32,0%Het u diens geïmplementeer8

25 gebruikers het gestem. 6 gebruikers het buite stemming gebly.

Bron: will.com

Voeg 'n opmerking