Istorija našeg otvorenog koda: kako smo napravili analitičku uslugu u Go-u i učinili je javno dostupnim

Trenutno skoro svaka kompanija na svijetu prikuplja statistiku o radnjama korisnika na web resursu. Motivacija je jasna - kompanije žele znati kako se njihov proizvod/web stranica koristi i bolje razumjeti svoje korisnike. Naravno, na tržištu postoji veliki broj alata za rješavanje ovog problema – od analitičkih sistema koji daju podatke u obliku kontrolne table i grafikona (npr. Google Analytics) na Platformu podataka o kupcima, koja vam omogućava da prikupljate i agregirate podatke iz različitih izvora u bilo kojem skladištu (npr. segment).

Ali pronašli smo problem koji još nije riješen. Tako je rođen EventNative — analitička usluga otvorenog koda. Pročitajte zašto smo odlučili da razvijemo sopstveni servis, šta nam je to dalo i kakav je bio krajnji rezultat (sa delovima koda).

Istorija našeg otvorenog koda: kako smo napravili analitičku uslugu u Go-u i učinili je javno dostupnim

Zašto bismo trebali razviti vlastitu uslugu?

Bile su devedesete, preživljavali smo kako smo mogli. 2019. razvili smo API First Customer Data Platform kSense, što je omogućilo agregiranje podataka iz različitih izvora (Facebook oglasi, Stripe, Salesforce, Google play, Google Analytics, itd.) za praktičniju analizu podataka, identifikaciju zavisnosti itd. Primijetili smo da mnogi korisnici koriste našu platformu za analizu podataka posebno Google Analytics (u daljem tekstu GA). Razgovarali smo s nekim korisnicima i saznali da su im potrebni analitički podaci za njihov proizvod koje dobijaju putem GA, ali Google uzorkuje podatke i za mnoge, GA korisnički interfejs nije standard pogodnosti. Imali smo dovoljno razgovora sa našim korisnicima i shvatili da mnogi koriste i platformu Segment (koja je, inače, tek neki dan prodat za 3.2 milijarde dolara).

Instalirali su Segment javascript piksel na svoj web resurs i podaci o ponašanju njihovih korisnika su učitani u navedenu bazu podataka (na primjer Postgres). Ali Segment ima i svoju lošu stranu - cijenu. Na primjer, ako web resurs ima 90,000 MTU (mjesečno praćenih korisnika), onda morate plaćati ~1,000 $ mjesečno blagajni. Postojao je i treći problem - neke ekstenzije pretraživača (kao što je AdBlock) blokirale su prikupljanje analitike jer... http zahtjevi iz pretraživača su poslani na domene GA i segmenta. Na osnovu želja naših klijenata kreirali smo uslugu analitike koja prikuplja kompletan set podataka (bez uzorkovanja), besplatna je i može raditi na vlastitoj infrastrukturi.

Kako servis funkcioniše

Servis se sastoji iz tri dela: javascript piksela (koji smo kasnije prepisali u kucani tekst), serverski deo je implementiran u GO jeziku, a planirano je da se kao interna baza podataka koriste Redshift i BigQuery (kasnije su dodali podršku za Postgres, ClickHouse i Snowflake).

Odlučeno je da se struktura GA i Segment događaja ostavi nepromijenjena. Sve što je bilo potrebno je duplirati sve događaje iz web resursa gdje je piksel instaliran na naš backend. Kako se ispostavilo, to nije teško učiniti. Javascript piksel je nadjačao originalnu GA metodu biblioteke novom, koja je duplirala događaj u našem sistemu.

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

Sa segmentnim pikselom sve je jednostavnije; on ima međuverske metode, od kojih smo jednu koristili.


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

Pored kopiranja događaja, dodali smo i mogućnost slanja proizvoljnog json-a:


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

Dalje, hajde da pričamo o serverskom delu. Backend bi trebao prihvatiti http zahtjeve, ispuniti ih dodatnim informacijama, na primjer, geografskim podacima (hvala maxmind za ovo) i zabilježite ga u bazi podataka. Željeli smo uslugu učiniti što praktičnijom kako bi se mogla koristiti uz minimalnu konfiguraciju. Implementirali smo funkcionalnost određivanja šeme podataka na osnovu strukture dolaznog json događaja. Tipovi podataka su definirani vrijednostima. Ugniježđeni objekti se razlažu i svode na ravnu strukturu:

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

