A historia do noso código aberto: como fixemos un servizo de análise en Go e o puxemos a disposición do público

Actualmente, case todas as empresas do mundo recollen estatísticas sobre as accións dos usuarios nun recurso web. A motivación é clara: as empresas queren saber como se usa o seu produto/sitio web e comprender mellor os seus usuarios. Por suposto, hai un gran número de ferramentas no mercado para resolver este problema: desde sistemas de análise que proporcionan datos en forma de paneis e gráficos (por exemplo, Google Analytics) á Plataforma de datos do cliente, que lle permiten recoller e agregar datos de diferentes fontes en calquera almacenamento (por exemplo, Segmento).

Pero atopamos un problema que aínda non foi resolto. Así naceu EventNative - Servizo de análise de código aberto. Sobre por que fomos desenvolver o noso propio servizo, o que nos deu e o que pasou ao final (con anacos de código), ler baixo o corte.

A historia do noso código aberto: como fixemos un servizo de análise en Go e o puxemos a disposición do público

Por que debemos desenvolver o noso propio servizo?

Eran os anos noventa, sobrevivimos como puidemos. En 2019, desenvolvemos a API First Customer Data Platform kSense, que permitiu agregar datos de diferentes fontes (anuncios de Facebook, Stripe, Salesforce, Google play, Google Analytics, etc.) para unha análise de datos máis cómoda, identificación de dependencias, etc. Observamos que moitos usuarios usan a nosa plataforma de análise de datos, en concreto Google Analytics (en diante, GA). Falamos con algúns usuarios e descubrimos que necesitan os seus datos de análise de produtos, que reciben mediante GA, pero Google mostra datos e para moitos a interface de usuario de GA non é un estándar de conveniencia. Mantivemos bastantes conversas cos nosos usuarios e decatámonos de que moitos tamén usaban a plataforma Segment (que, por certo, foi hai só uns días). vendido por 3.2 millóns de dólares).

Instalaron un píxel de javascript de segmento no seu recurso web e os datos do comportamento do usuario cargáronse nunha base de datos especificada (por exemplo, Postgres). Pero o segmento tamén ten o seu inconveniente: o prezo. Por exemplo, se un recurso web ten 90,000 MTU (usuarios de seguimento mensual), entón tes que pagar ~ $ 1,000 ao mes ao caixeiro. Tamén houbo un terceiro problema: algunhas extensións do navegador (como AdBlock) bloquearon a recollida de análises. As solicitudes http do navegador enviáronse aos dominios GA e Segment. Partindo do desexo dos nosos clientes, creamos un servizo de análise que recolle un conxunto completo de datos (sen mostraxe), de xeito gratuíto e pode traballar na nosa propia infraestrutura.

Como funciona o servizo

O servizo consta de tres partes: un píxel de javascript (que máis tarde reescribimos a máquina), unha parte do servidor implementada na linguaxe GO e estaba previsto utilizar Redshift e BigQuery como base de datos interna (máis tarde engadiron soporte para Postgres). , ClickHouse e Snowflake).

A estrutura dos eventos GA e Segmento decidiron deixar sen cambios. Todo o que facía falta era duplicar todos os eventos do recurso web onde está instalado o píxel ata o noso backend. Como se ve, isto é doado de facer. O píxel de Javascript anulou o método orixinal da biblioteca de GA cun novo que duplicou o evento no noso sistema.

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

Todo é máis sinxelo co píxel Segment, ten métodos de middleware e usamos un deles.


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

Ademais de copiar eventos, engadimos a posibilidade de enviar json arbitrario:


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

A continuación, imos falar do lado do servidor. O backend debería aceptar solicitudes http, enchelas con información adicional, por exemplo, xeodatos (grazas maxmind para iso) e escribir na base de datos. Queriamos facer o servizo o máis cómodo posible para que se poida utilizar cunha configuración mínima. Implementamos a funcionalidade de determinar o esquema de datos en función da estrutura do evento json entrante. Os tipos de datos defínense mediante valores. Os obxectos anidados descompoñen e redúcense a unha estrutura plana:

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

