Pomáháme budovat risk engine — mikroservisně

Tomáš Hanus
OpenWise Tech blog
Published in
8 min readSep 3, 2019

V poslední době se o přístupu mikroservisně orientované architektury mluví doslova na každém rohu. U nás ve firmě toto téma rezonuje také. Pokud jde například o projekty, máme to teď zhruba půl na půl. Tedy, pořád existují projekty, u kterých vyniká jednoduchost monolitického řešení, a pak jsou tu zase projekty, kde se mikroservisní architektuře moc neubráníte :).

Na projektu, kde působím v roli jednoho z architektů, pomáháme s týmem připravovat produktovou platformu, jejímž hlavním cílem je postavit risk engine, nejlépe samozřejmě fungující v reálném čase. I když aktuálně řešíme scénáře z konkrétní business domény, snažíme se celé řešení koncipovat více abstraktně — chceme vytvořit univerzální platformu, která vypočítává rizikovost dat.

Dá se to popsat i jako identifikace rizikového chování v různých systémech zákazníka, kde samotné chování je reprezentováno nějakou datovou stopou, a u ní se právě snažíme dané riziko vyhodnotit.

Nejlépe to v daném kontextu vysvětluje pojem Fraud Detection System (Fraud is a billion-dollar business). Přiznejme si, jedná se o činnost nemilou, avšak běžnou. Ač se to na první pohled možná nezdá, scénářů a ve výsledku hlavně firem, které musí řešit podvody, je opravdu nespočet. Prvními odvětvími, kde se tento problém začal řešit, byly telekomunikace, pojišťovnictví a bankovní sféra. Postupem času se přidávala další odvětví a dnes můžeme mluvit již o globálním problému. U některých odvětví je kontrola řízena dokonce i zákony se snahou uchránit hlavně klienty různých služeb.

Uveďme několik příkladů:

Příklad z telekomunikací může být třeba “subscription fraud”, neboli zřízení telekomunikační služby a její používání s úmyslem za ní nezaplatit. Osobně z praxe například si nasmlouvávat postpaid služby a kupovat si za ně pizzu než na to někdo přijde :)

Pojišťovny se třeba dost intenzivně brání tomu někoho pojistit kdo vzápětí požaduje plnění, pochopitelně. Dalším častým typem podvodu jsou anomálie, odlehlé hodnoty, například výše škody v daném segmentu a daných parametrů samotného pojištění.

Z bankovnictví to může být třeba vytvoření nového účtu, na který přijde velký objem peněz, který do pár vteřin zase zmizí. Anebo přihlášení do internetového bankovnictví, ze kterého v mžiku odchází platba. Případně, pokud jste doposud nevytáhli paty z České republiky, a najednou chcete platit na Kubě — může vám pak zazvonit telefon v kapse, kde Vás banka kontaktuje, jestli je platba v pořádku.

Proč to vlastně ale řešíme mikroservisně? Na úvod je dobré říci, že tuto platformu nevyvíjíme od základu. Platforma již existuje, ale je dodnes monolitická. Jedná se tedy o její novou verzi. S odstupem času a i z provozu bylo dlouhodobě vidět, že celé řešení se špatně škáluje a je náchylné na výkonnostní špičky. V současné době, kdy roste množství dat, rostou i nároky na jejich zpracování. Do budoucna by tedy současné řešení bránilo rozvoji a nebyli bychom schopni nabídnout dostatečnou performance. Druhým kritériem je dosáhnout vysoké dostupnosti řešení, kdy díky omezené horizontální škálovatelnosti je výpadek jednoho z uzlů řešení kritický — je potřeba do deploymentu začlenit více redundantních uzlů a s tím je u monolitu obecně problém.

Obecně: princip vyhodnocení rizika je relativně komplexní. Každý systém obsahuje vždy místa, která až tak při zvýšené zátěži “netrpí”, zároveň však v systému existuje několik oblastí, které to mají přesně naopak. Aby platforma byla schopna zpracovávat opravdu velké množství dat, bylo nutné ji s ohledem na propustnost, škálování a resilienci přepracovat. Obecných argumentů pro mikroservisní architekturu je samozřejmě více (například rozložení složitosti do týmů, izolované a technologicky zaměnitelné komponenty, testovatelnost, odolnost vůči chybě a zamezení dopadů, …). Pro nás byla zásadní motivací hlavně performance a škálovatelnost řešení. Bavíme se zde o plánovaných nasazeních u opravdu velkých firem, které požadují minimální propustnost v řádech tisíců až desetitisíců req/sec s maximální latencí v řádech stovek milisekund. A prakticky, dnes se většina služeb sjednává prostřednictvím internetu, v bankovnictví třeba rezonují instantní platby, online sjednávání pojištění, telekomunikační služby, což je vlastně všechno real-time analýza dat. I toto lze řešit pomocí naší platformy.

