Aplikacje kontekstowe i złożona architektura przetwarzania zdarzeń

Opublikowany: 2022-03-11

Korzystanie z telefonów komórkowych na całym świecie stale rośnie. W 2013 r. około 73% internautów korzystało z treści za pośrednictwem urządzenia mobilnego, a do 2017 r. odsetek ten ma osiągnąć blisko 90%.

Powodów rewolucji mobilnej jest oczywiście wiele. Ale jednym z najważniejszych jest to, że aplikacje mobilne na ogół uzyskują dostęp do bogatszego kontekstu, ponieważ prawie wszystkie dzisiejsze smartfony są wyposażone w czujniki lokalizacji, czujniki ruchu, Bluetooth i Wi-Fi. Wykorzystując swoje dane, aplikacje mogą osiągnąć „świadomość kontekstową”, która może radykalnie zwiększyć ich możliwości i wartość oraz naprawdę wyróżnić je w sklepach z aplikacjami.

W tym samouczku omówimy tworzenie aplikacji kontekstowych na przykładzie przetwarzania złożonych zdarzeń. Użyjemy dość prostego przykładu: aplikacji cenowej, która wyszukuje najlepsze ceny paliw w Twojej okolicy.

Świadomość kontekstu w aplikacjach można utworzyć za pomocą złożonego samouczka dotyczącego przetwarzania zdarzeń, takiego jak ten.

Aplikacje kontekstowe

W Designing Calm Technology Mark Weiser i John Seely Brown opisują spokojną technologię jako „taką, która informuje, ale nie wymaga naszego skupienia ani uwagi”.

Kontekstowe aplikacje mobilne są wysoce spójne z tym pojęciem i stanowią ważny i cenny krok na tej ścieżce. Wykorzystują informacje kontekstowe zebrane z ich czujników, aby proaktywnie dostarczać użytkownikowi cennych informacji i robią to przy minimalnym wysiłku ze strony użytkownika. Mark Weiser i John Seely Brown bez wątpienia przyklasną temu postępowi technologicznemu.

Świadomość kontekstu to idea, że ​​aplikacja może wykrywać i reagować na podstawie danych kontekstowych, do których ma dostęp. Taka aplikacja wykorzystuje bogate dane z czujników, które są dostępne na urządzeniu mobilnym, aby zapewnić użytkownikowi dokładne i istotne informacje w odpowiednim kontekście. Dzięki trendom, jakie obserwuje w trakcie użytkowania urządzenia i/lub dzięki informacjom zwrotnym dostarczanym przez użytkownika, taka aplikacja może „uczyć się” z czasem, stając się w ten sposób „mądrzejsza” i bardziej użyteczna.

Kompleksowe przetwarzanie zdarzeń

Złożone przetwarzanie zdarzeń (CEP) to forma przetwarzania zdarzeń, która wykorzystuje bardziej wyrafinowane analizy wielu zdarzeń (tj. w czasie, z różnych źródeł itd.), integrując i analizując ich zawartość w celu wywnioskowania bardziej znaczących informacji i wzorców.

W aplikacji mobilnej CEP można zastosować do zdarzeń generowanych z czujników urządzenia mobilnego, a także zewnętrznych źródeł danych, do których aplikacja ma dostęp.

Najważniejsze cechy naszej aplikacji cen paliw

Na potrzeby naszego złożonego samouczka dotyczącego przetwarzania zdarzeń załóżmy, że funkcje naszej aplikacji cen paliw są ograniczone do następujących elementów:

  • automatyczne wykrywanie lokalizacji, które są istotne geograficznie dla użytkownika (np. miejsce zamieszkania użytkownika i miejsce pracy użytkownika)
  • automatyczne identyfikowanie stacji paliw w rozsądnej odległości od miejsca zamieszkania i pracy użytkownika
  • automatyczne powiadamianie użytkownika o najlepszych cenach paliw w pobliżu domu i pracy

OK, zaczynajmy.

Wykrywanie lokalizacji domu i pracy użytkownika

Zacznijmy od logiki automatycznego wykrywania lokalizacji domu i pracy użytkownika. Aby uprościć nasz przykład z przetwarzaniem złożonych zdarzeń, założymy, że użytkownik ma dość normalny harmonogram pracy. Możemy zatem założyć, że użytkownik zazwyczaj będzie w domu między 2 a 3 w nocy, a w biurze między 14:00 a 15:00.

