I øjeblikket indsamler næsten alle virksomheder i verden statistik om brugerhandlinger på en webressource. Motivationen er klar – virksomheder vil gerne vide, hvordan deres produkt/hjemmeside bruges og bedre forstå deres brugere. Der findes naturligvis en lang række værktøjer på markedet til at løse dette problem - lige fra analysesystemer, der leverer data i form af dashboards og grafer (f.eks.
Men vi har fundet et problem, der ikke er løst endnu. Således blev født
Hvorfor skal vi udvikle vores egen service?
Det var halvfemserne, vi overlevede så godt vi kunne. 2019 udviklede vi API First Customer Data Platform kSense, som gjorde det muligt at aggregere data fra forskellige kilder (Facebook-annoncer, Stripe, Salesforce, Google play, Google Analytics osv.) for mere bekvem dataanalyse, identifikation af afhængigheder mv. Vi har bemærket, at mange brugere bruger vores platform til dataanalyse specifikt Google Analytics (herefter GA). Vi talte med nogle brugere og fandt ud af, at de har brug for analysedata for deres produkt, som de modtager ved hjælp af GA, men
De installerede en Segment-javascript-pixel på deres webressource, og data om deres brugeres adfærd blev indlæst i den angivne database (for eksempel Postgres). Men Segment har også sin bagside – prisen. For eksempel, hvis en webressource har 90,000 MTU (månedligt sporede brugere), så skal du betale ~1,000 $ om måneden til kassereren. Der var også et tredje problem - nogle browserudvidelser (såsom AdBlock) blokerede indsamlingen af analyser, fordi... http-anmodninger fra browseren blev sendt til GA- og Segment-domænerne. Baseret på vores kunders ønsker har vi skabt en analyseservice, der indsamler et komplet sæt data (uden stikprøver), er gratis og kan arbejde på vores egen infrastruktur.
Sådan fungerer tjenesten
Tjenesten består af tre dele: en javascript-pixel (som vi senere omskrev i typescript), serverdelen er implementeret i GO-sproget, og det var planlagt at bruge Redshift og BigQuery som en intern database (senere tilføjede de support til Postgres, ClickHouse og Snowflake).
Det blev besluttet at lade strukturen af GA- og segmentbegivenhederne være uændret. Det eneste, der skulle til, var at duplikere alle hændelser fra webressourcen, hvor pixlen er installeret, til vores backend. Som det viser sig, er dette ikke svært at gøre. Javascript-pixlen tilsidesatte den originale GA-biblioteksmetode med en ny, som duplikerede hændelsen i vores system.
//'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-pixlen er alt enklere; den har middleware-metoder, hvoraf vi brugte en.
//'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.');
}
Ud over at kopiere hændelser har vi tilføjet muligheden for at 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'
});
Lad os derefter tale om serverdelen. Backend bør acceptere http-anmodninger, udfylde dem med yderligere oplysninger, for eksempel geodata (tak
//входящий 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 i øjeblikket simpelthen konverteret til strenge, fordi Ikke alle relationelle databaser understøtter gentagne felter. Det er også muligt at ændre feltnavne eller slette dem ved hjælp af valgfri kortlægningsregler. De giver dig mulighed for at ændre dataskemaet om nødvendigt eller konvertere en datatype til en anden. For eksempel, hvis et json-felt indeholder en streng med tidsstempel (field_3_sub_field_1_sub_sub_field_1 fra eksemplet ovenfor), for at oprette et felt i databasen med tidsstempeltypen, skal du skrive en mapping-regel i konfigurationen. Med andre ord bestemmes feltets datatype først af json-værdien, og derefter anvendes type-casting-reglen (hvis den er konfigureret). Vi har identificeret 4 hoveddatatyper: STRING, FLOAT64, INT64 og TIMESTAMP. Kortlægnings- og typecasting-reglerne ser sådan ud:
rules:
- "/field_1/subfield_1 -> " #правило удаления поля
- "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
- "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа
Algoritme til bestemmelse af datatypen:
- konverter json-struktur til flad struktur
- bestemme datatypen for felter ved hjælp af værdier
- anvendelse af kortlægning og typestøbningsregler
Så fra den indgående json-struktur:
{
"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"
}
}
dataskemaet vil blive indhentet:
"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 tænkte også, at brugeren skulle være i stand til at konfigurere partitionering eller opdele data i databasen efter andre kriterier og implementerede muligheden for at sætte tabelnavnet med en konstant eller
tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'
Strukturen af indgående hændelser kan dog ændre sig under kørsel. Vi har implementeret en algoritme til at kontrollere forskellen mellem strukturen af en eksisterende tabel og strukturen af en indkommende begivenhed. Hvis der findes en forskel, vil tabellen blive opdateret med nye felter. For at gøre dette skal du bruge patch SQL-forespørgslen:
#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying
arkitektur
Hvorfor skal du skrive hændelser til filsystemet, og ikke bare skrive dem direkte til databasen? Databaser fungerer ikke altid godt, når de håndterer et stort antal indstik (
Open Source og planer for fremtiden
På et tidspunkt begyndte tjenesten at ligne et fuldgyldigt produkt, og vi besluttede at frigive den til Open Source. I øjeblikket er integrationer med Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake blevet implementeret. Alle integrationer understøtter både batch- og streaming-tilstande til dataindlæsning. Tilføjet support til anmodninger via API.
Den nuværende integrationsordning ser således ud:
Selvom tjenesten kan bruges uafhængigt (for eksempel ved hjælp af Docker), har vi også
Vi vil blive glade, hvis EventNative hjælper med at løse dine problemer!
Kun registrerede brugere kan deltage i undersøgelsen.
Hvilket statistikindsamlingssystem bruges i din virksomhed?
-
48,0 %Google Analytics12
-
4,0 %Segment 1
-
16,0 %En anden (skriv i kommentarerne)4
-
32,0 %Implementeret din service8
25 brugere stemte. 6 brugere undlod at stemme.
Kilde: www.habr.com