Architektura

Zkusím v pár odstavcích popsat, jak takový systém může v praxi fungovat.

Hlavní charakteristika této platformy je vlastně data streaming. Vstupní zpráva se přijme, zvaliduje, převede se do interní struktury, vykoná se vlastní výpočet rizika (complex event processing) — případně za pomoci screenování (vyhledávání v datech obsahující rizikové patterny, nebo sada v minulosti identifikovaných subjektů ), dle definovaných business pravidel a výsledek se předává ve zjednodušeném flow zpět.

Jako centrální komunikační kanál používáme Apache Kafka. Z toho, na co všechno se Apache Kafka dá použít, je distribuované streamování dat asi nejtypičtější. Zejména pro konzistenci zpráv uvnitř Kafky používáme Schema Registry od Confluent a zprávy jsou přenášeny AVRO protokolem. Ta binární serializace je znát i na rychlosti. Ale umí toho více, včetně například verzování. Kdo nezná, vřele doporučuji.

Jednotlivé komponenty jsou docker kontejnery, které nyní provozujeme v Kubernetes. Vlastně se to snažíme nasazovat i do AKS, případně OpenShiftu, ať máme také reálné zkušenosti z různých prostředí. Komponenty (backend) jsou převážně psané ve Spring Bootu, něco je také i v Groovy. Víceméně všude používáme pro build Gradle (zde si musím povzdechnout, osobně jsem mu pořád nepřišel na chuť).

Některé komponenty mají význam administrační, ty vystavují zpravidla REST API, které konzumuje klientská aplikace psaná v Angularu. Principiálně se snažíme používat microfrontend přístup. Mezi GUI a jednotlivými backend službami máme nyní jednoduchou API Gateway psanou v Spring Cloud Gateway používající WebFlux — reactor princip. Autentizace/autorizace v rámci administrace systému je řešena přes OIDC, kde jsme si pomohli Keycloakem.

Ten reactor má něco do sebe, ale kolegové si občas škubou vlasy :).

Ostatní komponenty jsou exekuční. Mezi administračními a exekučními komponentami bychom rádi použili event sourcing. Zde jsme tedy vlastně na začátku, nicméně na vysvětlenou: těch “administračních” komponent bude co se týče počtu instancí daleko méně než bude počet exekučních komponent. Potřebujeme tedy data uložit perzistentně, ale pak také zajistit, aby se podle konfigurace exekuční komponenty chovaly. Data obecně ukládáme buď do relační databáze (chceme být agnostičtí, ale preferujeme PostgreSQL), Elastic, Mongo či Cassandra. Některé komponenty jsou technologicky orientované, některé jen vlastně přesypávají data, tam obecně používáme hlavně Apache Camel. Prozatím máme asi do 20 komponent a snažíme se, aby každá komponenta řešila vždy jednu “aktivitu” ve výše popsaném flow. Reálně existuje pak několik instancí. Pokud potřebujeme něco cachovat, používáme aktuálně Redis. Možná pro pořádek: pro logy klasika ELK a pro tracování integrovaný Zipkin. Architekturu máme tedy definovanou a nyní se podíváme, jak je vše složené dohromady.

V první řadě vstupní data, u kterých je požadavek na vyhodnocení rizika, k nám proudí nějakým kanálem, určitým protokolem. Ten se liší dle zákazníka, nicméně taková komponenta představuje jakýsi datový adaptér. Její úlohou je v zásadě odstínit nás od okolí — realizovat externí napojení s okolím (REST, SOAP, MQ, file transfer, …) a dále řešit datovou transformaci do naší interní struktury (CDM). U té se snažíme, aby byla pokud možno jednotná, poplatná business doméně. Výhodu to má třeba v předpřipravených pravidlech nad stejnými strukturami. Technicky se jedná o Spring Boot aplikaci, kde využíváme mediační platformu Apache Camel a pro datovou transformaci používáme ve většině případů Groovy. Takto zvalidovaná a přemapovaná zpráva se pak předává do Apache Kafka topicu.

Existuje i dedikovaná komponenta pro dávky (batch), která primárně řeší dávkový (offline) přenos. Víceméně jen rozdělí dávku do jednotlivých zpráv, které posílá pak dále. Zároveň spojuje jednotlivé zprávy (výsledky) do batch souboru a ten distribuuje zpět.

