Budowa potoku analitycznego do wykrywania oszustw w czasie rzeczywistym
Omówienie architektury: przyjmowanie 50 tys. zdarzeń na sekundę, wzbogacanie o smart signals i ocena ryzyka w mniej niż 10 ms dzięki naszemu silnikowi strumieniowemu.
Przetwarzanie 50 000 zdarzeń fingerprintingu na sekundę, wzbogacanie każdego z nich o smart signals i zwracanie oceny ryzyka w mniej niż 10 milisekund wymaga starannie zaprojektowanej architektury strumieniowej. Ten artykuł prowadzi przez nasz potok od przyjęcia danych do decyzji.
Warstwa przyjmowania danych
Zdarzenia docierają jako żądania HTTPS POST od naszego agenta JavaScript działającego w przeglądarkach odwiedzających. Każde zdarzenie zawiera zaszyfrowany ładunek sygnałów — zwykle 8–12 KB skompresowanych danych obejmujących ponad 130 sygnałów przeglądarki. Nasze serwery brzegowe kończą TLS, weryfikują sygnaturę żądania i przekazują ładunek do potoku przetwarzania.
Stosujemy wdrożenie wieloregionalne, w którym serwery brzegowe są kolokowane z węzłami CDN naszych klientów. Dzięki temu czas obiegu sieciowego pozostaje poniżej 20 ms dla 95% żądań na całym świecie. Serwery brzegowe to bezstanowe usługi Go działające za load balancerem i skalujące się poziomo w zależności od wolumenu żądań.
Ekstrakcja sygnałów
Pierwszy etap przetwarzania odszyfrowuje i parsuje ładunek sygnałów. Każdy sygnał jest wyodrębniany, walidowany i typowany. Skróty canvas są weryfikowane pod kątem znanych niemożliwych wartości (które wskazują na blokowanie lub podszywanie się w canvasie). Parametry WebGL są wzajemnie walidowane pod kątem spójności. Właściwości obiektu Navigator są sprawdzane względem znanych prawidłowych kombinacji.
Ten etap wykonuje też normalizację sygnałów. Ciągi User Agent są parsowane do ustrukturyzowanych komponentów (przeglądarka, wersja, system operacyjny, urządzenie). Wymiary ekranu są normalizowane z uwzględnieniem skalowania DPI. Przesunięcia strefy czasowej są walidowane względem danych geolokalizacji IP.
Wzbogacanie Smart Signals
Wyodrębnione sygnały są następnie wzbogacane analizą Smart Signals — naszą warstwą inteligencji po stronie serwera. Obejmuje to wykrywanie trybu incognito (porównywanie wzorców sygnałów ze znanymi sygnaturami prywatnego przeglądania), wykrywanie VPN (zestawianie danych IP z sygnałami strefy czasowej i ustawień regionalnych), wykrywanie manipulacji przeglądarką (identyfikowanie niespójności wskazujących na podszywanie się pod sygnały) oraz wykrywanie maszyn wirtualnych (rozpoznawanie profili sprzętowych powiązanych z VMware, VirtualBox i chmurowymi VM).
Każdy smart signal jest obliczany niezależnie i zwraca zarówno wynik logiczny, jak i ocenę pewności. Etap wzbogacania dodaje 24 dodatkowe sygnały do każdego zdarzenia, zapewniając kompleksową ocenę zagrożeń, która wykracza poza to, co można osiągnąć samym zbieraniem danych po stronie klienta.
Silnik oceny ryzyka
Wzbogacone zdarzenie trafia do naszego silnika oceny ryzyka — modelu drzewa decyzyjnego z gradient boostingiem, wytrenowanego na milionach oznaczonych etykietami zdarzeń. Model uwzględnia wszystkie ponad 130 surowych sygnałów, 24 smart signals oraz kilka cech pochodnych: metryki tempa (ile zdarzeń z tego urządzenia w ciągu ostatnich 5 minut, 1 godziny i 24 godzin), historyczne wzorce zachowań oraz oceny reputacji sieci.
Model zwraca ocenę ryzyka w zakresie od 0 do 100 wraz z głównymi czynnikami wpływającymi na wynik. Ocenie 85 mogą na przykład towarzyszyć czynniki takie jak „wykryto VPN”, „tryb incognito” czy „wysokie tempo — 47 zdarzeń w 5 minut”. Ta możliwość interpretacji jest kluczowa dla analityków oszustw, którzy muszą rozumieć, dlaczego dane zdarzenie zostało oznaczone.
Warstwa przechowywania i zapytań
Wszystkie zdarzenia są utrwalane w ClickHouse — kolumnowej bazie danych zoptymalizowanej pod zapytania analityczne na dużych zbiorach danych. ClickHouse obsługuje nasz wolumen zapisu (50 tys. zdarzeń na sekundę) bez najmniejszego wysiłku, a jego kolumnowe przechowywanie umożliwia analityczne zapytania w czasie poniżej sekundy na miliardach wierszy.
Stosujemy wielopoziomową strategię retencji. Dane gorące (ostatnie 7 dni) są przechowywane na dyskach NVMe SSD dla czasu odpowiedzi zapytań poniżej 100 ms. Dane ciepłe (7–90 dni) znajdują się na standardowych dyskach SSD. Dane zimne (powyżej 90 dni) są kompresowane i przenoszone do magazynu obiektowego — pozostają dostępne w zapytaniach, ale z wyższym opóźnieniem.
Kafka jako szkielet
Apache Kafka spina cały potok. Każdy etap odczytuje z tematów Kafki i do nich zapisuje. Warstwa przyjmowania danych zapisuje surowe zdarzenia. Etap ekstrakcji sygnałów odczytuje surowe zdarzenia i zapisuje wyodrębnione zdarzenia. Etap wzbogacania Smart Signals odczytuje wyodrębnione zdarzenia i zapisuje wzbogacone zdarzenia. Silnik oceny ryzyka odczytuje wzbogacone zdarzenia i zapisuje ocenione zdarzenia.
Ta architektura daje kilka korzyści: etapy można skalować niezależnie, awarie na jednym etapie nie wpływają na pozostałe, a zdarzenia można odtworzyć przez dowolny etap na potrzeby debugowania lub ponownego przetwarzania. Grupy konsumentów Kafki umożliwiają równoległe przetwarzanie w obrębie każdego etapu, a semantyka exactly-once gwarantuje, że żadne zdarzenie nie zostanie przetworzone dwukrotnie ani utracone.
Budżet opóźnień
Nasz docelowy czas opóźnienia end-to-end wynosi 10 ms — od momentu, gdy wzbogacony ładunek sygnałów dociera do potoku przetwarzania, do momentu zwrócenia oceny ryzyka. Oto jak rozkłada się ten budżet: ekstrakcja sygnałów zajmuje 1–2 ms, wzbogacanie Smart Signals 3–4 ms, scoring ryzyka 2–3 ms, a serializacja i odpowiedź 1–2 ms. Skok przez Kafkę między etapami dodaje mniej niż 1 ms w naszym kolokowanym wdrożeniu.
Konsekwentne mieszczenie się w tym budżecie przy 50 tys. zdarzeń na sekundę wymaga starannej optymalizacji na każdym etapie. Stosujemy wstępnie zaalokowane pule pamięci, serializację zero-copy oraz wsadowe zapisy do ClickHouse. Model oceny ryzyka jest kompilowany do kodu natywnego przy użyciu ONNX Runtime, co eliminuje narzut interpretera Pythona.
Mark spędził dwa tygodnie na profilowaniu potoku, zanim znalazł wąskie gardło w naszej warstwie rozproszonych wyszukiwań — pojedynczy mutex serializował wyszukiwania we wszystkich gorutynach. Po przejściu na projekt z blokadami shardowanymi p99 spadło z 48 ms do 9 ms. Czasem, gdy już znajdziesz przyczynę, poprawka okazuje się boleśnie prosta.