Na podstawie tych założeń definiujemy dwie reguły CEP i zbieramy dane o lokalizacji i czasie ze smartfona użytkownika:

  • Strona główna Zasada lokalizacji
    • zbierać dane o lokalizacji między 2 a 3 w nocy przez tydzień
    • grupować dane o lokalizacji, aby uzyskać przybliżony adres domowy

  • Zasada lokalizacji pracy
    • zbieraj dane o lokalizacji między 14:00 a 15:00 w dni powszednie
    • grupować dane o lokalizacji, aby uzyskać przybliżoną lokalizację pracy

Poniżej przedstawiono algorytm wysokiego poziomu do wykrywania lokalizacji.

Ten diagram pokazuje, jak będzie działać aplikacja obsługująca kontekst w tym samouczku.

Załóżmy następującą prostą strukturę danych JSON dla danych lokalizacji:

 { "uid": "some unique identifier for device/user", "location": [longitude, latitude] "time": "time in user's timezone" }

Uwaga: Zawsze dobrą praktyką jest uczynienie danych czujnika niezmiennymi (lub typu wartości), aby mogły być bezpiecznie używane przez różne moduły w przepływie pracy CEP.

Realizacja

Nasz algorytm zaimplementujemy przy użyciu wzorca modułów komponowalnych , w którym każdy moduł wykonuje tylko jedno zadanie i wywołuje następne po zakończeniu zadania. Jest to zgodne z filozofią Unix Rule of Modularity.

W szczególności każdy moduł jest funkcją, która akceptuje obiekt config i next funkcję, która jest wywoływana w celu przekazania danych do następnego modułu. W związku z tym każdy moduł zwraca funkcję, która może przyjmować dane z czujników. Oto podstawowa sygnatura modułu:

 // nominal structure of each composable module function someModule(config, next) { // do initialization if required return function(data) { // do runtime processing, handle errors, etc. var nextData = SomeFunction(data); // optionally call next with nextData next(nextData); } }

Aby zaimplementować nasz algorytm wyliczania lokalizacji domu i pracy użytkownika, będziemy potrzebować następujących modułów:

  • Moduł filtra czasu
  • Moduł akumulatora
  • Moduł klastrowania

Każdy z tych modułów został szczegółowo opisany w kolejnych podrozdziałach.

Moduł filtra czasu

Nasz filtr czasu to prosta funkcja, która przyjmuje zdarzenia danych lokalizacji jako dane wejściowe i przekazuje dane do next modułu tylko wtedy, gdy zdarzenie wystąpiło w interesującym nas przedziale czasu. Dane config dla tego modułu składają się zatem z czasów rozpoczęcia i zakończenia interesującego przedziału czasu. (Bardziej zaawansowana wersja modułu może filtrować na podstawie wielu przedziałów czasu.)