Další komponenta na cestě je risk engine postavená nad complex event processing—vlastně jedna z těch nejkritičtějších komponent. Obsahuje Esper pravidla, která definují business chování. Víceméně se jedná o logiku, která stojí za vyhodnocováním rizik dat (=scoring). Co říci — je to pekelně rychlé a obsahuje to různé pokročilejší metody jako jsou třeba časová okna nebo udržování kontextu dat. Jedná se o klasickou Spring Boot aplikaci, která integruje na Esper EPL. Na Esper se doporučuji obecně podívat. Má to třeba SQL syntaxi a široké uplatnění. Chtěl bych se mu blíže věnovat v některém z dalších článků.

Complex Event Processing is technology for detecting situations in real-time among events and considering time.

Zde ještě máme pár nevyřešených problémů. Jeden zásadnější je třeba sdílení kontextu mezi více instancemi, což Esper jako takový nepodporuje. Pro vysvětlení: v plánuje je shardování dat do Kafka topic partition dle určitého klíče, řekněme ID zákazníka, což pro většinu scénářů parádně řeší distribuci dat a tedy paralelní zpracování a ve výsledku pak i performance. Nicméně třeba identifikaci rizika napříč zákazníky pak neuděláme (=k tomu bychom potřebovali sdílet kontext, řešení existuje, jen není tak jednoduché jak se zdá). Existuje i EsperHA, nicméně to řeší principiálně HADR.

Tato komponenta je úzce propojena s další, která řeší tak zvaný screening datvyhodnocování vstupních dat vůči jiným datům. Lze to ilustrovat například na databázi dlužníků jako je SOLUS. Vůči takové databázi typicky můžete vyhledávat, aby se eliminovala možnost podvodu skrze zadlužené osoby. Takových databází je samozřejmě mnoho. Některé si spravují sami zákazníci, některé jsou i veřejné. Tyto databáze plánujeme v Elastic. Elastic musí s malou latencí vrátit výsledky až s propustností někde kolem 30 000 req/sec. Běžně jsou dotazy opravdu veliké, používají se i built-in skripty. Zcela běžně se používá Fuzzy search s Levenshtein distance algoritmem (efektivní řešení například na překlepy). Na základě výsledku ze screeningu pak risk engine a pravidla v něm vyhodnotí risk a vzniká rizikový profil. Ten může být pestrý a záleží hodně na interpretaci výsledku. Pravidel, které se vykonají, může být mnoho. Každé pravidlo reprezentuje vlastně rizikový faktor (například zda je žadatel o službu na seznamu zadlužených osob). Tento rizikový profil pak platforma vrátí a je na konzumentovi, jak ho interpretuje. Můžeme vracet třeba nějakou severitu, kolekci severit, numerickou hodnotu, vlastně cokoli. To vše záleží na vhodně zvolené strategii a nasazení u zákazníka.

Systém se pro různé scénáře sám snaží učit. Snaží se vyhodnocovat data v reálném čase, výsledky si ukládat, vytvářet rizikové profily, eliminovat false-positive nálezy. Tuto obrovskou kvantu dat chceme ukládat do Cassandra storage. Jde o velké množství, které se zpravidla zpracovává v reálném čase či cyklicky (například přes noc). K analýze plánujeme použít Apache Spark.

Zdroj: https://dzone.com/articles/lambda-architecture-with-apache-spark

A poslední, co ještě nezaznělo, ale vybízí to k tomu, je použití umělé inteligence. Pravidla, která jsem zmiňoval, jsou hlavně know-how zákazníka, nicméně pro rozhodování a eliminaci false-positive se snažíme doplňovat do systému různé prvky umělé inteligence. Spousta procesů končí v manuální investigaci, kdy se systém z rozhodnutí člověka snaží sám poučit, případně sám hledá nové nastavení modelů.

Zkuste se zamyslet. Přijde požadavek na objednávku služby, ta má na 20 různých parametrů, kde každý z nich se musí nějak prověřit (=znázorňuje rizikový faktor). Některé z nich například vůči číselníkům (třeba zakázané země, z nichž objednat nic nelze), ověření vůči různým seznamům, kontrola proti existujícímu rizikovému profilu či kontrola, že objednávku nevytváří zákazník často a neúspěšně, a na to vše máte třeba maximálně 200ms. Obecně to není zase tak malý čas, na druhou stranu na různou funkcionalitu potřebujete různé technologie pro samotné zpracování a data tedy přenášíte, takže moc času pak nezbývá. S performance testy jsme na začátku. Používáme pro měření Prometheus spolu s Grafanou. Snažíme se to vše zautomatizovat, reálné srovnání nás však zatím čeká.

Nakonec, já za sebe jsem rád, že po čase vidím využití mikroservisní architektury i z pohledu těch “hard” principů jako je performance a škálovatelnost řešení. Ta komplexita řešení je vysoká, velké množství technologií, u kterých však věřím, že v platformě mají své místo oprávněně. Jak říká kolega, chybí nám tam snad jen Blockchain :).

--

--