Sejarah open source kami: bagaimana kami membuat layanan analitik di Go dan membuatnya tersedia untuk umum

Saat ini, hampir setiap perusahaan di dunia mengumpulkan statistik tentang tindakan pengguna pada sumber daya web. Motivasinya jelas - perusahaan ingin mengetahui bagaimana produk/situs web mereka digunakan dan lebih memahami penggunanya. Tentu saja, ada banyak sekali alat di pasaran untuk mengatasi masalah ini - mulai dari sistem analitik yang menyediakan data dalam bentuk dasbor dan grafik (misalnya Google Analytics) ke Platform Data Pelanggan, yang memungkinkan Anda mengumpulkan dan menggabungkan data dari berbagai sumber di penyimpanan mana pun (misalnya, Ruas).

Namun kami menemukan masalah yang belum terpecahkan. Jadi lahirlah Acara Asli β€” layanan analitik sumber terbuka. Baca tentang alasan kami memutuskan untuk mengembangkan layanan kami sendiri, apa manfaatnya bagi kami, dan apa hasil akhirnya (dengan potongan kode).

Sejarah open source kami: bagaimana kami membuat layanan analitik di Go dan membuatnya tersedia untuk umum

Mengapa kita harus mengembangkan layanan kita sendiri?

Saat itu tahun sembilan puluhan, kami bertahan sebaik mungkin. Pada tahun 2019, kami mengembangkan API Platform Data Pelanggan Pertama krasa, yang memungkinkan pengumpulan data dari berbagai sumber (iklan Facebook, Stripe, Salesforce, Google play, Google Analytics, dll.) untuk analisis data yang lebih nyaman, mengidentifikasi ketergantungan, dll. Kami memperhatikan bahwa banyak pengguna menggunakan platform kami untuk analisis data khususnya Google Analytics (selanjutnya disebut GA). Kami berbicara dengan beberapa pengguna dan menemukan bahwa mereka memerlukan data analitik untuk produk yang mereka terima menggunakan GA, namun Data sampel Google dan bagi banyak orang, Antarmuka Pengguna GA bukanlah standar kenyamanan. Kami melakukan cukup banyak percakapan dengan pengguna kami dan menyadari bahwa banyak juga yang menggunakan platform Segmen (yang, kebetulan, baru beberapa hari yang lalu dijual seharga $3.2 miliar).

Mereka memasang piksel javascript Segmen pada sumber daya web mereka dan data perilaku pengguna dimuat ke dalam database tertentu (misalnya Postgres). Tapi Segmen juga punya kekurangannya - harga. Misalnya, jika sumber daya web memiliki 90,000 MTU (pengguna terlacak bulanan), maka Anda harus membayar ~$1,000 per bulan ke kasir. Ada juga masalah ketiga - beberapa ekstensi browser (seperti AdBlock) memblokir pengumpulan analitik. permintaan http dari browser dikirim ke domain GA dan Segmen. Berdasarkan keinginan klien kami, kami telah menciptakan layanan analitik yang mengumpulkan kumpulan data lengkap (tanpa pengambilan sampel), gratis dan dapat bekerja pada infrastruktur kami sendiri.

Bagaimana layanan ini bekerja

Layanan ini terdiri dari tiga bagian: piksel javascript (yang kemudian kami tulis ulang dalam skrip ketikan), bagian server diimplementasikan dalam bahasa GO, dan direncanakan untuk menggunakan Redshift dan BigQuery sebagai database internal (kemudian mereka menambahkan dukungan untuk Postgres, ClickHouse dan Kepingan Salju).

Struktur acara GA dan Segmen diputuskan untuk tidak diubah. Yang diperlukan hanyalah menduplikasi semua peristiwa dari sumber daya web tempat piksel dipasang ke backend kami. Ternyata, hal ini mudah dilakukan. Piksel Javascript menggantikan metode pustaka GA asli dengan metode baru yang menduplikasi peristiwa di sistem kami.

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

Dengan Segmen pixel segalanya menjadi lebih sederhana; ia memiliki metode middleware, salah satunya kami gunakan.


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

Selain menyalin acara, kami telah menambahkan kemampuan untuk mengirim json sewenang-wenang:


//ΠžΡ‚ΠΏΡ€Π°Π²ΠΊΠ° событий с ΠΏΡ€ΠΎΠΈΠ·Π²ΠΎΠ»ΡŒΠ½Ρ‹ΠΌ 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'
});

Selanjutnya, mari kita bicara tentang sisi server. Backend harus menerima permintaan http, mengisinya dengan informasi tambahan, misalnya geodata (terima kasih pikiran maksimal untuk itu) dan menulis ke database. Kami ingin membuat layanan senyaman mungkin sehingga dapat digunakan dengan konfigurasi minimal. Kami telah menerapkan fungsi menentukan skema data berdasarkan struktur acara json yang masuk. Tipe data ditentukan oleh nilai. Objek bersarang didekomposisi dan direduksi menjadi struktur datar:

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

