上下文感知應用程序和復雜事件處理架構

已發表: 2022-03-11

全球移動電話的使用量不斷增加。 截至 2013 年,大約 73% 的互聯網用戶通過移動設備消費內容,預計到 2017 年這一比例將接近 90%。

當然,移動革命有很多原因。 但最重要的一點是移動應用程序通常可以訪問更豐富的上下文,因為當今幾乎所有的智能手機都配備了位置傳感器、運動傳感器、藍牙和 wifi。 通過利用它們的數據,應用程序可以實現“上下文感知”,從而顯著提高它們的能力和價值,並真正讓它們在應用商店中脫穎而出。

在本教程中,我們將通過一個複雜的事件處理示例來探索創建上下文感知應用程序。 我們將使用一個相當簡單的示例:一個燃料價格應用程序,它可以找到您所在地區的最佳燃料價格。

應用程序中的上下文感知可以通過像這樣的複雜事件處理教程來創建。

上下文感知應用

在 Designing Calm Technology 中,Mark Weiser 和 John Seely Brown 將冷靜技術描述為“能夠提供信息但不需要我們關注或關注的技術”。

上下文感知移動應用程序與這一概念高度一致,並且是沿著這條道路邁出的重要且有價值的一步。 他們使用從傳感器收集的上下文信息來主動為用戶提供有價值的信息,並且他們以最小的用戶努力做到這一點。 Mark Weiser 和 John Seely Brown 無疑會為這種技術進步喝彩。

上下文感知是應用程序可以根據它可以訪問的上下文數據感知和反應的想法。 這樣的應用程序利用移動設備上可用的豐富傳感器數據,在適當的上下文中向用戶提供準確和相關的信息。 通過在設備使用過程中觀察到的趨勢,和/或通過用戶提供的反饋,這樣的應用程序實際上可以隨著時間的推移“學習”,從而變得“更智能”和更有用。

複雜事件處理

複雜事件處理 (CEP) 是一種事件處理形式,它對多個事件(即,隨著時間的推移、來自不同來源等)進行更複雜的分析,整合和分析它們的內容以推斷出更有意義的信息和模式。

在移動應用程序中,CEP 可以應用於從移動設備的傳感器以及應用程序可以訪問的外部數據源生成的事件。

我們的燃油價格應用程序的主要功能

出於我們複雜事件處理教程的目的,我們假設我們的燃油價格應用程序的功能僅限於以下內容:

  • 自動檢測與用戶地理相關的位置(例如,用戶的家庭位置和用戶的工作位置)
  • 自動識別用戶家和工作地點合理距離內的加油站
  • 自動通知用戶在家和工作地點附近的最佳燃油價格

好的,讓我們開始吧。

檢測用戶的家庭和工作地點

讓我們從自動檢測用戶的家庭和工作地點的邏輯開始。 為了讓我們的複雜事件處理示例保持簡單,我們將假設用戶有一個相當正常的工作時間表。 因此,我們可以假設用戶通常會在凌晨 2 點到 3 點之間在家,並且通常會在下午 2 點到 3 點之間在他們的辦公室。

基於這些假設,我們定義了兩個 CEP 規則並從用戶的智能手機收集位置和時間數據:

  • 家庭位置規則
    • 在一周的凌晨 2 點到 3 點之間收集位置數據
    • 聚類位置數據以獲得大致的家庭地址

  • 工作地點規則
    • 收集工作日下午 2 點到 3 點之間的位置數據
    • 聚類位置數據以獲得大致的工作位置

檢測位置的高級算法如下所示。

此圖演示了本教程中的上下文感知應用程序將如何工作。

讓我們假設位置數據有以下簡單的 JSON 數據結構:

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

注意:使傳感器數據不可變(或值類型)始終是一種很好的做法,以便 CEP 工作流程中的不同模塊可以安全地使用它。

執行

我們將使用可組合模塊模式來實現我們的算法,其中每個模塊只執行一個任務,並在任務完成時調用 next。 這符合 Unix 模塊化原則。

具體來說,每個模塊都是一個函數,它接受一個config對象和一個被調用以將數據傳遞給下一個模塊的next函數。 因此,每個模塊都返回一個可以接受傳感器數據的函數。 這是模塊的基本簽名:

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

為了實現我們推斷用戶家庭和工作地點的算法,我們需要以下模塊:

  • 時間過濾模塊
  • 蓄能器模塊
  • 集群模塊

下面的小節將更詳細地描述這些模塊中的每一個。

時間過濾模塊

我們的時間過濾器是一個簡單的函數,它將位置數據事件作為輸入,並且僅當事件發生在感興趣的時間片內時才將數據傳遞給next模塊。 因此,此模塊的config數據由感興趣的時間片的開始時間和結束時間組成。 (更複雜的模塊版本可以基於多個時間片進行過濾。)

