Kontextbewusste Anwendungen und komplexe Ereignisverarbeitungsarchitektur

Veröffentlicht: 2022-03-11

Die Nutzung von Mobiltelefonen nimmt weltweit stetig zu. Im Jahr 2013 konsumierten rund 73 % der Internetnutzer Inhalte über ein mobiles Gerät, und dieser Prozentsatz wird voraussichtlich bis 2017 fast 90 % erreichen.

Natürlich gibt es viele Gründe für die mobile Revolution. Einer der wichtigsten ist jedoch, dass mobile Apps im Allgemeinen Zugriff auf einen umfassenderen Kontext erhalten, da fast alle Smartphones heute mit Standortsensoren, Bewegungssensoren, Bluetooth und WLAN ausgestattet sind. Durch die Nutzung ihrer Daten können Apps ein „Kontextbewusstsein“ erreichen, das ihre Fähigkeiten und ihren Wert dramatisch steigern und sie in App-Stores wirklich hervorheben kann.

In diesem Lernprogramm untersuchen wir die Erstellung kontextbezogener Apps anhand eines Beispiels für die Verarbeitung komplexer Ereignisse. Wir verwenden ein ziemlich einfaches Beispiel: eine Kraftstoffpreis-App, die die besten Kraftstoffpreise in Ihrer Nähe findet.

Kontextbewusstsein in Apps kann durch ein komplexes Ereignisverarbeitungs-Tutorial wie dieses erstellt werden.

Kontextbewusste Apps

In Designing Calm Technology beschreiben Mark Weiser und John Seely Brown ruhige Technologie als „das, was informiert, aber nicht unseren Fokus oder unsere Aufmerksamkeit fordert“.

Kontextbewusste mobile Apps stehen in hohem Maße im Einklang mit dieser Vorstellung und sind ein wichtiger und wertvoller Schritt auf diesem Weg. Sie verwenden kontextbezogene Informationen, die von ihren Sensoren gesammelt werden, um dem Benutzer proaktiv wertvolle Informationen bereitzustellen, und zwar mit minimalem Aufwand seitens des Benutzers. Mark Weiser und John Seely Brown würden diesen technologischen Fortschritt zweifellos begrüßen.

Kontextbewusstsein ist die Idee, dass eine App basierend auf den Kontextdaten, auf die sie Zugriff hat, erkennen und reagieren kann. Eine solche App nutzt reichhaltige Sensordaten, die auf einem mobilen Gerät verfügbar sind, um dem Benutzer genaue und relevante Informationen im entsprechenden Kontext bereitzustellen. Durch Trends, die sie im Laufe der Nutzung des Geräts beobachtet, und/oder durch Feedback des Benutzers, kann eine solche App mit der Zeit sogar „lernen“ und wird dadurch „intelligenter“ und nützlicher.

Komplexe Ereignisverarbeitung

Komplexe Ereignisverarbeitung (CEP) ist eine Form der Ereignisverarbeitung, die ausgefeiltere Analysen mehrerer Ereignisse (dh im Laufe der Zeit, aus verschiedenen Quellen usw.) verwendet und deren Inhalt integriert und analysiert, um aussagekräftigere Informationen und Muster abzuleiten.

In einer mobilen App kann CEP auf Ereignisse angewendet werden, die von den Sensoren des mobilen Geräts generiert werden, sowie auf externe Datenquellen, auf die die App Zugriff hat.

Hauptmerkmale unserer Spritpreis-App

Nehmen wir für unser komplexes Ereignisverarbeitungs-Tutorial an, dass die Funktionen unserer Kraftstoffpreis-App auf die folgenden beschränkt sind:

  • Automatisches Erkennen von Orten, die für den Benutzer geografisch relevant sind (z. B. der Wohnort des Benutzers und der Arbeitsort des Benutzers)
  • Automatisches Identifizieren von Tankstellen in angemessener Entfernung von den Wohn- und Arbeitsorten des Benutzers
  • automatische Benachrichtigung des Benutzers über die besten Kraftstoffpreise in der Nähe von Wohnort und Arbeitsplatz

Okay, fangen wir an.

Erkennen des Wohn- und Arbeitsortes des Benutzers

Beginnen wir mit der Logik zum automatischen Erkennen des Wohn- und Arbeitsorts des Benutzers. Um die Dinge für unser komplexes Ereignisverarbeitungsbeispiel einfach zu halten, gehen wir davon aus, dass der Benutzer einen ziemlich normalen Arbeitszeitplan hat. Wir können daher davon ausgehen, dass der Benutzer normalerweise zwischen 2 und 3 Uhr morgens zu Hause und normalerweise zwischen 14 und 15 Uhr in seinem Büro ist.

