私たちのオープンソースの歴史: どのようにして Go で分析サービスを作成し、一般公開したのか

現在、世界中のほぼすべての企業が、Web リソース上のユーザーのアクションに関する統計を収集しています。 動機は明らかです。企業は自社の製品や Web サイトがどのように使用されているかを知り、ユーザーをより深く理解したいと考えています。 もちろん、市場には、ダッシュボードやグラフの形式でデータを提供する分析システムなど、この問題を解決するためのツールが多数市販されています(たとえば、 Google Analytics) を顧客データ プラットフォームに統合し、任意のウェアハウス (たとえば、 セグメント).

しかし、まだ解決されていない問題が見つかりました。 こうして誕生した イベントネイティブ — オープンソースの分析サービス。 独自のサービスを開発することにした理由、それによって得られたもの、および最終結果 (コードの一部) についてお読みください。

私たちのオープンソースの歴史: どのようにして Go で分析サービスを作成し、一般公開したのか

なぜ独自のサービスを開発する必要があるのでしょうか?

2019年代、私たちはできる限りのことをして生き延びました。 XNUMX年、API First顧客データプラットフォームを開発 kSenseこれにより、さまざまなソース (Facebook 広告、Stripe、Salesforce、Google play、Google Analytics など) からのデータを集約して、より便利なデータ分析や依存関係の特定などが可能になりました。 多くのユーザーが当社のプラットフォームをデータ分析、特に Google Analytics (以下 GA) に使用していることに気づきました。 何人かのユーザーと話をしたところ、GA を使用して受け取る製品の分析データが必要であることがわかりましたが、 Googleのサンプルデータ そして多くの人にとって、GA ユーザー インターフェイスは利便性の標準ではありません。 ユーザーと十分な会話を行った結果、多くのユーザーが Segment プラットフォームも使用していることがわかりました (ちなみに、つい先日のことです) 3.2億ドルで売却).

Web リソースにセグメント JavaScript ピクセルをインストールし、ユーザーの行動に関するデータが指定されたデータベース (Postgres など) にロードされました。 しかし、セグメントには価格という欠点もあります。 たとえば、Web リソースの MTU (毎月追跡されるユーザー) が 90,000 の場合、レジ係に毎月最大 1,000 ドルを支払う必要があります。 XNUMX 番目の問題もありました。一部のブラウザ拡張機能 (AdBlock など) が分析の収集をブロックしたためです。 ブラウザからの http リクエストは GA ドメインとセグメント ドメインに送信されました。 クライアントの要望に基づいて、当社は完全なデータセットを (サンプリングなしで) 収集し、無料で独自のインフラストラクチャで動作できる分析サービスを作成しました。

サービスの仕組み

このサービスは XNUMX つの部分で構成されています: JavaScript ピクセル (後で typescript で書き直しました)、サーバー部分は GO 言語で実装され、社内データベースとして Redshift と BigQuery を使用する予定でした (後にサポートを追加しました) Postgres、ClickHouse、Snowflake)。

GA およびセグメント イベントの構造は変更しないことが決定されました。 必要なのは、ピクセルがインストールされている Web リソースからバックエンドにすべてのイベントを複製することだけでした。 結局のところ、これを行うのは難しいことではありません。 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);
        });
    });
}

セグメント ピクセルを使用すると、すべてがシンプルになり、ミドルウェア メソッドがあり、そのうちの XNUMX つを使用しました。


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

次に、サーバー部分について説明します。 バックエンドは http リクエストを受け入れ、地理データなどの追加情報を入力する必要があります (ありがとう) マックスマインド この場合)、データベースに記録します。 私たちは、最小限の構成でサービスを使用できるように、サービスをできるだけ便利なものにしたいと考えました。 受信した 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 値によって決定され、次に型キャスト ルール (構成されている場合) が適用されます。 STRING、FLOAT4、INT64、TIMESTAMP の 64 つの主要なデータ型が特定されました。 マッピングと型キャストのルールは次のようになります。

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 は受信イベントをファイルに書き込み、別のゴルーチン (スレッド) ファイル リーダーがファイルを読み取り、データが変換されて決定されます。 テーブル マネージャーがテーブル スキーマが最新であることを確認した後、データは XNUMX つのバッチでデータベースに書き込まれます。 その後、データベースにデータを直接書き込む機能を追加しましたが、このモードは、変換などのそれほど多くないイベントに使用します。

オープンソースと将来の計画

ある時点で、このサービスが本格的な製品のように見え始めたので、私たちはそれをオープンソースとしてリリースすることにしました。 現在、Postgres、ClickHouse、BigQuery、Redshift、S3、Snowflake との統合が実装されています。 すべての統合は、データ読み込みのバッチ モードとストリーミング モードの両方をサポートします。 API 経由のリクエストのサポートが追加されました。

現在の統合スキームは次のようになります。

私たちのオープンソースの歴史: どのようにして Go で分析サービスを作成し、一般公開したのか

このサービスは独立して使用することもできますが (Docker を使用するなど)、 ホストされたバージョンここでは、データ ウェアハウスとの統合を設定したり、ドメインに CNAME を追加したり、イベント数の統計を表示したりできます。 私たちの当面の計画は、Web リソースからの統計だけでなく、外部データ ソースからのデータも集計し、それらを任意のストレージに保存する機能を追加することです。

→ GitHubの
→ Документация
→ Slack

EventNative があなたの問題解決に貢献できれば幸いです。

登録ユーザーのみがアンケートに参加できます。 ログインお願いします。

あなたの会社ではどのような統計収集システムが使用されていますか?

  • 視聴者の38%がGoogle Analytics12

  • 視聴者の38%がセグメント1

  • 視聴者の38%が別の(コメントに書いてください)4

  • 視聴者の38%がサービスを実装しました8

25 人のユーザーが投票しました。 6名のユーザーが棄権した。

出所: habr.com

コメントを追加します