Non obstante, actualmente as matrices simplemente convértense en cadeas. non todas as bases de datos relacionais admiten campos repetidos. Tamén é posible cambiar os nomes dos campos ou eliminalos mediante regras de mapeo opcionais. Permítenche cambiar o esquema de datos, se é necesario, ou transmitir un tipo de datos a outro. Por exemplo, se o campo json contén unha cadea cunha marca de tempo (campo_3_sub_campo_1_sub_sub_campo_1 a partir do exemplo anterior), entón para crear un campo na base de datos co tipo de marca de tempo, cómpre escribir unha regra de asignación na configuración. Noutras palabras, o tipo de datos do campo determínase primeiro polo valor json e despois aplícase a regra de conversión de tipos (se está configurada). Identificamos 4 tipos de datos principais: STRING, FLOAT64, INT64 e TIMESTAMP. As regras de mapeo e emisión teñen o seguinte aspecto:

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

Algoritmo para determinar o tipo de datos:

  • converter json struct en flat struct
  • determinando o tipo de datos dos campos por valores
  • aplicando regras de mapeo e de fundición de tipos

A continuación, desde a estrutura json entrante:

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

obterase o esquema de datos:

"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

Tamén pensamos que o usuario debería poder configurar a partición ou dividir os datos na base de datos segundo outros criterios e implementamos a posibilidade de establecer o nome da táboa como unha constante ou expresión en configuración. No seguinte exemplo, o evento gardarase nunha táboa cun nome calculado en función dos valores dos campos product_type e _timestamp (por exemplo subministracións_2020_10):

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

Non obstante, a estrutura dos eventos entrantes pode cambiar no tempo de execución. Implementamos un algoritmo para comprobar a diferenza entre a estrutura dunha táboa existente e a estrutura dun evento entrante. Se se atopa unha diferenza, a táboa actualizarase con novos campos. Para iso, use a consulta de parche SQL:

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

Arquitectura

A historia do noso código aberto: como fixemos un servizo de análise en Go e o puxemos a disposición do público

Por que precisas escribir eventos no sistema de ficheiros e non só escribilos directamente na base de datos? As bases de datos non sempre mostran un alto rendemento cunha gran cantidade de insercións (recomendacións de postgres). Para iso, Logger escribe os eventos entrantes nun ficheiro e xa nunha goroutine separada (fíos) o lector de ficheiros le o ficheiro, despois ten lugar a transformación e definición do esquema de datos. Despois de que o xestor de táboas se asegure de que o esquema da táboa estea actualizado, os datos escribiranse na base de datos nun lote. Posteriormente, engadimos a posibilidade de escribir datos directamente na base de datos, pero usamos este modo para eventos que non son moitos, por exemplo, conversións.

Código aberto e plans de futuro

Nalgún momento, o servizo converteuse nun produto completo e decidimos poñelo en código aberto. Polo momento, implementáronse integracións con Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Todas as integracións admiten os modos de carga de datos por lotes e transmisión de datos. Engadido soporte para solicitudes a través da API.

O esquema de integración actual ten o seguinte aspecto:

A historia do noso código aberto: como fixemos un servizo de análise en Go e o puxemos a disposición do público

Aínda que o servizo se pode usar de forma independente (por exemplo, usando Docker), tamén o temos versión aloxada, onde podes configurar a integración co almacén de datos, engadir un CNAME ao teu dominio e ver estatísticas sobre o número de eventos. Os nosos plans inmediatos son engadir a capacidade de agregar non só estatísticas dun recurso web, senón tamén datos de fontes de datos externas e gardalos no almacenamento que desexes.

→ GitHub
→ Documentación
→ Neglixente

Estaremos encantados de que EventNative che axude a resolver os teus problemas.

Só os usuarios rexistrados poden participar na enquisa. Rexístrate, por favor.

Que sistema de recollida de estatísticas se utiliza na súa empresa

  • 48,0%Google Analytics 12

  • 4,0%Segmento 1

  • 16,0%Outro (escribe nos comentarios) 4

  • 32,0%Implementou o seu servizo8

Votaron 25 usuarios. 6 usuarios abstivéronse.

Fonte: www.habr.com

Engadir un comentario