Međutim, nizovi se trenutno jednostavno pretvaraju u nizove jer Ne podržavaju sve relacijske baze podataka ponovljena polja. Također je moguće promijeniti nazive polja ili ih izbrisati korištenjem opcijskih pravila mapiranja. Oni vam omogućavaju da promijenite šemu podataka ako je potrebno ili pretvorite jedan tip podataka u drugi. Na primjer, ako json polje sadrži niz s vremenskom oznakom (field_3_sub_field_1_sub_sub_field_1 iz gornjeg primjera), tada da biste kreirali polje u bazi podataka s tipom vremenske oznake, trebate napisati pravilo mapiranja u konfiguraciji. Drugim riječima, tip podataka polja je određen najprije json vrijednošću, a zatim se primjenjuje pravilo određivanja tipa (ako je konfigurirano). Identificirali smo 4 glavna tipa podataka: STRING, FLOAT64, INT64 i TIMESTAMP. Pravila mapiranja i ubacivanja tipova izgledaju ovako:

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

Algoritam za određivanje tipa podataka:

  • pretvoriti json strukturu u ravnu strukturu
  • određivanje tipa podataka polja po vrijednostima
  • primjena pravila mapiranja i tipova

Zatim iz dolazne json strukture:

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

dobiće se šema podataka:

"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

Također smo smatrali da bi korisnik trebao biti u mogućnosti da konfigurira particioniranje ili podjelu podataka u bazi podataka prema drugim kriterijima i implementirali smo mogućnost postavljanja imena tablice sa konstantom ili izraz u konfiguraciji. U primjeru ispod, događaj će biti spremljen u tablicu s imenom izračunatim na osnovu vrijednosti polja product_type i _timestamp (na primjer zalihe_2020_10):

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

Međutim, struktura dolaznih događaja može se promijeniti u vremenu izvođenja. Implementirali smo algoritam za provjeru razlike između strukture postojeće tabele i strukture dolaznog događaja. Ako se pronađe razlika, tabela će biti ažurirana novim poljima. Da biste to učinili, koristite SQL upit zakrpe:

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

arhitektura

Istorija našeg otvorenog koda: kako smo napravili analitičku uslugu u Go-u i učinili je javno dostupnim

Zašto trebate pisati događaje u sistem datoteka, a ne samo ih pisati direktno u bazu podataka? Baze podataka ne rade uvijek dobro kada se bave velikim brojem umetanja (Postgres preporuke). Da bi to uradio, Logger upisuje dolazne događaje u datoteku i u zasebnoj goroutini (thread) Čitač datoteka čita datoteku, zatim se podaci konvertuju i određuju. Nakon što se upravitelj tablica uvjeri da je šema tablice ažurna, podaci će biti upisani u bazu podataka u jednom paketu. Naknadno smo dodali mogućnost pisanja podataka direktno u bazu podataka, ali ovaj način koristimo za događaje koji nisu brojni - na primjer, konverzije.

Open Source i planovi za budućnost

U nekom trenutku usluga je počela izgledati kao punopravni proizvod i odlučili smo da je pustimo na Open Source. Trenutno su implementirane integracije sa Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Sve integracije podržavaju i paketni i streaming način učitavanja podataka. Dodata podrška za zahtjeve putem API-ja.

Trenutna šema integracije izgleda ovako:

Istorija našeg otvorenog koda: kako smo napravili analitičku uslugu u Go-u i učinili je javno dostupnim

Iako se usluga može koristiti samostalno (na primjer korištenjem Dockera), imamo i mi hostovana verzija, u kojem možete postaviti integraciju sa skladištem podataka, dodati CNAME svojoj domeni i pregledati statistiku o broju događaja. Naši neposredni planovi su da dodamo mogućnost agregiranja ne samo statistike sa web resursa, već i podataka iz eksternih izvora podataka i spremanje ih u bilo koju memoriju po vašem izboru!

→ GitHub
→ Dokumentacija
→ zatišje

Bit će nam drago ako EventNative pomogne u rješavanju vaših problema!

Samo registrovani korisnici mogu učestvovati u anketi. Prijavite semolim.

Koji sistem prikupljanja statistike se koristi u vašoj kompaniji?

  • 48,0%Google Analytics12

  • 4,0%Segment1

  • 16,0%Još jedan (napišite u komentarima)4

  • 32,0%Implementirao vaš servis8

Glasalo je 25 korisnika. Uzdržano je bilo 6 korisnika.

izvor: www.habr.com

Dodajte komentar