ประวัติของโอเพ่นซอร์สของเรา: วิธีที่เราสร้างบริการวิเคราะห์ใน Go และเผยแพร่สู่สาธารณะ

ปัจจุบัน เกือบทุกบริษัทในโลกรวบรวมสถิติเกี่ยวกับการกระทำของผู้ใช้บนทรัพยากรบนเว็บ แรงจูงใจนั้นชัดเจน บริษัทต่างๆ ต้องการทราบว่าผลิตภัณฑ์/เว็บไซต์ของตนถูกนำไปใช้อย่างไร และเข้าใจผู้ใช้ได้ดีขึ้น แน่นอนว่ามีเครื่องมือมากมายในตลาดที่จะแก้ไขปัญหานี้ - จากระบบการวิเคราะห์ที่ให้ข้อมูลในรูปแบบของแดชบอร์ดและกราฟ (เช่น Google Analytics) ไปยังแพลตฟอร์มข้อมูลลูกค้า ซึ่งช่วยให้คุณสามารถรวบรวมและรวบรวมข้อมูลจากแหล่งต่างๆ ในที่จัดเก็บข้อมูลใดก็ได้ (เช่น ส่วน).

แต่เราพบปัญหาที่ยังไม่ได้รับการแก้ไข เกิดมาเลย เหตุการณ์พื้นเมือง — บริการวิเคราะห์โอเพ่นซอร์ส เกี่ยวกับสาเหตุที่เราไปพัฒนาบริการของเราเอง สิ่งที่มอบให้เรา และสิ่งที่เกิดขึ้นในท้ายที่สุด (พร้อมโค้ดบางส่วน) โปรดอ่านอย่างละเอียด

ประวัติของโอเพ่นซอร์สของเรา: วิธีที่เราสร้างบริการวิเคราะห์ใน Go และเผยแพร่สู่สาธารณะ

ทำไมเราจึงต้องพัฒนาบริการของเราเอง?

มันเป็นยุคเก้าสิบ เราเอาตัวรอดอย่างดีที่สุดเท่าที่จะทำได้ ในปี 2019 เราได้พัฒนา API แพลตฟอร์มข้อมูลลูกค้ารายแรก เคเซนส์ซึ่งอนุญาตให้รวบรวมข้อมูลจากแหล่งต่างๆ (โฆษณา Facebook, Stripe, Salesforce, Google play, Google Analytics ฯลฯ) เพื่อการวิเคราะห์ข้อมูลที่สะดวกยิ่งขึ้น การระบุการขึ้นต่อกัน ฯลฯ เราสังเกตเห็นว่าผู้ใช้จำนวนมากใช้แพลตฟอร์มการวิเคราะห์ข้อมูลของเรา โดยเฉพาะ Google Analytics (ต่อไปนี้จะเรียกว่า GA) เราได้พูดคุยกับผู้ใช้บางคนและพบว่าพวกเขาต้องการข้อมูลการวิเคราะห์ผลิตภัณฑ์ซึ่งพวกเขาได้รับโดยใช้ GA แต่ Google ตัวอย่างข้อมูล และสำหรับอินเทอร์เฟซผู้ใช้ GA จำนวนมากก็ไม่ใช่มาตรฐานของความสะดวกสบาย เราได้พูดคุยกับผู้ใช้ของเรามามากพอแล้ว และพบว่ามีหลายคนที่ใช้แพลตฟอร์ม Segment เช่นกัน (ซึ่งก็คือเมื่อไม่กี่วันก่อน) ขายในราคา 3.2 พันล้านดอลลาร์).

พวกเขาติดตั้งพิกเซลจาวาสคริปต์ของเซ็กเมนต์บนทรัพยากรบนเว็บ และข้อมูลพฤติกรรมผู้ใช้ถูกโหลดลงในฐานข้อมูลที่ระบุ (เช่น Postgres) แต่เซ็กเมนต์ก็มีราคาลบด้วย ตัวอย่างเช่น หากทรัพยากรบนเว็บมี 90,000 MTU (ผู้ใช้ที่ติดตามรายเดือน) คุณจะต้องจ่ายเงิน ~ $ 1,000 ต่อเดือนให้กับแคชเชียร์ นอกจากนี้ยังมีปัญหาที่สาม - ส่วนขยายเบราว์เซอร์บางตัว (เช่น AdBlock) บล็อกการรวบรวมการวิเคราะห์ คำขอ http จากเบราว์เซอร์ถูกส่งไปยังโดเมน GA และ Segment ตามความต้องการของลูกค้าของเรา เราได้สร้างบริการการวิเคราะห์ที่รวบรวมข้อมูลชุดเต็ม (โดยไม่ต้องสุ่มตัวอย่าง) โดยไม่เสียค่าใช้จ่ายและสามารถทำงานบนโครงสร้างพื้นฐานของเราเองได้

บริการทำงานอย่างไร

บริการประกอบด้วยสามส่วน: พิกเซลจาวาสคริปต์ (ซึ่งต่อมาเราเขียนใหม่เป็น typescript) ส่วนเซิร์ฟเวอร์ที่ใช้งานในภาษา GO และมีการวางแผนที่จะใช้ Redshift และ BigQuery เป็นฐานข้อมูลภายใน (ต่อมาได้เพิ่มการรองรับสำหรับ Postgres , ClickHouse และ Snowflake)

โครงสร้างของเหตุการณ์ GA และ Segment ตัดสินใจที่จะไม่เปลี่ยนแปลง สิ่งเดียวที่จำเป็นคือการทำซ้ำกิจกรรมทั้งหมดจากแหล่งข้อมูลบนเว็บที่ติดตั้งพิกเซลไว้ที่แบ็กเอนด์ของเรา ปรากฎว่านี่เป็นเรื่องง่ายที่จะทำ พิกเซล 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);
        });
    });
}

ด้วยพิกเซลของเซ็กเมนต์ ทุกอย่างจะง่ายขึ้น มีวิธีมิดเดิลแวร์ และเราใช้วิธีใดวิธีหนึ่ง


//'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 ก่อน จากนั้นจึงใช้กฎการคัดเลือกประเภท (หากกำหนดค่าไว้) เราได้ระบุประเภทข้อมูลหลัก 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 Analytics12

  • ลด 4,0%ส่วนที่ 1

  • ลด 16,0%อื่นๆ (เขียนในความคิดเห็น) 4

  • ลด 32,0%ดำเนินการบริการของคุณ8

ผู้ใช้ 25 คนโหวต ผู้ใช้ 6 รายงดออกเสียง

ที่มา: will.com

เพิ่มความคิดเห็น