Historia e burimit tonë të hapur: si e bëmë një shërbim analitik në Go dhe e bëmë atë të disponueshëm publikisht

Aktualisht, pothuajse çdo kompani në botë mbledh statistika në lidhje me veprimet e përdoruesve në një burim në internet. Motivimi është i qartë - kompanitë duan të dinë se si përdoret produkti/faqja e tyre e internetit dhe të kuptojnë më mirë përdoruesit e tyre. Sigurisht, ka një numër të madh mjetesh në treg për të zgjidhur këtë problem - nga sistemet analitike që ofrojnë të dhëna në formën e tabelave dhe grafikëve (për shembull Google Analytics) te Platforma e të Dhënave të Klientit, e cila ju lejon të grumbulloni dhe grumbulloni të dhëna nga burime të ndryshme në çdo depo (për shembull Segment).

Por ne gjetëm një problem që ende nuk është zgjidhur. Kështu lindi NgjarjeNative — shërbim analitik me burim të hapur. Lexoni se përse vendosëm të zhvillonim shërbimin tonë, çfarë na dha ai dhe cili ishte rezultati përfundimtar (me copa kodi).

Historia e burimit tonë të hapur: si e bëmë një shërbim analitik në Go dhe e bëmë atë të disponueshëm publikisht

Pse duhet të zhvillojmë shërbimin tonë?

Ishin vitet nëntëdhjetë, mbijetuam sa më mirë. 2019, ne zhvilluam platformën e parë të të dhënave të klientit API kSense, i cili bëri të mundur grumbullimin e të dhënave nga burime të ndryshme (reklama në Facebook, Stripe, Salesforce, Google play, Google Analytics, etj.) për analiza më të përshtatshme të të dhënave, identifikimin e varësive, etj. Ne kemi vënë re se shumë përdorues përdorin platformën tonë për analizën e të dhënave, veçanërisht Google Analytics (në tekstin e mëtejmë GA). Ne biseduam me disa përdorues dhe zbuluam se ata kanë nevojë për të dhënat analitike për produktin e tyre që marrin duke përdorur GA, por Google ka mostra të të dhënave dhe për shumë njerëz, ndërfaqja e përdoruesit GA nuk është standardi i komoditetit. Ne patëm mjaftueshëm biseda me përdoruesit tanë dhe kuptuam se shumë po përdornin gjithashtu platformën Segment (e cila, nga rruga, ishte vetëm një ditë tjetër shitur për 3.2 miliardë dollarë).

Ata instaluan një piksel javascript të Segmentit në burimin e tyre të internetit dhe të dhënat në lidhje me sjelljen e përdoruesve të tyre u ngarkuan në bazën e të dhënave të specifikuar (për shembull Postgres). Por Segmenti gjithashtu ka anën e tij negative - çmimin. Për shembull, nëse një burim në internet ka 90,000 MTU (përdorues të gjurmuar mujor), atëherë duhet të paguani ~ 1,000 $ në muaj te arkëtari. Kishte gjithashtu një problem të tretë - disa shtesa të shfletuesit (si AdBlock) bllokuan mbledhjen e analitikëve sepse... Kërkesat http nga shfletuesi u dërguan në domenet GA dhe Segment. Bazuar në dëshirat e klientëve tanë, ne kemi krijuar një shërbim analitik që mbledh një grup të plotë të dhënash (pa kampionim), është falas dhe mund të funksionojë në infrastrukturën tonë.

Si funksionon shërbimi

Shërbimi përbëhet nga tre pjesë: një pixel javascript (të cilin më vonë e rishkruam me shkrim shkrimi), pjesa e serverit zbatohet në gjuhën GO dhe ishte planifikuar të përdorej Redshift dhe BigQuery si një bazë të dhënash të brendshme (më vonë ata shtuan mbështetje për Postgres, ClickHouse dhe Snowflake).

U vendos që të lihej e pandryshuar struktura e GA dhe e ngjarjeve të Segmentit. Gjithçka që duhej ishte të kopjoheshin të gjitha ngjarjet nga burimi i uebit ku është instaluar piksel në fundin tonë. Siç rezulton, kjo nuk është e vështirë për t'u bërë. Pixeli Javascript ka anuluar metodën origjinale të bibliotekës GA me një të re, e cila e dyfishoi ngjarjen në sistemin tonë.

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

Me pikselin Segment çdo gjë është më e thjeshtë; ai ka metoda të softuerit të mesëm, një prej të cilave ne kemi përdorur.


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

Përveç kopjimit të ngjarjeve, ne kemi shtuar mundësinë për të dërguar json arbitrare:


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

Më pas, le të flasim për pjesën e serverit. Backend duhet të pranojë kërkesat http, t'i plotësojë ato me informacione shtesë, për shembull, të dhëna gjeo (faleminderit maxmind për këtë) dhe regjistrojeni atë në bazën e të dhënave. Ne donim ta bënim shërbimin sa më të përshtatshëm në mënyrë që të mund të përdoret me konfigurim minimal. Ne kemi zbatuar funksionalitetin e përcaktimit të skemës së të dhënave bazuar në strukturën e ngjarjes hyrëse json. Llojet e të dhënave përcaktohen me vlera. Objektet e mbivendosura zbërthehen dhe reduktohen në një strukturë të sheshtë:

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

