Η ιστορία του ανοιχτού κώδικα μας: πώς δημιουργήσαμε μια υπηρεσία αναλυτικών στοιχείων στο Go και την κάναμε δημόσια διαθέσιμη

Επί του παρόντος, σχεδόν κάθε εταιρεία στον κόσμο συλλέγει στατιστικά στοιχεία σχετικά με τις ενέργειες των χρηστών σε έναν πόρο Ιστού. Το κίνητρο είναι ξεκάθαρο - οι εταιρείες θέλουν να γνωρίζουν πώς χρησιμοποιείται το προϊόν/ιστοσελίδα τους και να κατανοούν καλύτερα τους χρήστες τους. Φυσικά, υπάρχει ένας μεγάλος αριθμός εργαλείων στην αγορά για την επίλυση αυτού του προβλήματος - από συστήματα ανάλυσης που παρέχουν δεδομένα με τη μορφή πινάκων εργαλείων και γραφημάτων (για παράδειγμα Google Analytics) στην πλατφόρμα δεδομένων πελατών, η οποία σας επιτρέπει να συλλέγετε και να συγκεντρώνετε δεδομένα από διαφορετικές πηγές σε οποιαδήποτε αποθήκη (για παράδειγμα Segment).

Όμως βρήκαμε ένα πρόβλημα που δεν έχει επιλυθεί ακόμα. Έτσι γεννήθηκε EventNative — υπηρεσία αναλυτικών στοιχείων ανοιχτού κώδικα. Διαβάστε γιατί αποφασίσαμε να αναπτύξουμε τη δική μας υπηρεσία, τι μας έδωσε και ποιο ήταν το τελικό αποτέλεσμα (με κομμάτια κώδικα).

Η ιστορία του ανοιχτού κώδικα μας: πώς δημιουργήσαμε μια υπηρεσία αναλυτικών στοιχείων στο Go και την κάναμε δημόσια διαθέσιμη

Γιατί πρέπει να αναπτύξουμε τη δική μας υπηρεσία;

Ήταν δεκαετία του '2019, επιβιώσαμε όσο καλύτερα μπορούσαμε. Το XNUMX, αναπτύξαμε την API First Customer Data Platform kSense, που κατέστησε δυνατή τη συγκέντρωση δεδομένων από διαφορετικές πηγές (διαφημίσεις Facebook, Stripe, Salesforce, Google play, Google Analytics κ.λπ.) για πιο βολική ανάλυση δεδομένων, εντοπισμό εξαρτήσεων κ.λπ. Παρατηρήσαμε ότι πολλοί χρήστες χρησιμοποιούν την πλατφόρμα μας για ανάλυση δεδομένων και συγκεκριμένα το Google Analytics (εφεξής GA). Μιλήσαμε με ορισμένους χρήστες και ανακαλύψαμε ότι χρειάζονται τα δεδομένα αναλυτικών στοιχείων για το προϊόν τους που λαμβάνουν χρησιμοποιώντας GA, αλλά Η Google λαμβάνει δείγματα δεδομένων και για πολλούς, η διεπαφή χρήστη GA δεν είναι το πρότυπο ευκολίας. Είχαμε αρκετές συζητήσεις με τους χρήστες μας και συνειδητοποιήσαμε ότι πολλοί χρησιμοποιούσαν επίσης την πλατφόρμα Segment (η οποία, παρεμπιπτόντως, ήταν μόλις τις προάλλες πωλήθηκε για 3.2 δισεκατομμύρια δολάρια).

Εγκατέστησαν ένα εικονοστοιχείο javascript Segment στον πόρο ιστού τους και δεδομένα σχετικά με τη συμπεριφορά των χρηστών τους φορτώθηκαν στην καθορισμένη βάση δεδομένων (για παράδειγμα Postgres). Αλλά το Segment έχει επίσης το μειονέκτημά του - την τιμή. Για παράδειγμα, εάν ένας πόρος Ιστού έχει 90,000 MTU (μηνιαίους χρήστες που παρακολουθούνται), τότε πρέπει να πληρώνετε ~1,000 $ το μήνα στο ταμείο. Υπήρξε επίσης ένα τρίτο πρόβλημα - ορισμένες επεκτάσεις προγράμματος περιήγησης (όπως το AdBlock) απέκλεισαν τη συλλογή των αναλυτικών στοιχείων επειδή... Τα αιτήματα http από το πρόγραμμα περιήγησης στάλθηκαν στους τομείς GA και Τμήματος. Με βάση τις επιθυμίες των πελατών μας, δημιουργήσαμε μια υπηρεσία ανάλυσης που συλλέγει ένα πλήρες σύνολο δεδομένων (χωρίς δειγματοληψία), είναι δωρεάν και μπορεί να λειτουργήσει στη δική μας υποδομή.

Πώς λειτουργεί η υπηρεσία

