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,
Pero atopamos un problema que aínda non foi resolto. Así naceu
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
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
//входящий 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
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
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 (
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índa que o servizo se pode usar de forma independente (por exemplo, usando Docker), tamén o temos
→
→
→
Estaremos encantados de que EventNative che axude a resolver os teus problemas.
Só os usuarios rexistrados poden participar na enquisa.
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