這是時間過濾器模塊的偽代碼實現:

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

蓄能器模塊

累加器的職責只是收集位置數據,然後傳遞給next模塊。 該函數維護一個內部固定大小的存儲桶來存儲數據。 遇到的每個新位置都會添加到存儲桶中,直到存儲桶已滿。 然後將桶中累積的位置數據作為數組發送到下一個模塊。

支持兩種類型的累加器桶。 桶的類型影響數據轉發到下一階段後對桶的內容做了什麼,如下:

  • 翻滾窗口桶( type = 'tumbling' ):轉發數據後,清空整個桶並重新開始(將桶大小減少回0)

  • 運行窗口類型( type = 'running' ):轉發數據後,只丟棄桶中最舊的數據元素(桶大小減1)

這是累加器模塊的基本實現:

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

集群模塊

當然,坐標幾何中有許多複雜的技術可以對 2D 數據進行聚類。 這是對位置數據進行聚類的一種簡單方法:

  • 在一組位置中查找每個位置的鄰居
  • 如果某些鄰居屬於現有集群,則使用集群擴展鄰居
  • 如果鄰居集中的位置超過閾值,則將鄰居添加為新集群

這是此聚類算法的實現(使用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; }); }

上面的代碼假設存在一個計算兩個地理位置之間距離(以米為單位)的distance()函數。 它以[longitude, latitude]的形式接受兩個位置點,並返回它們之間的距離。 以下是此類函數的示例實現:

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

通過定義和實現我們的聚類算法(在前面顯示的createClusters()函數中),我們可以將它用作我們的聚類模塊的基礎:

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

將它們拉在一起

現在定義了所有必需的組件功能,因此我們準備編寫我們的家庭/工作位置規則。

例如,這裡是家庭位置規則的可能實現:

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

現在,無論何時從智能手機(通過 websocket、TCP、HTTP)接收到位置數據,我們都會將此數據轉發給home_rule函數,該函數進而檢測用戶家的集群。

然後假設用戶的“家庭位置”是家庭位置集群的中心。

注意:雖然這可能不完全準確,但對於我們的簡單示例來說已經足夠了,尤其是因為無論如何這個應用程序的目標只是了解用戶家周圍的區域,而不是了解用戶的精確家庭位置。

這是一個簡單的示例函數,它通過平均集群集中所有點的緯度和經度來計算集群中一組點的“中心”:

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

可以採用類似的方法來推斷工作地點,唯一的區別是它將使用下午 2 點到 3 點之間的時間過濾器(而不是凌晨 2 點到 3 點)。

因此,我們的燃料應用程序能夠自動檢測用戶的工作和家庭位置,而無需任何用戶干預。 這是最好的上下文感知計算!

尋找附近的加油站

建立情境感知的艱鉅工作現已完成,但我們仍需要一個規則來識別要監控的加油站價格(即,哪些加油站離用戶家或工作地點足夠近以便相關)。 此規則需要訪問燃料應用程序支持的所有區域的所有加油站位置。 規則如下:

  • 加油站規則
    • 為每個家庭和工作地點查找最近的加油站

這可以使用前面顯示的距離函數作為位置過濾器輕鬆實現,以應用於應用程序已知的所有加油站。

上下文感知應用程序會隨著時間的推移變得更加智能,例如本教程應用程序。

監控燃油價格

一旦燃料應用程序為用戶獲取了首選(即附近)加油站的列表,它就可以輕鬆地查看這些加油站的最佳燃油價格。 它還可以在這些加油站之一有特價或優惠時通知用戶,尤其是當檢測到用戶在這些加油站附近時。

這個複雜的事件處理教程演示瞭如何在應用程序中創建上下文感知。

結論

在這個複雜的事件處理教程中,我們幾乎沒有觸及上下文感知計算的表面。

在我們的簡單示例中,我們將位置上下文添加到原本簡單的燃油價格報告應用程序中,並使其更智能。 該應用程序現在在每台設備上的行為都不同,並且隨著時間的推移會檢測位置模式以自動提高其提供給用戶的信息的價值。

當然可以添加更多的邏輯和傳感器數據來提高我們的上下文感知應用程序的準確性和實用性。 例如,一個聰明的移動開發者可以利用社交網絡數據、天氣數據、POS 終端交易數據等來為我們的應用程序添加更多的上下文感知,使其更具可行性和市場價值。

使用上下文感知計算,可能性是無窮無盡的。 越來越多的智能應用程序將繼續出現在應用程序商店中,這些應用程序利用這項強大的技術讓我們的生活變得更簡單。