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 300 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.
Wzbogacanie Smart Signals
Wyodrębnione sygnały są następnie wzbogacane analizą Smart Signals — naszą warstwą inteligencji po stronie serwera. Obejmuje to klasyfikację sieci (ustalanie adresów VPN, proxy, Tor i centrów danych na podstawie komercyjnych baz IP intelligence), wykrywanie manipulacji przeglądarką (identyfikowanie niespójności wskazujących na podszywanie się pod sygnały), analizę tempa zdarzeń (zliczanie zdarzeń, odrębnych adresów i lokalizacji w ostatnich oknach czasowych) oraz scoring behawioralny (porównywanie dynamiki wskaźnika, klawiatury i przewijania z wzorcami ludzkimi).
Plany Enterprise dodają na tym samym etapie wykrywanie maszyn wirtualnych. Odczytuje ono ciągi renderera i dostawcy WebGL w poszukiwaniu sygnatur VMware, VirtualBox, Parallels, QEMU i Hyper-V, a następnie zestawia je z rozdzielczościami ekranu, liczbami rdzeni i rozmiarami pamięci typowymi dla VM — nie rozstrzyga żaden pojedynczy marker, decyduje ich kombinacja.
Każdy smart signal jest obliczany niezależnie i zwraca zarówno wynik, jak i ocenę pewności. Etap wzbogacania dodaje do każdego zdarzenia ponad 35 pól obliczanych po stronie serwera, zapewniając 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 300 surowych sygnałów, ponad 35 smart signals obliczanych po stronie serwera 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.