Η υπηρεσία αποτελείται από τρία μέρη: ένα εικονοστοιχείο javascript (το οποίο αργότερα ξαναγράψαμε σε πληκτρολόγιο), το τμήμα διακομιστή υλοποιείται στη γλώσσα GO και σχεδιάστηκε να χρησιμοποιηθεί το Redshift και το BigQuery ως εσωτερική βάση δεδομένων (αργότερα πρόσθεσαν υποστήριξη για Postgres, ClickHouse και Snowflake).

Αποφασίστηκε να παραμείνει αμετάβλητη η δομή των συμβάντων GA και Τμήματος. Το μόνο που χρειαζόταν ήταν να αντιγράψουμε όλα τα συμβάντα από τον πόρο ιστού όπου είναι εγκατεστημένο το pixel στο backend μας. Όπως αποδεικνύεται, αυτό δεν είναι δύσκολο να γίνει. Το εικονοστοιχείο Javascript αντικατέστησε την αρχική μέθοδο βιβλιοθήκης GA με μια νέα, η οποία αντιτύπωσε το συμβάν στο σύστημά μας.

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

Με το Segment pixel όλα είναι πιο απλά· έχει μεθόδους ενδιάμεσου λογισμικού, μία από τις οποίες χρησιμοποιήσαμε.


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

Εκτός από την αντιγραφή συμβάντων, έχουμε προσθέσει τη δυνατότητα αποστολής αυθαίρετων json:


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

Στη συνέχεια, ας μιλήσουμε για το μέρος του διακομιστή. Το backend θα πρέπει να δέχεται αιτήματα http, να τα συμπληρώνει με πρόσθετες πληροφορίες, για παράδειγμα, γεωγραφικά δεδομένα (ευχαριστώ maxmind για αυτό) και καταγράψτε το στη βάση δεδομένων. Θέλαμε να κάνουμε την υπηρεσία όσο πιο βολική γίνεται, ώστε να μπορεί να χρησιμοποιηθεί με ελάχιστες ρυθμίσεις. Έχουμε εφαρμόσει τη λειτουργικότητα του προσδιορισμού του σχήματος δεδομένων με βάση τη δομή του εισερχόμενου συμβάντος json. Οι τύποι δεδομένων ορίζονται από τιμές. Τα ένθετα αντικείμενα αποσυντίθενται και ανάγονται σε μια επίπεδη δομή:

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

Ωστόσο, οι πίνακες προς το παρόν απλώς μετατρέπονται σε συμβολοσειρές επειδή Δεν υποστηρίζουν όλες οι σχεσιακές βάσεις δεδομένων επαναλαμβανόμενα πεδία. Είναι επίσης δυνατό να αλλάξετε τα ονόματα πεδίων ή να τα διαγράψετε χρησιμοποιώντας προαιρετικούς κανόνες αντιστοίχισης. Σας επιτρέπουν να αλλάξετε το σχήμα δεδομένων εάν είναι απαραίτητο ή να μετατρέψετε έναν τύπο δεδομένων σε έναν άλλο. Για παράδειγμα, εάν ένα πεδίο json περιέχει μια συμβολοσειρά με χρονική σήμανση (field_3_sub_field_1_sub_sub_field_1 από το παραπάνω παράδειγμα), στη συνέχεια, για να δημιουργήσετε ένα πεδίο στη βάση δεδομένων με τον τύπο χρονικής σφραγίδας, πρέπει να γράψετε έναν κανόνα αντιστοίχισης στη διαμόρφωση. Με άλλα λόγια, ο τύπος δεδομένων του πεδίου καθορίζεται πρώτα από την τιμή json και, στη συνέχεια, εφαρμόζεται ο κανόνας μετάδοσης τύπου (αν έχει διαμορφωθεί). Έχουμε εντοπίσει 4 βασικούς τύπους δεδομένων: STRING, FLOAT64, INT64 και TIMESTAMP. Οι κανόνες χαρτογράφησης και χύτευσης τύπου μοιάζουν με αυτό:

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

Αλγόριθμος για τον προσδιορισμό του τύπου δεδομένων:

  • μετατροπή δομής json σε επίπεδη δομή
  • προσδιορίζοντας τον τύπο δεδομένων των πεδίων με τιμές
  • εφαρμόζοντας κανόνες χαρτογράφησης και χύτευσης τύπου

Στη συνέχεια, από την εισερχόμενη δομή 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"
    }
}

θα ληφθεί το σχήμα δεδομένων:

"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

Θεωρήσαμε επίσης ότι ο χρήστης θα πρέπει να είναι σε θέση να διαμορφώσει την κατάτμηση ή τη διαίρεση δεδομένων στη βάση δεδομένων σύμφωνα με άλλα κριτήρια και εφαρμόσαμε τη δυνατότητα να ορίσουμε το όνομα του πίνακα με μια σταθερά ή έκφραση στη διαμόρφωση. Στο παρακάτω παράδειγμα, το συμβάν θα αποθηκευτεί σε έναν πίνακα με ένα όνομα που υπολογίζεται με βάση τις τιμές των πεδίων product_type και _timestamp (για παράδειγμα προμήθειες_2020_10):

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