Namun, array saat ini hanya dikonversi menjadi string. tidak semua database relasional mendukung kolom berulang. Dimungkinkan juga untuk mengubah nama bidang atau menghapusnya menggunakan aturan pemetaan opsional. Mereka memungkinkan Anda mengubah skema data, jika perlu, atau mentransmisikan satu tipe data ke tipe data lainnya. Misalnya, jika bidang json berisi string dengan stempel waktu (bidang_3_sub_bidang_1_sub_sub_bidang_1 dari contoh di atas), maka untuk membuat field di database dengan tipe timestamp, Anda perlu menulis aturan pemetaan di konfigurasinya. Dengan kata lain, tipe data bidang ditentukan terlebih dahulu oleh nilai json, dan kemudian aturan casting tipe (jika dikonfigurasi) diterapkan. Kami telah mengidentifikasi 4 tipe data utama: STRING, FLOAT64, INT64 dan TIMESTAMP. Aturan pemetaan dan casting terlihat seperti ini:

rules:
  - "/field_1/subfield_1 -> " #ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ удалСния поля
  - "/field_2/subfield_1 -> /field_10/subfield_1" #ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ пСрСноса поля
  - "/field_3/subfield_1/subsubfield_1 -> (timestamp) /field_20" #ΠΏΡ€Π°Π²ΠΈΠ»ΠΎ пСрСноса поля ΠΈ привСдСния Ρ‚ΠΈΠΏΠ°

Algoritma untuk menentukan tipe data:

  • ubah struct json menjadi struct datar
  • menentukan tipe data bidang berdasarkan nilai
  • menerapkan pemetaan dan mengetikkan aturan casting

Kemudian dari struktur json yang masuk:

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

skema data akan diperoleh:

"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

Kami juga berpikir bahwa pengguna harus dapat mengkonfigurasi partisi atau membagi data dalam database sesuai dengan kriteria lain dan menerapkan kemampuan untuk mengatur nama tabel dengan konstanta atau ekspresi dalam konfigurasi. Pada contoh di bawah, event akan disimpan ke tabel dengan nama yang dihitung berdasarkan nilai kolom product_type dan _timestamp (misalnya persediaan_2020_10):

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

Namun, struktur kejadian yang masuk dapat berubah saat runtime. Kami telah menerapkan algoritma untuk memeriksa perbedaan antara struktur tabel yang ada dan struktur peristiwa yang masuk. Jika ditemukan perbedaan, tabel akan diperbarui dengan kolom baru. Untuk melakukannya, gunakan kueri patch SQL:

#ΠŸΡ€ΠΈΠΌΠ΅Ρ€ для Postgres
ALTER TABLE "schema"."table" ADD COLUMN new_column character varying

Arsitektur

Sejarah open source kami: bagaimana kami membuat layanan analitik di Go dan membuatnya tersedia untuk umum

Mengapa Anda perlu menulis acara ke sistem file dan tidak hanya menulisnya langsung ke database? Basis data tidak selalu bekerja dengan baik ketika berhadapan dengan sejumlah besar sisipan (rekomendasi postgres). Untuk melakukan ini, Logger menulis kejadian yang masuk ke file dan di goroutine (thread) terpisah, Pembaca file membaca file tersebut, kemudian data dikonversi dan ditentukan. Setelah Manajer Tabel memastikan bahwa skema tabel sudah diperbarui, data akan ditulis ke database dalam satu batch. Selanjutnya, kami menambahkan kemampuan untuk menulis data langsung ke database, tetapi kami menggunakan mode ini untuk peristiwa yang jumlahnya tidak banyak - misalnya, konversi.

Open Source dan rencana masa depan

Pada titik tertentu, layanan ini menjadi seperti produk lengkap dan kami memutuskan untuk memasukkannya ke dalam Open Source. Saat ini, integrasi dengan Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake telah diterapkan. Semua integrasi mendukung mode pemuatan data batch dan streaming. Menambahkan dukungan untuk permintaan melalui API.

Skema integrasi saat ini terlihat seperti ini:

Sejarah open source kami: bagaimana kami membuat layanan analitik di Go dan membuatnya tersedia untuk umum

Meskipun layanan ini dapat digunakan secara mandiri (misalnya menggunakan Docker), kami juga memilikinya versi yang dihosting, tempat Anda dapat menyiapkan integrasi dengan gudang data, menambahkan CNAME ke domain Anda, dan melihat statistik jumlah peristiwa. Rencana segera kami adalah menambahkan kemampuan untuk mengumpulkan tidak hanya statistik dari sumber daya web, tetapi juga data dari sumber data eksternal dan menyimpannya ke penyimpanan mana pun pilihan Anda!

β†’ GitHub
β†’ Π”окумСнтация
β†’ Kendur

Kami akan senang jika EventNative membantu Anda memecahkan masalah Anda!

Hanya pengguna terdaftar yang dapat berpartisipasi dalam survei. Masuk, silakan.

Sistem pengumpulan statistik apa yang digunakan di perusahaan Anda

  • 48,0%Google Analitik12

  • 4,0%Segmen1

  • 16,0%Lainnya (tulis di komentar) 4

  • 32,0%Menerapkan layanan Anda8

25 pengguna memilih. 6 pengguna abstain.

Sumber: www.habr.com

Tambah komentar