For tiden samler nesten alle selskaper i verden inn statistikk om brukerhandlinger på en nettressurs. Motivasjonen er klar – bedrifter ønsker å vite hvordan deres produkt/nettside brukes og bedre forstå brukerne sine. Selvfølgelig finnes det et stort antall verktøy på markedet for å løse dette problemet - fra analysesystemer som gir data i form av dashboards og grafer (f.eks.
Men vi fant et problem som ikke er løst ennå. Slik ble født
Hvorfor skal vi utvikle vår egen tjeneste?
Det var nittitallet, vi overlevde så godt vi kunne. I 2019 utviklet vi API First Customer Data Platform kSense, som gjorde det mulig å samle data fra forskjellige kilder (Facebook-annonser, Stripe, Salesforce, Google play, Google Analytics, etc.) for mer praktisk dataanalyse, identifisering av avhengigheter osv. Vi har lagt merke til at mange brukere bruker vår plattform for dataanalyse spesifikt Google Analytics (heretter GA). Vi snakket med noen brukere og fant ut at de trenger analysedataene for produktet deres som de mottar ved hjelp av GA, men
De installerte en Segment-javascript-piksel på nettressursen deres, og data om oppførselen til brukerne deres ble lastet inn i den spesifiserte databasen (for eksempel Postgres). Men Segment har også sin bakside – prisen. For eksempel, hvis en nettressurs har 90,000 1,000 MTU (månedlig sporede brukere), må du betale ~XNUMX XNUMX $ per måned til kassereren. Det var også et tredje problem - noen nettleserutvidelser (som AdBlock) blokkerte innsamlingen av analyser fordi... http-forespørsler fra nettleseren ble sendt til GA- og Segment-domenene. Basert på ønsker fra våre kunder har vi laget en analysetjeneste som samler inn et komplett sett med data (uten prøvetaking), er gratis og kan jobbe på vår egen infrastruktur.
Hvordan tjenesten fungerer
Tjenesten består av tre deler: en javascript-piksel (som vi senere skrev om i typescript), serverdelen er implementert i GO-språket, og det var planlagt å bruke Redshift og BigQuery som en intern database (senere la de til støtte for Postgres, ClickHouse og Snowflake).
Det ble besluttet å forlate strukturen til GA- og segmenthendelsene uendret. Alt som var nødvendig var å duplisere alle hendelser fra nettressursen der pikselen er installert til vår backend. Som det viser seg, er dette ikke vanskelig å gjøre. Javascript-pikselen overstyrte den originale GA-bibliotekmetoden med en ny, som dupliserte hendelsen inn i systemet vårt.
//'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);
});
});
}
Med Segment-pikselen er alt enklere; den har mellomvaremetoder, hvorav en vi brukte.
//'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.');
}
I tillegg til å kopiere hendelser, har vi lagt til muligheten til å sende vilkårlig 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'
});
La oss deretter snakke om serverdelen. Backend bør godta http-forespørsler, fylle dem med tilleggsinformasjon, for eksempel geodata (takk
//входящий 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"
}
Imidlertid er arrays for øyeblikket ganske enkelt konvertert til strenger fordi Ikke alle relasjonsdatabaser støtter gjentatte felt. Det er også mulig å endre feltnavn eller slette dem ved hjelp av valgfrie kartleggingsregler. De lar deg endre dataskjemaet om nødvendig eller konvertere en datatype til en annen. For eksempel, hvis et json-felt inneholder en streng med tidsstempel (field_3_sub_field_1_sub_sub_field_1 fra eksempelet ovenfor), så for å lage et felt i databasen med tidsstempeltypen, må du skrive en tilordningsregel i konfigurasjonen. Med andre ord, datatypen til feltet bestemmes først av json-verdien, og deretter brukes type-casting-regelen (hvis konfigurert). Vi har identifisert 4 hoveddatatyper: STRING, FLOAT64, INT64 og TIMESTAMP. Kartleggings- og typecasting-reglene ser slik ut:
rules:
- "/field_1/subfield_1 -> " #правило удаления поля
- "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
- "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа
Algoritme for å bestemme datatypen:
- konverter json-struktur til flat struktur
- bestemme datatypen for felt ved hjelp av verdier
- anvendelse av kartleggings- og typestøpingsregler
Så fra den innkommende json-strukturen:
{
"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"
}
}
dataskjemaet vil bli innhentet:
"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
Vi tenkte også at brukeren skulle kunne konfigurere partisjonering eller dele data i databasen i henhold til andre kriterier og implementerte muligheten til å sette tabellnavnet med en konstant eller
tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'
Strukturen til innkommende hendelser kan imidlertid endres under kjøring. Vi har implementert en algoritme for å sjekke forskjellen mellom strukturen til en eksisterende tabell og strukturen til en innkommende hendelse. Hvis en forskjell blir funnet, vil tabellen bli oppdatert med nye felt. For å gjøre dette, bruk patch SQL-spørringen:
#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying
arkitektur
Hvorfor trenger du å skrive hendelser til filsystemet, og ikke bare skrive dem direkte til databasen? Databaser gir ikke alltid gode resultater når de håndterer et stort antall innlegg (
Åpen kildekode og planer for fremtiden
På et tidspunkt begynte tjenesten å se ut som et fullverdig produkt, og vi bestemte oss for å gi den ut til åpen kildekode. For tiden er integrasjoner med Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake implementert. Alle integrasjoner støtter både batch- og streamingmoduser for datalasting. Lagt til støtte for forespørsler via API.
Dagens integreringsordning ser slik ut:
Selv om tjenesten kan brukes uavhengig (for eksempel ved hjelp av Docker), har vi også
→
→
→
Vi vil være glade hvis EventNative hjelper deg med å løse dine problemer!
Kun registrerte brukere kan delta i undersøkelsen.
Hvilket statistikkinnsamlingssystem brukes i din bedrift?
-
48,0%Google Analytics12
-
4,0%Segment 1
-
16,0%En annen (skriv i kommentarfeltet)4
-
32,0%Implementerte tjenesten din8
25 brukere stemte. 6 brukere avsto.
Kilde: www.habr.com