Ωστόσο, η δομή των εισερχόμενων συμβάντων μπορεί να αλλάξει κατά το χρόνο εκτέλεσης. Έχουμε εφαρμόσει έναν αλγόριθμο για να ελέγξουμε τη διαφορά μεταξύ της δομής ενός υπάρχοντος πίνακα και της δομής ενός εισερχόμενου συμβάντος. Εάν βρεθεί διαφορά, ο πίνακας θα ενημερωθεί με νέα πεδία. Για να το κάνετε αυτό, χρησιμοποιήστε το ερώτημα ενημέρωσης κώδικα SQL:

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

Αρχιτεκτονική

Η ιστορία του ανοιχτού κώδικα μας: πώς δημιουργήσαμε μια υπηρεσία αναλυτικών στοιχείων στο Go και την κάναμε δημόσια διαθέσιμη

Γιατί χρειάζεται να γράφετε συμβάντα στο σύστημα αρχείων και όχι απλώς να τα γράφετε απευθείας στη βάση δεδομένων; Οι βάσεις δεδομένων δεν έχουν πάντα καλή απόδοση όταν ασχολούνται με μεγάλους αριθμούς ένθετων (Συστάσεις Postgres). Για να γίνει αυτό, το Logger γράφει τα εισερχόμενα συμβάντα σε ένα αρχείο και σε μια ξεχωριστή goroutine (νήμα) Αναγνώστης αρχείων διαβάζει το αρχείο και, στη συνέχεια, τα δεδομένα μετατρέπονται και προσδιορίζονται. Αφού ο διαχειριστής πίνακα βεβαιωθεί ότι το σχήμα του πίνακα είναι ενημερωμένο, τα δεδομένα θα εγγραφούν στη βάση δεδομένων σε μία παρτίδα. Στη συνέχεια, προσθέσαμε τη δυνατότητα εγγραφής δεδομένων απευθείας στη βάση δεδομένων, αλλά χρησιμοποιούμε αυτήν τη λειτουργία για συμβάντα που δεν είναι πολλά - για παράδειγμα, μετατροπές.

Ανοιχτός κώδικας και σχέδια για το μέλλον

Κάποια στιγμή, η υπηρεσία άρχισε να μοιάζει με ένα ολοκληρωμένο προϊόν και αποφασίσαμε να το κυκλοφορήσουμε στο Open Source. Επί του παρόντος, έχουν υλοποιηθεί ενσωματώσεις με Postgres, ClickHouse, BigQuery, Redshift, S3, Snowflake. Όλες οι ενσωματώσεις υποστηρίζουν τρόπους φόρτωσης δεδομένων τόσο δέσμης όσο και ροής. Προστέθηκε υποστήριξη για αιτήματα μέσω API.

Το τρέχον σχήμα ολοκλήρωσης μοιάζει με αυτό:

Η ιστορία του ανοιχτού κώδικα μας: πώς δημιουργήσαμε μια υπηρεσία αναλυτικών στοιχείων στο Go και την κάναμε δημόσια διαθέσιμη

Αν και η υπηρεσία μπορεί να χρησιμοποιηθεί ανεξάρτητα (για παράδειγμα χρησιμοποιώντας Docker), έχουμε επίσης φιλοξενούμενη έκδοση, στο οποίο μπορείτε να ρυθμίσετε την ενοποίηση με μια αποθήκη δεδομένων, να προσθέσετε ένα CNAME στον τομέα σας και να προβάλετε στατιστικά στοιχεία για τον αριθμό των συμβάντων. Τα άμεσα σχέδιά μας είναι να προσθέσουμε τη δυνατότητα να συγκεντρώνουμε όχι μόνο στατιστικά στοιχεία από έναν πόρο Ιστού, αλλά και δεδομένα από εξωτερικές πηγές δεδομένων και να τα αποθηκεύουμε σε οποιοδήποτε χώρο αποθήκευσης της επιλογής σας!

→ GitHub
→ Εγγραφές
→ Χαλαρότητα

Θα χαρούμε αν το EventNative βοηθήσει στην επίλυση των προβλημάτων σας!

Μόνο εγγεγραμμένοι χρήστες μπορούν να συμμετάσχουν στην έρευνα. Συνδεθείτε, Σας παρακαλούμε.

Ποιο σύστημα συλλογής στατιστικών στοιχείων χρησιμοποιείται στην εταιρεία σας;

  • 48,0%Google Analytics 12

  • 4,0%Τμήμα 1

  • 16,0%Άλλο (γράψτε στα σχόλια)4

  • 32,0%Εφάρμοσε την υπηρεσία σας8

Ψήφισαν 25 χρήστες. 6 χρήστες απείχαν.

Πηγή: www.habr.com

Προσθέστε ένα σχόλιο