Oto implementacja pseudokodu modułu filtra czasu:

 function timeFilter(config, next) { function isWithin(timeval) { // implementation to compare config.start <= timeval <= config.end // return true if within time slice, false otherwise } return function (data) { if(isWithin(data.time)) { next(data); } }; }

Moduł akumulatora

Zadaniem akumulatora jest po prostu zbieranie danych o lokalizacji, które następnie są przekazywane do next modułu. Ta funkcja utrzymuje wewnętrzny kubeł o stałym rozmiarze do przechowywania danych. Każda nowa napotkana lokalizacja jest dodawana do zasobnika, aż zasobnik się zapełni. Zgromadzone dane o lokalizacji w wiadrze są następnie wysyłane do następnego modułu jako tablica.

Obsługiwane są dwa rodzaje kubełków akumulatorowych. Typ wiaderka wpływa na to, co jest robione z zawartością wiaderka po przekazaniu danych do następnej fazy, w następujący sposób:

  • Kubełek z oknem bębnowym ( type = 'tumbling' ): po przekazaniu danych opróżnia całe wiadro i zaczyna od nowa (zmniejszony rozmiar wiaderka z powrotem do 0)

  • Running window type ( type = 'running' ): po przekazaniu danych odrzuca tylko najstarszy element danych w zasobniku (zmniejsza rozmiar zasobnika o 1)

Oto podstawowa implementacja modułu akumulatora:

 function accumulate(config, next) { var bucket = []; return function (data) { bucket.unshift(data); if(bucket.length >= config.size) { var newSize = (config.type === 'tumbling' ? 0 : bucket.length - 1); next(bucket.slice(0)); bucket.length = newSize; } }; }

Moduł klastrowania

W geometrii współrzędnych istnieje oczywiście wiele wyrafinowanych technik klastrowania danych 2D. Oto jeden prosty sposób grupowania danych o lokalizacji:

  • znajdź sąsiadów dla każdej lokalizacji w zestawie lokalizacji
  • jeśli niektórzy sąsiedzi należą do istniejącego klastra, rozwiń sąsiadów za pomocą klastra
  • jeśli lokalizacje w zestawie sąsiadów są większe niż próg, dodaj sąsiadów jako nowy klaster

Oto implementacja tego algorytmu klastrowania (przy użyciu Lo-Dash ):

 var _ = require('lodash'); function createClusters(location_data, radius) { var clusters = []; var min_points = 5; // Minimum cluster size function neighborOf(this_location, all_locations) { return _.filter(all_locations, function(neighbor) { var distance = distance(this_point.location, neighbor.location); // maximum allowed distance between neighbors is 500 meters. return distance && (500 > distance); } } _.each(location_data, function (loc_point) { // Find neighbors of loc_point var neighbors = neighborOf(loc_point, location_data, radius); _.each(clusters, function (cluster, index) { // Check whether some of the neighbors belong to cluster. if(_.intersection(cluster, neighbors).length){ // Expand neighbors neighbors = _.union(cluster, neighbors); // Remove existing cluster. We will add updated cluster later. clusters[index] = void 0; } }); if(neighbors.length >= min_points){ // Add new cluster. clusters.unshift(neighbors); } }); return _.filter(clusters, function(cluster){ return cluster !== void 0; }); }

Powyższy kod zakłada istnienie funkcji distance() , która oblicza odległość (w metrach) między dwoma lokalizacjami geograficznymi. Przyjmuje dwa punkty lokalizacji w postaci [longitude, latitude] i zwraca odległość między nimi. Oto przykładowa implementacja takiej funkcji:

 function distance(point1, point2) { var EARTH_RADIUS = 6371000; var lng1 = point1[0] * Math.PI / 180; var lat1 = point1[1] * Math.PI / 180; var lng2 = point2[0] * Math.PI / 180; var lat2 = point2[1] * Math.PI / 180; var dLat = lat2 - lat1; var dLon = lng2 - lng1; var a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2); var arc = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); var distance = EARTH_RADIUS * arc; return distance; }

Po zdefiniowaniu i zaimplementowaniu naszego algorytmu klastrowania (w przedstawionej wcześniej funkcji createClusters() ) możemy go użyć jako podstawy dla naszego modułu klastrowania:

 function clusterize(config, next) { return function(data) { var clusters = createClusters(data, config.radius); next(clusters); }; }

Ciągnąc to wszystko razem

Wszystkie wymagane funkcje komponentów są teraz zdefiniowane, więc jesteśmy gotowi do kodowania naszych reguł lokalizacji domu/pracy.

Oto na przykład możliwa implementacja reguły lokalizacji domu:

 var CLUSTER_RADIUS = 150; // use cluster radius of 150 meters var BUCKET_SIZE = 500; // collect 500 location points var BUCKET_TYPE = 'tumbling'; // use a tumbling bucket in our accumulator var home_cluster = clusterize({radius: CLUSTER_RADIUS}, function(clusters) { // Save clusters in db }); var home_accumulator = accumulate({size: BUCKET_SIZE, type: BUCKET_TYPE}, home_cluster); var home_rule = timeFilter({start: "2AM", end: "3AM"}, home_accumulator);

Teraz za każdym razem, gdy dane lokalizacyjne są odbierane ze smartfona (przez websocket, TCP, HTTP), przekazujemy te dane do funkcji home_rule , która z kolei wykrywa klastry dla domu użytkownika.

Zakłada się wówczas, że „lokalizacja domowa” użytkownika jest środkiem klastra lokalizacji domowych.

Uwaga: chociaż może to nie być do końca precyzyjne, jest odpowiednie dla naszego prostego przykładu, zwłaszcza że celem tej aplikacji jest w każdym przypadku po prostu poznanie obszaru otaczającego dom użytkownika, a nie dokładnej lokalizacji domu użytkownika.

Oto prosta przykładowa funkcja, która oblicza „środek” zestawu punktów w klastrze na podstawie średniej szerokości i długości geograficznej wszystkich punktów w zestawie klastra:

 function getCentre(cluster_data) { var len = cluster_data.length; var sum = _.reduce(cluster_data, function(memo, cluster_point){ memo[0] += cluster_point[0]; memo[1] += cluster_point[1]; return memo; }, [0, 0]); return [sum[0] / len, sum[1] / len]; }

Podobne podejście można zastosować do wydedukowania miejsca pracy, z tą tylko różnicą, że użyje filtru czasu między 14:00 a 15:00 (w przeciwieństwie do 2 i 3 w nocy).

Dzięki temu nasza aplikacja paliwowa jest w stanie automatycznie wykrywać lokalizację pracy i domu użytkownika bez konieczności interwencji użytkownika. To jest przetwarzanie kontekstowe w najlepszym wydaniu!

Wyszukiwanie pobliskich stacji paliw

Ciężka praca nad ustaleniem świadomości kontekstu została już wykonana, ale nadal potrzebujemy jeszcze jednej reguły, aby określić, które ceny na stacjach paliw należy monitorować (tj. które stacje paliw znajdują się wystarczająco blisko domu lub miejsca pracy użytkownika, aby były istotne). Ta reguła wymaga dostępu do wszystkich lokalizacji stacji paliw we wszystkich regionach obsługiwanych przez aplikację paliwową. Zasada jest następująca:

  • Zasada stacji paliw
    • znajdź najbliższe stacje paliw dla każdej lokalizacji domu i pracy

Można to łatwo wdrożyć, korzystając z funkcji odległości pokazanej wcześniej jako filtr lokalizacji, który można zastosować na wszystkich stacjach paliw znanych aplikacji.

Aplikacje kontekstowe z czasem stają się coraz bardziej inteligentne, takie jak ta aplikacja samouczka.

Monitorowanie cen paliw

Gdy aplikacja paliwowa uzyska listę preferowanych (tj. znajdujących się w pobliżu) stacji paliw dla użytkownika, może z łatwością obserwować najlepsze ceny paliw na tych stacjach. Może również powiadamiać użytkownika, gdy jedna z tych stacji paliw ma specjalne ceny lub oferty, zwłaszcza gdy zostanie wykryte, że użytkownik znajduje się w pobliżu tych stacji paliw.

Ten złożony samouczek dotyczący przetwarzania zdarzeń pokazuje, w jaki sposób można tworzyć świadomość kontekstu w aplikacji.

Wniosek

W tym złożonym samouczku dotyczącym przetwarzania zdarzeń naprawdę ledwo zarysowaliśmy powierzchnię przetwarzania kontekstowego.

W naszym prostym przykładzie dodaliśmy kontekst lokalizacji do prostej aplikacji do raportowania cen paliwa i uczyniliśmy ją inteligentniejszą. Aplikacja zachowuje się teraz inaczej na każdym urządzeniu i z czasem wykrywa wzorce lokalizacji, aby automatycznie poprawiać wartość informacji dostarczanych użytkownikom.

Z pewnością można dodać znacznie więcej danych logicznych i czujników, aby zwiększyć dokładność i użyteczność naszej aplikacji kontekstowej. Sprytny programista mobilny może na przykład wykorzystać dane z sieci społecznościowych, dane pogodowe, dane transakcji terminala POS itd., aby jeszcze bardziej zwiększyć świadomość kontekstu naszej aplikacji i uczynić ją bardziej opłacalną i rynkową.

Dzięki przetwarzaniu kontekstowemu możliwości są nieograniczone. Coraz więcej inteligentnych aplikacji będzie pojawiać się w sklepach z aplikacjami, które wykorzystują tę potężną technologię, aby ułatwić nam życie.