Basierend auf diesen Annahmen definieren wir zwei CEP-Regeln und erfassen Standort- und Zeitdaten vom Smartphone des Benutzers:

  • Regel für den Heimatort
    • Sammeln Sie eine Woche lang zwischen 2 und 3 Uhr morgens Standortdaten
    • Clustern Sie Standortdaten, um die ungefähre Heimatadresse zu erhalten

  • Arbeitsortregel
    • Sammeln Sie Standortdaten an Wochentagen zwischen 14 und 15 Uhr
    • Standortdaten gruppieren, um den ungefähren Arbeitsstandort zu erhalten

Der allgemeine Algorithmus zum Erkennen von Standorten ist unten dargestellt.

Dieses Diagramm zeigt, wie die kontextsensitive Anwendung in diesem Lernprogramm funktioniert.

Nehmen wir die folgende einfache JSON-Datenstruktur für Standortdaten an:

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

Hinweis: Es empfiehlt sich immer, Sensordaten (oder Werttypen) unveränderlich zu machen, damit sie sicher von verschiedenen Modulen im CEP-Workflow verwendet werden können.

Implementierung

Wir werden unseren Algorithmus mithilfe eines zusammensetzbaren Modulmusters implementieren, wobei jedes Modul nur eine Aufgabe ausführt und die nächste aufruft, wenn die Aufgabe abgeschlossen ist. Dies entspricht der Unix Rule of Modularity-Philosophie.

Insbesondere ist jedes Modul eine Funktion, die ein config und eine next Funktion akzeptiert, die aufgerufen wird, um die Daten an das nächste Modul weiterzuleiten. Dementsprechend gibt jedes Modul eine Funktion zurück, die Sensordaten annehmen kann. Hier ist die grundlegende Signatur eines Moduls:

 // 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); } }

Um unseren Algorithmus zur Ableitung der Wohn- und Arbeitsorte des Benutzers zu implementieren, benötigen wir die folgenden Module:

  • Zeitfiltermodul
  • Akkumodul
  • Clustering-Modul

Jedes dieser Module wird in den folgenden Unterabschnitten ausführlicher beschrieben.

Zeitfiltermodul

Unser Zeitfilter ist eine einfache Funktion, die Standortdatenereignisse als Eingabe verwendet und Daten nur dann an das next Modul weiterleitet, wenn das Ereignis innerhalb der interessierenden Zeitscheibe aufgetreten ist. Die config für dieses Modul bestehen daher aus den Start- und Endzeiten der interessierenden Zeitscheibe. (Eine ausgefeiltere Version des Moduls könnte basierend auf mehreren Zeitscheiben filtern.)

Hier ist eine Pseudocode-Implementierung des Zeitfiltermoduls:

 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); } }; }

Akkumodul

Die Verantwortung des Akkumulators besteht einfach darin, Standortdaten zu sammeln, die dann an das next Modul weitergegeben werden. Diese Funktion verwaltet einen internen Bucket mit fester Größe zum Speichern von Daten. Jeder neu angetroffene Standort wird dem Bucket hinzugefügt, bis der Bucket voll ist. Die gesammelten Standortdaten im Bucket werden dann als Array an das nächste Modul gesendet.

Es werden zwei Arten von Akkumulator-Buckets unterstützt. Der Bucket-Typ wirkt sich wie folgt darauf aus, was mit dem Inhalt des Buckets gemacht wird, nachdem die Daten an die nächste Phase weitergeleitet wurden:

  • Taumelnder Fenster-Bucket ( type = 'tumbling' ): nach dem Weiterleiten von Daten wird der gesamte Bucket geleert und neu gestartet (reduzierte Bucket-Größe zurück auf 0)

  • Laufender Fenstertyp ( type = 'running' ): Verwirft nach dem Weiterleiten von Daten nur das älteste Datenelement im Bucket (reduziert die Bucket-Größe um 1)

Hier ist eine grundlegende Implementierung des Akkumulatormoduls:

 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; } }; }

Clustering-Modul

Es gibt natürlich viele ausgefeilte Techniken in der Koordinatengeometrie, um 2D-Daten zu clustern. Hier ist eine einfache Möglichkeit, Standortdaten zu gruppieren:

  • Finden Sie Nachbarn für jeden Ort in einer Reihe von Orten
  • wenn einige der Nachbarn zu einem bestehenden Cluster gehören, dann erweitern Sie die Nachbarn mit Cluster
  • Wenn Standorte im Nachbarsatz größer als der Schwellenwert sind, füge Nachbarn als neuen Cluster hinzu

Hier ist eine Implementierung dieses Clustering-Algorithmus (mit 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; }); }

Der obige Code geht von der Existenz einer Funktion distance() aus, die die Entfernung (in Metern) zwischen zwei geografischen Orten berechnet. Es akzeptiert zwei Standortpunkte in Form von [longitude, latitude] und gibt die Entfernung zwischen ihnen zurück. Hier ist eine Beispielimplementierung einer solchen Funktion:

 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; }

