tracio.ai'nin 30 ms Altı İşlem Hattını Nasıl Kurduk
Sinyal toplamadan ziyaretçi kimliğine 30 ms altında: Go, ClickHouse, Redis ve dağıtık işlemeyi kullanan mimarimiz.
tracio.ai'nin cihaz tanımlama motorunu kurmaya başladığımızda, üzerinde pazarlık edilemeyecek tek bir gereksinimimiz vardı: şifrelenmiş sinyalleri almaktan bir ziyaretçi kimliği döndürmeye kadar tüm işlem hattı, 95. yüzdelik dilimde 30 milisaniyenin altında tamamlanmalı. Bu makale, o hedefi karşılamak için kurduğumuz mimarinin ayrıntılı bir incelemesidir.
İşlem Hattına Genel Bakış
Tanımlama işlem hattının beş aşaması vardır: sinyal şifre çözme, sinyal normalleştirme, hash hesaplama, kimlik çözümleme ve yanıt serileştirme. Her aşama bağımsız olarak optimize edilir ve paralel çalışabilen aşamalar paralel çalışır. Toplam bütçe 30 ms olup kabaca şöyle dağıtılır: şifre çözme 2 ms, normalleştirme 3 ms, hash'leme 2 ms, kimlik çözümleme 20 ms, serileştirme 1 ms. Kalan 2 ms tampon olarak ayrılır.
Sinyal şifre çözme, istemci tarafında şifrelenen taşımayı tersine çevirir. Donanım hızlandırmalı Go'nun kripto paketlerini kullanıyoruz; bu paketler tipik bir 4 KB'lık yükün şifresini 1 ms'nin altında çözer. Normalleştirme, sinyal JSON'unu ayrıştırır, türleri doğrular ve platforma özgü dönüşümleri uygular; örneğin, sürüme özgü gürültüyü kaldırmak için user agent dizelerini normalleştirir.
Dağıtık Kimlik Çözümleme
Kimlik çözümleme — bu cihazın daha önce görülüp görülmediğini belirleme — gecikmeye en duyarlı aşamadır. Cihaz profillerini, dağıtık bir anahtar yönlendirme katmanı kullanarak bir küme genelinde parçalanmış olarak Redis'te saklarız. Yönlendirme, anahtarları donanım katmanı parmak izine göre dağıtır; bu da aynı cihaza yönelik aramaların her zaman aynı Redis düğümüne ulaşmasını sağlar.
Parçalama uygulamamız, eşit dağılımı sağlamak için sanal düğümler (fiziksel düğüm başına 150) kullanır. Bir düğüm eklendiğinde veya çıkarıldığında, anahtarların yalnızca 1/N'inin yeniden eşlenmesi gerekir; burada N düğüm sayısıdır. Yönlendirme katmanını Go'da O(log n) arama süresi ve sıfır ayırma ile uyguladık.
Kimlik Deposu Olarak Redis
Alternatifler (Memcached, ScyllaDB, DynamoDB) yerine Redis'i, tutarlı milisaniye altı yanıt süreleri ve karmaşık veri yapıları desteği nedeniyle seçtik. Her cihaz profili, her sinyal katmanının hash'i, ziyaretçi kimliği, son görülme zaman damgası ve güven meta verileri için alanlara sahip bir Redis hash'i olarak saklanır.
Kimlik çözümleme sorgusu, tek bir HGETALL çağrısının ardından gelen sinyal hash'lerinin saklanan hash'lerle karşılaştırılmasından oluşur. Donanım katmanı eşleşirse, mevcut ziyaretçi kimliğini yüksek güvenle döndürürüz. Yalnızca yazılım katmanı eşleşirse, bunun güncellenmiş bir tarayıcıya sahip aynı cihaz olup olmadığını belirlemek için sinyal düzeyindeki verilerin bir benzerlik karşılaştırmasını yaparız. Hiçbir şey eşleşmezse, yeni bir ziyaretçi kimliği oluştururuz.
Olay Depolama İçin ClickHouse
Her tanımlama olayı ClickHouse'a eşzamansız olarak yazılır. Eklemeleri toplu işleyen tamponlanmış bir yazıcı kullanırız; olayları 100 ms boyunca veya 1.000 olay birikene kadar (hangisi önce olursa) toplar. Bu toplu işleme kritiktir, çünkü ClickHouse tek tek satır eklemeleri yerine büyük eklemelerle (her seferinde binlerce satır) en iyi performansı gösterir.
ClickHouse şemamız en yaygın iki sorgu deseni için optimize edilmiştir: belirli bir ziyaretçi kimliğine ait tüm olayları arama ve olayları zaman dilimleri üzerinden toplama. Birincil anahtarı (visitor_id, timestamp) olan bir MergeTree motoru kullanırız; bu, hızlı nokta aramaları ve verimli aralık taramaları sağlar. Materyalleştirilmiş görünümler, önceden toplanmış günlük ve saatlik metrikleri tutar.
Ölçekte 30 ms Altına Ulaşmak
Gecikme hedefimizi karşılamak için üç mimari karar kritikti. İlk olarak, işlem hattı tamamen akış tabanlıdır — sinyalleri, HTTP istek gövdesinin tamamı alınmadan önce işlemeye başlarız. İkinci olarak, Redis aramaları kalıcı bağlantılarla bağlantı havuzu kullanır ve TCP el sıkışma ek yükünü ortadan kaldırır. Üçüncü olarak, ClickHouse yazımları tamamen eşzamansızdır ve yanıt yolunu asla bloke etmez.
Saniyede 50 bin isteklik yük testinde p50 gecikmemiz 12 ms, p95 24 ms ve p99 38 ms'dir. p99, Redis küme yeniden dengelemesi sırasında zaman zaman 30 ms hedefimizi aşar, ancak p95 tutarlı biçimde 30 ms'nin altında kalır. Daha katı gecikme gereksinimleri olan müşteriler için, çok kiracılı çekişmeyi ortadan kaldıran özel Redis kümeleri sunuyoruz.