Op it stuit sammelet hast elk bedriuw yn 'e wrâld statistiken oer brûkersaksjes op in webboarne. De motivaasje is dúdlik - bedriuwen wolle witte hoe't har produkt / webside wurdt brûkt en har brûkers better begripe. Fansels binne d'r in grut oantal ark op 'e merke om dit probleem op te lossen - fan analytyske systemen dy't gegevens leverje yn' e foarm fan dashboards en grafiken (bygelyks,
Mar wy hawwe in probleem fûn dat noch net oplost is. Sa berne
Wêrom moatte wy ús eigen tsjinst ûntwikkelje?
It wiene de jierren njoggentich, wy oerlibbe sa goed as wy koenen. 2019 hawwe wy de First Customer Data Platform API ûntwikkele kSense, wêrtroch it sammeljen fan gegevens út ferskate boarnen (Facebook-advertinsjes, Stripe, Salesforce, Google play, Google Analytics, ensfh.) Wy hawwe opmurken dat in protte brûkers ús data analytics platfoarm brûke, spesifyk Google Analytics (hjirnei oantsjutten as GA). Wy sprieken mei guon brûkers en fûn út dat se nedich harren produkt analytics gegevens, dy't se ûntfange mei help fan GA, mar
Se ynstalleare in Segment javascript-pixel op har webboarne en har brûkersgedrachsgegevens waarden laden yn in spesifisearre database (bgl. Postgres). Mar Segment hat ek syn minus - de priis. Bygelyks, as in webboarne 90,000 MTU hat (moanlikse tracked brûkers), dan moatte jo ~ $ 1,000 per moanne betelje oan de kassier. D'r wie ek in tredde probleem - guon browser-útwreidings (lykas AdBlock) blokkearren de kolleksje fan analytiken. http-oanfragen fan 'e browser waarden stjoerd nei de GA- en Segment-domeinen. Op grûn fan 'e winsk fan ús kliïnten hawwe wy in analytyske tsjinst makke dy't in folsleine set gegevens sammelt (sûnder sampling), fergees en kin wurkje oan ús eigen ynfrastruktuer.
Hoe't de tsjinst wurket
De tsjinst bestiet út trije dielen: in javascript-piksel (dy't wy letter omskreaun hawwe nei typescript), in serverdiel ymplementearre yn 'e GO-taal, en it wie pland om Redshift en BigQuery te brûken as in eigen databank (letter hawwe se stipe tafoege foar Postgres , ClickHouse en Snowflake).
De struktuer fan eveneminten GA en Segment besletten om te ferlitten ûnferoare. Alles wat nedich wie wie om alle eveneminten te duplikearjen fan 'e webboarne wêr't de piksel is ynstalleare nei ús efterkant. As it docht bliken, is dit maklik te dwaan. De Javascript-piksel hat de orizjinele GA-bibleteekmetoade oerskreaun mei in nije dy't it evenemint yn ús systeem duplikearre.
//'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);
});
});
}
Mei de Segment-piksel is alles ienfâldiger, it hat middelware-metoaden, en wy hawwe ien fan har brûkt.
//'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.');
}
Neist it kopiearjen fan eveneminten hawwe wy de mooglikheid tafoege om willekeurige json te stjoeren:
//Отправка событий с произвольным 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'
});
Lit ús dan prate oer de serverkant. De backend moat http-oanfragen akseptearje, folje se mei oanfoljende ynformaasje, bygelyks geodata (tank
//входящий 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"
}
Arrays wurde op it stuit lykwols gewoan omboud ta strings. net alle relasjonele databases stypje werhelle fjilden. It is ek mooglik om fjildnammen te feroarjen of te ferwiderjen mei opsjonele mappingregels. Se tastean jo te feroarjen it gegevens skema, as it nedich is, of cast ien gegevens type nei in oar. Bygelyks, as it json-fjild in tekenrige befettet mei in tiidstempel (field_3_sub_field_1_sub_sub_field_1 út it foarbyld hjirboppe), dan moatte jo in mappingregel skriuwe yn 'e konfiguraasje om in fjild yn' e database te meitsjen mei it tiidstempeltype. Mei oare wurden, it gegevenstype fan it fjild wurdt earst bepaald troch de json-wearde, en dan wurdt de type casting-regel (as ynsteld) tapast. Wy hawwe 4 haadgegevenstypen identifisearre: STRING, FLOAT64, INT64 en TIMESTAMP. De regels foar mapping en casting sjogge der sa út:
rules:
- "/field_1/subfield_1 -> " #правило удаления поля
- "/field_2/subfield_1 -> /field_10/subfield_1" #правило переноса поля
- "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #правило переноса поля и приведения типа
Algoritme foar it bepalen fan it gegevenstype:
- konvertearje json struct nei flat struct
- it bepalen fan it gegevenstype fan fjilden troch wearden
- it tapassen fan mapping en type casting regels
Dan fan 'e ynkommende json-struktuer:
{
"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"
}
}
gegevensskema sil wurde krigen:
"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
Wy tochten ek dat de brûker partitionearjen of splitsen fan gegevens yn 'e databank neffens oare kritearia soe moatte ynstelle en ymplementearre de mooglikheid om de tabelnamme yn te stellen as in konstante of
tableName: '{{.product_type}}_{{._timestamp.Format "2006_01"}}'
De struktuer fan ynkommende eveneminten kin lykwols feroarje by runtime. Wy hawwe in algoritme ymplementearre foar it kontrolearjen fan it ferskil tusken de struktuer fan in besteande tabel en de struktuer fan in ynkommende evenemint. As in ferskil wurdt fûn, sil de tabel bywurke wurde mei nije fjilden. Om dit te dwaan, brûk de patch SQL-query:
#Пример для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying
arsjitektuer
Wêrom moatte jo eveneminten skriuwe nei it bestânsysteem, en net gewoan direkt nei de databank skriuwe? Databanken litte net altyd hege prestaasjes sjen mei in grut oantal ynserts (
Iepen boarne en takomstplannen
Op in stuit waard de tsjinst as in folweardich produkt en wy besletten it yn Open Source te pleatsen. Op it stuit binne yntegraasjes mei Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake ymplementearre. Alle yntegraasjes stypje sawol batch- as streamende gegevensladingsmodi. Stipe tafoege foar oanfragen fia API.
It hjoeddeiske yntegraasjeskema sjocht der sa út:
Hoewol de tsjinst selsstannich kin wurde brûkt (bygelyks mei Docker), hawwe wy ek
→
→
→
Wy sille bliid wêze as EventNative jo sil helpe jo problemen op te lossen!
Allinnich registrearre brûkers kinne meidwaan oan 'e enkête.
Hokker statistyksammelsysteem wurdt brûkt yn jo bedriuw
-
48,0%Google Analytics 12
-
4,0%Segment 1
-
16,0%Oare (skriuw yn 'e opmerkings) 4
-
32,0%Implementearre jo tsjinst8
25 brûkers stimden. 6 brûkers ûntholden har.
Boarne: www.habr.com