Wenn unser Clustering-Algorithmus definiert und implementiert ist (in unserer zuvor gezeigten Funktion createClusters() ), können wir ihn als Grundlage für unser Clustering-Modul verwenden:

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

Alles zusammenziehen

Alle erforderlichen Komponentenfunktionen sind jetzt definiert, sodass wir bereit sind, unsere Heim-/Arbeitsstandortregeln zu codieren.

Hier ist zum Beispiel eine mögliche Implementierung der Heimatortsregel:

 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);

Wenn nun Standortdaten vom Smartphone (über Websocket, TCP, HTTP) empfangen werden, leiten wir diese Daten an die home_rule Funktion weiter, die wiederum Cluster für das Zuhause des Benutzers erkennt.

Es wird dann angenommen, dass der "Heimatstandort" des Benutzers das Zentrum des Heimatstandort-Clusters ist.

Hinweis: Dies ist zwar nicht ganz genau, aber für unser einfaches Beispiel ausreichend, zumal das Ziel dieser App ohnehin nur darin besteht, die Umgebung des Wohnorts des Benutzers zu kennen, anstatt den genauen Wohnort des Benutzers zu kennen.

Hier ist eine einfache Beispielfunktion, die das „Zentrum“ einer Menge von Punkten in einem Cluster berechnet, indem sie die Breiten- und Längengrade aller Punkte in der Clustermenge mittelt:

 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]; }

Ein ähnlicher Ansatz könnte für die Ableitung des Arbeitsorts verwendet werden, mit dem einzigen Unterschied, dass ein Zeitfilter zwischen 14 und 15 Uhr (im Gegensatz zu 2 und 3 Uhr) verwendet würde.

Unsere Tank-App ist somit in der Lage, den Arbeits- und Wohnort des Benutzers automatisch zu erkennen, ohne dass ein Benutzereingriff erforderlich ist. Das ist kontextbewusstes Computing vom Feinsten!

Tankstellen in der Nähe finden

Die harte Arbeit zur Schaffung von Kontextbewusstsein ist nun getan, aber wir brauchen noch eine weitere Regel, um zu identifizieren, welche Tankstellenpreise überwacht werden sollen (dh welche Tankstellen nahe genug am Wohn- oder Arbeitsort des Benutzers liegen, um relevant zu sein). Diese Regel benötigt Zugriff auf alle Tankstellenstandorte für alle Regionen, die von der Tank-App unterstützt werden. Die Regel lautet wie folgt:

  • Tankstellenregel
    • Finden Sie die nächstgelegenen Tankstellen für jeden Wohn- und Arbeitsort

Dies lässt sich einfach über die zuvor gezeigte Entfernungsfunktion als Standortfilter für alle der App bekannten Tankstellen realisieren.

Kontextbewusste Anwendungen werden mit der Zeit intelligenter, wie diese Tutorial-App.

Kraftstoffpreise überwachen

Sobald die Tank-App die Liste bevorzugter (dh in der Nähe befindlicher) Tankstellen für den Benutzer erhält, kann sie leicht nach den besten Kraftstoffpreisen an diesen Tankstellen Ausschau halten. Es kann den Benutzer auch benachrichtigen, wenn eine dieser Tankstellen Sonderpreise oder Angebote hat, insbesondere wenn festgestellt wird, dass sich der Benutzer in der Nähe dieser Tankstellen befindet.

Dieses Tutorial zur komplexen Ereignisverarbeitung zeigt, wie Kontextsensitivität in einer Anwendung erstellt werden kann.

Fazit

In diesem komplexen Lernprogramm zur Ereignisverarbeitung haben wir wirklich kaum an der Oberfläche des kontextsensitiven Computing gekratzt.

In unserem einfachen Beispiel haben wir einer ansonsten einfachen App für Kraftstoffpreisberichte einen Standortkontext hinzugefügt und sie intelligenter gemacht. Die App verhält sich jetzt auf jedem Gerät anders und erkennt im Laufe der Zeit die Standortmuster, um den Wert der Informationen, die sie ihren Benutzern bietet, automatisch zu verbessern.

Sicherlich können noch viel mehr Logik- und Sensordaten hinzugefügt werden, um die Genauigkeit und Nützlichkeit unserer kontextsensitiven App zu erhöhen. Ein cleverer Entwickler von Mobilgeräten könnte beispielsweise Daten aus sozialen Netzwerken, Wetterdaten, Transaktionsdaten von POS-Terminals usw. nutzen, um unserer App noch mehr Kontextbewusstsein zu verleihen und sie rentabler und marktfähiger zu machen.

Mit kontextsensitivem Computing sind die Möglichkeiten endlos. Immer mehr intelligente Apps werden in App Stores erscheinen, die diese leistungsstarke Technologie einsetzen, um unser Leben einfacher zu machen.