Megjithatë, vargjet aktualisht konvertohen thjesht në vargje sepse Jo të gjitha bazat e të dhënave relacionale mbështesin fusha të përsëritura. Është gjithashtu e mundur të ndryshoni emrat e fushave ose t'i fshini ato duke përdorur rregullat opsionale të hartës. Ato ju lejojnë të ndryshoni skemën e të dhënave nëse është e nevojshme ose të konvertoni një lloj të dhënash në një tjetër. Për shembull, nëse një fushë json përmban një varg me vulën kohore (fusha_3_nënfusha_1_nënfusha_1 nga shembulli i mësipërm), atëherë për të krijuar një fushë në bazën e të dhënave me llojin e vulës kohore, duhet të shkruani një rregull të hartës në konfigurim. Me fjalë të tjera, lloji i të dhënave të fushës përcaktohet së pari nga vlera json, dhe më pas zbatohet rregulli i hedhjes së tipit (nëse është konfiguruar). Ne kemi identifikuar 4 lloje kryesore të të dhënave: STRING, FLOAT64, INT64 dhe TIMESTAMP. Rregullat e hartës dhe hedhjes së tipit duken kështu:

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

Algoritmi për përcaktimin e llojit të të dhënave:

  • konvertoni strukturën json në strukturë të sheshtë
  • përcaktimi i llojit të të dhënave të fushave sipas vlerave
  • duke zbatuar rregullat e hedhjes së hartës dhe tipit

Pastaj nga struktura hyrëse json:

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

do të merret skema e të dhënave:

"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

Ne gjithashtu menduam se përdoruesi duhet të jetë në gjendje të konfigurojë ndarjen ose ndarjen e të dhënave në bazën e të dhënave sipas kritereve të tjera dhe zbatuam aftësinë për të vendosur emrin e tabelës me një konstante ose shprehje në konfigurim. Në shembullin më poshtë, ngjarja do të ruhet në një tabelë me një emër të llogaritur bazuar në vlerat e fushave product_type dhe _timestamp (për shembull furnizimet_2020_10):

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

Megjithatë, struktura e ngjarjeve në hyrje mund të ndryshojë në kohën e ekzekutimit. Ne kemi zbatuar një algoritëm për të kontrolluar ndryshimin midis strukturës së një tabele ekzistuese dhe strukturës së një ngjarjeje hyrëse. Nëse gjendet një ndryshim, tabela do të përditësohet me fusha të reja. Për ta bërë këtë, përdorni pyetjen patch SQL:

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

Arkitekturë

Historia e burimit tonë të hapur: si e bëmë një shërbim analitik në Go dhe e bëmë atë të disponueshëm publikisht

Pse keni nevojë të shkruani ngjarje në sistemin e skedarëve, dhe jo thjesht t'i shkruani ato drejtpërdrejt në bazën e të dhënave? Bazat e të dhënave nuk funksionojnë gjithmonë mirë kur kemi të bëjmë me një numër të madh insertesh (Rekomandimet e Postgres). Për ta bërë këtë, Logger shkruan ngjarjet hyrëse në një skedar dhe në një gorutinë të veçantë (fije) Lexuesi i skedarit lexon skedarin, më pas të dhënat konvertohen dhe përcaktohen. Pasi menaxheri i tabelës të sigurohet që skema e tabelës është e përditësuar, të dhënat do të shkruhen në bazën e të dhënave në një grup. Më pas, ne shtuam mundësinë për të shkruar të dhëna direkt në bazën e të dhënave, por ne e përdorim këtë mënyrë për ngjarje që nuk janë të shumta - për shembull, konvertimet.

Burimi i hapur dhe planet për të ardhmen

Në një moment, shërbimi filloi të dukej si një produkt i plotë dhe ne vendosëm ta lëshojmë atë në Open Source. Aktualisht, janë zbatuar integrimet me Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Të gjitha integrimet mbështesin mënyrat e ngarkimit të të dhënave si në grup ashtu edhe në transmetim. Mbështetje e shtuar për kërkesat përmes API.

Skema aktuale e integrimit duket si kjo:

Historia e burimit tonë të hapur: si e bëmë një shërbim analitik në Go dhe e bëmë atë të disponueshëm publikisht

Edhe pse shërbimi mund të përdoret në mënyrë të pavarur (për shembull duke përdorur Docker), ne gjithashtu kemi versioni i pritur, në të cilin mund të konfiguroni integrimin me një depo të dhënash, të shtoni një CNAME në domenin tuaj dhe të shikoni statistikat për numrin e ngjarjeve. Planet tona të menjëhershme janë të shtojmë aftësinë për të grumbulluar jo vetëm statistika nga një burim ueb, por edhe të dhëna nga burime të jashtme të të dhënave dhe t'i ruajmë ato në çdo ruajtje sipas zgjedhjes suaj!

→ GitHub
→ Records
→ I plogët

Do të jemi të lumtur nëse EventNative ndihmon në zgjidhjen e problemeve tuaja!

Vetëm përdoruesit e regjistruar mund të marrin pjesë në anketë. Hyni, te lutem

Cili sistem i mbledhjes së statistikave përdoret në kompaninë tuaj?

  • 48,0%Google Analytics12

  • 4,0%Segmenti 1

  • 16,0%Një tjetër (shkruani në komente)4

  • 32,0%Zbatoi shërbimin tuaj8

25 përdorues votuan. 6 përdorues abstenuan.

Burimi: www.habr.com

Shto një koment