边缘流:一种用于分层边缘计算系统中分布式应用的流处理方法
王小阳∗†,周哲∗†,韩平†,孟通∗,孙广宇∗†1,翟继东‡
∗北京大学,北京,中国,{yaoer, zhou.zhe, mengtong, gsun}@pku.edu.cn
先进信息技术研究院, 杭州, 浙江, 中国, hpdgy@163.com
‡清华大学,北京,中国,zhaijidong@tsinghua.edu.cn
摘要
随着物联网设备的快速增长,传统的云计算方案对于许多基于物联网的应用而言效率低下,主要由于网络数据洪流、高延迟和隐私问题。为此,提出了边缘计算方案以缓解这些问 题。然而,在边缘计算系统中,应用开发变得更加复杂,因为它涉及越来越多层级的边缘节点。尽管已有一些研究工作被提出,现有的边缘计算框架在各种应用场景中仍存在一些局限性。
为了克服这些局限性,我们提出了一种新的编程模型,称为边缘流模型。它是一种简单且对程序员友好的模型,能够覆盖边缘计算中的典型场景。此外,我们在该模型中解决了数据共享和区域感知等若干新问题。我们还基于边缘流模型实现了一个边缘计算框架原型,并基于该原型进行了综合评估。实验结果证明了该模型的有效性。
索引术语— 边缘计算;编程模型;
一、引言
物联网(IoT)设备近年来经历了指数级增长。基于物联网技术的应用已广泛应用于医疗保健、交通和制造业等多个领域。传统上,这些地理上分布的设备所收集的数据会在数据中心进行集中处理。然而,由于连接的物联网设备迅速增加,这种解决方案可能导致网络被大量数据流量淹没。此外,当终端用户对延迟敏感时,将所有原始数据传输到远程数据中心进行计算既不高效也不必要。更重要的是,许多现代应用对数据位置感知和隐私保护提出了要求,而传统云计算无法满足这些需求。
边缘计算的概念作为一种有前景的方案被提出,以解决这些问题。其基本思想是将处理任务卸载到靠近物联网设备的位置,利用附近节点的计算能力。此外,5G通信技术的发展推动了在网络中广泛部署具备常规操作系统和充足存储空间的强大服务器,例如宏基站、小基站、云粒服务器和接入点[26]。因此,边缘计算
1通讯作者
该方案能够提高这些资源的利用率,并避免因大量数据流量而使网络过载。如图1所示,整个网络中的所有参与者通常被组织成一种分层结构。数据中心位于根部,而物联网设备构成叶节点。中间的计算节点则构成内部节点。该结构中的所有节点都具有不同程度的计算和存储能力,这些能力累积起来相当可观。近年来,工业界和学术界提出了大量边缘计算方法[5][20][26],充分展示了边缘计算系统在延迟、能耗和隐私保护等方面的优势。
然而,在边缘计算系统中,应用程序开发效率仍然是一个关键问题。与两层的云计算架构相比,由于引入了新的参与者(即边缘节点),边缘计算框架通常更为复杂。因此,让开发者在这样一组多层节点上手动管理底层并行性和资源分配细节将过于复杂。为了使边缘计算方案具有实用性,必须提供一种专用的高级编程模型。该模型应独立于物联网网络的物理拓扑结构,并能够覆盖各种物联网应用的大多数常见场景。
一些项目已经注意到此类需求,并通过提供适当的抽象来关注应用开发的便利性。它们从各个方面探索了大量技术,充分利用可用资源以最大化其优化目标。然而,大多数现有方法存在一些共同的局限性,影响了其在实际部署中的效率。具体解释如下。
首先,一些框架关注的是网络中仅运行单个应用程序的情况。在多任务场景下,如果终端设备被划分为各个任务独立的组,则系统可以像单任务系统一样正常运行。但是,如果它们共享同一组数据源,每个任务将重复请求相同的数据,导致冗余数据流量。一些商业产品[3][28][14][2]和社区项目[10][11]尝试通过在物联网设备上引入消息队列服务来解决该问题。这实际上提供了
一种用于不同任务的发布‐订阅接口。然而,这种类型的抽象对开发者并不友好。用户必须手动管理数据传输细节。
其次,在实际应用中,终端参与者之间的数据移动十分复杂。大多数研究方法仅关注某些特殊情况的优化,例如将物联网传感器的数据收集到云,或者反向地将广播数据内容传输到终端设备。然而,实际应用可能需要更复杂的数据传输路径。
第三,物联网所有者之间的协作尚未得到探索。物联网设备通常为不同目的而单独部署。随着设备数量的持续增加,不同设备采集的数据也有可能被结合用于新的任务。因此,如何实现用户之间的数据共享成为一个新的挑战。
为了克服这些局限性,我们提出了一种新的编程模型,称为边缘流。在此模型中,数据流被表示为流,从而对程序员隐藏了底层细节。此外,流可以在不同任务之间共享。该模型支持多种数据移动模式,以涵盖常见场景。
本文的其余部分组织如下。在第二节中,我们回顾了边缘计算的基础知识,并介绍了四个典型场景。此外,还讨论了现有方法的局限性。为了克服这些局限性,在第三节中提出了边缘流(Edge‐Stream)模型。基于该模型,我们在第四节中进一步探讨了若干关键的实现问题。第五节给出了评估设置和结果,第六节为结论。
II. 相关工作
在本节中,我们首先回顾边缘计算的基本思想以及支持边缘计算的现有框架。接着,我们研究了四种可利用边缘计算的物联网应用典型场景。随后,我们总结了当前方法的局限性,这些局限性促使我们提出了边缘流(Edge‐Stream)。
A. 边缘计算基础
随着物联网设备数量的快速增长,它们产生的数据和通信流量呈指数级增加。尽管现代云系统在计算和存储容量方面具有弹性,但这些嵌入式和移动设备通常距离数据中心较远。因此,传统云计算可能会面临严重的往返延迟和网络拥堵问题。为缓解这一问题,研究人员提出了边缘计算的概念。其目标是在更靠近物联网设备的网络边缘执行更多的数据处理任务。Mahmud et al.[26]对此领域进行了详细的综述。在各个方面均存在挑战与机遇[34]。
为了将这一理念变为现实,所有靠近物联网设备的节点类型都参与进来,共享计算和存储资源。云计算服务提供商如亚马逊AWS Greengrass[3],、微软Azure IoT Hub [28],和谷歌云物联网边缘 [14]提供了最直观的解决方案。借助专用的硬件和软件工具包,物联网设备能够执行一些本地推理,并管理与最近数据中心的数据交换。CloudPath[29]支持在客户端设备与传统广域云数据中心之间的网络路径上,沿数据中心序列执行第三方应用程序。云粒 [32]在互联网边缘部署称为云朵服务器的小型 “数据中心”,以降低应用延迟。早期的边缘计算系统 Paradrop [39]则利用接入点、智能路由器以及网络中其他现有组件的潜在计算能力。总体而言,这些基础设施在地理上靠近物联网设备,并具备相当的计算和存储能力。
B. 边缘计算中的典型场景
物联网设备已应用于大量不同的应用中。这些应用在设备数量、硬件平台、服务类型、通信基础设施等方面差异很大。因此,从这些应用中抽象出一些常见场景以提出高效的编程模型显得尤为重要。为此,我们根据数据流引入了四种典型的场景抽象,如图2所示。它们可以涵盖许多能够利用边缘计算的代表性应用。
第一种场景称为物联网‐边缘‐云,如图2 (a)所示。从数据流可以看出,系统将来自广泛分布的地理分布的物联网设备的数据汇集到多个数据中心进行处理。在这种情况下,通常需要一条从物联网设备到数据中心的完整路径数据传输。因此,沿路径的边缘设备上进行一些预计算可以显著减少数据量或整体延迟。Liuet al.[24]将马尔可夫决策过程(MDP)方法引入优化算法,从而降低了每项任务的平均延迟。Edgeflow [43]提供了一种考虑网络约束的解决数据突发问题的方法。Flask [27]允许程序员
使用函数式语言OCaml在传感器网络上通过高级编程连接语言描述其数据流图。Couper [19]专注于在三层边缘计算系统上部署深度学习算法。它支持快速创建用于视觉分析的生产级深度神经网络切片,并使其能够在现代基于容器的边缘软件栈中部署。
第二种场景称为Cloud-Edge-IoT,如图2 (b) 所示。这种场景常见于实时流媒体等应用。在这种情况下,数据从数据中心向下推送到大量终端设备。其核心思想是在网络边缘缓存热点内容,从而减少内容的传输延迟,并缓解数据中心的带宽压力。内容分发网络 [36] 通过在互联网边缘部署缓存服务器,已成为基于互联网缓存的一种成熟方法。以信息为中心的网络 [1] 利用无线缓存基础设施为移动用户提供内容分发服务。Femtocaching[13] 研究了一种无线分布式缓存网络,通过处理已缓存的热门文件请求来辅助宏基站。邢等人[40] 提出了一种分布式多级存储模型,为具有有限存储容量和网络稳定性差的物联网设备提供存储与计算服务。
第三种场景在本文中被称为物联网‐边缘‐物联网 ,如图2(c)所示。它指的是数据生产者和消费者均为终端物联网设备的应用。在追踪数据流时,我们发现数据请求通常来自地理上靠近数据源设备的设备。针对此场景的一种直接的边缘处理方法依赖于互连本地网络,例如无线传感器网络(WSN)[30]。DDF [12]提出了一种使用数据流模型开发程序的方法。此类场景常见于智能交通和智能制造等应用。事实上,亚马逊AWS Greengrass [3],、微软Azure物联网中心[28]和谷歌云物联网边缘[14]已提供了其架构,其中传感器收集数据进行分析,执行器作出响应。
到结果。据我们所知,目前很少有框架能够提供基于范围的数据聚合。例如,EdgeX [10] 和 AWS Greengrass [3] 允许用户创建主题以从源收集数据,并让接收器进行订阅。然而,这些主题大多需要预先定义,而无法动态跟随数据汇。如果一辆车辆想要了解其周围汽车的数量,则很难选择合适的话题进行累积。该案例的详细描述见第 III‐D小节。
最后一种类型称为物联网‐边缘,它关注一种特殊场景,即数据处理结果由相同的数据源设备收集。这种情况在移动应用中很常见,例如游戏、虚拟现实和增强现实。如果物联网或移动设备无法自行完成计算,则必须借用附近其他强大节点的计算资源。云粒 [32]允许设备发现附近的云朵服务器,以帮助其分担工作负载。移动雾[18]提出了一种平台即服务(PaaS)模型,为开发者提供简化的编程接口。Paradrop [39]为终端用户提供了多租户平台。Chen等[8]将多用户计算卸载过程建模为一种博弈,调度问题被转化为达到纳什均衡。
C. 我们方法的动机
从上一节的讨论中可以看出,大多数边缘计算应用可以归类为上述某一种场景或多种场景的组合,例如复杂的智慧城市系统[44]。然而,大多数最先进的边缘计算系统/基础设施仅关注单一场景,目前仍然缺乏能够支持所有场景的统一编程模型。此外,一些边缘计算系统未能很好地隐藏底层设计细节。而且,某些边缘计算基础设施需要特殊的硬件支持,例如亚马逊AWS Greengrass[3],微软Azure物联网中心[28],和谷歌云物联网边缘[14]。因此,在实际应用中开发边缘计算效率低下,特别是当一个应用涉及多个场景时。
数据中心中的传统分布式计算框架,包括 Flink[6]和 Storm[35],,旨在处理来自物联网设备、Web应用和社交媒体生成的连续数据包。采用流处理模型来描述任务。同一类型的数据包的无限序列被抽象为一个stream。开发者应用一系列operator来处理流中的每个元素。这种高层抽象对程序员友好,并且能够有效地描述数据中心中不同类型的任务。
受该方法的启发,我们引入了一种新的流处理模型——边缘流(Edge‐Stream),用于边缘计算场景。该模型非常简单且易于编程,但又足够通用,能够覆盖上述典型场景。同时,我们在商用硬件上基于边缘流模型实现了一个边缘计算框架的原型。详细内容将在后续章节中介绍。
III. 边缘流模型
在本节中,我们主要介绍边缘流模型的各个组件。首先,我们提供一个案例研究以便于解释。然后,我们从每个基本组件的角度描述流抽象。由于边缘流模型借鉴了传统文件系统的一些概念,我们还比较了两者之间的某些重合部分,并强调了流的特定特征。
A. 基本组件
当我们使用边缘流模型来描述一个场景时,它包含几个基本组件,包括“数据序列”、“流”和“算子”。它们的定义将通过图3所示的智能交通系统的一个简单示例进行介绍。
a) 数据序列 :数据序列是模型中算子可操作的基本单位。如图3 (a)所示,每个摄像头生成一个捕获车辆的数据序列。这些数据序列随后在系统中用于数据分析(例如车牌识别)。
b) 流 :流是一组数据序列,这些数据序列通过相同的算子进行处理。如图3 (a)所示,共有四个摄像头。来自这些摄像头的所有数据序列被分组为一个视频流。
c) 算子 :算子是边缘系统中用户定义的函数。它接收一个或多个流作为输入,并生成单个流作为输出。以图3 (a)中的示例为例,包含两个算子。第一个算子是车牌识别(识别)。它将摄像头的视频流作为输入,并生成一个包含车辆车牌信息的车牌流。第二个算子是保存。它将车牌流作为输入,并将其转换为用于存储的新流。
该示例的抽象视图如图3(b)所示。它包含三个流和两个算子。在每个抽象视图中,我们将算子放在上方,并将所有流分配在下方。在本文的其余部分,我们将使用抽象视图来介绍更多示例。基于边缘流模型的相应代码如清单 1所示。第一行声明了两个算子(即识别和保存)。第二行导入了视频流。车牌流和存储流分别通过最后两行中的算子生成。
require识别,保存
import视频流
车牌流 =识别 视频流
存储流 = 保存车牌流
清单1:车牌识别任务的代码
为了更好地理解边缘流模型中的这些组件,我们使用文件系统作为类比。边缘系统中的流可以类比为文件系统中的文件。数据序列可以类比为每个文件中的一行。算子可以类比为shell命令,例如tail, grep, cat。我们还提供了一个通过Linux shell命令操作文件的示例,如图4所示,以展示其相似性。命令cat读取 raw.txt中的每一行,并通过管道传递。该管道进程将在 PipeFS(一个挂载在内核中的特殊文件系统)中创建一个新的管道文件。然后命令grep读取管道文件中的每一行,并将结果写入result.txt。显然,图4中操作文件的示例与图3(b)中使用边缘流模型编程的示例非常相似。边缘流模型中“文件(流)”和“shell命令(算子)”的设计细节将在接下来的子章节中介绍。
B. 流设计
除了数据序列集合外,流还维护一个元数据列表以表明其属性。算子需要检查这些元数据项,以确定该流是否符合输入要求。流元数据的关键项列于表I中。其他次要特征(如流名称)已从列表中省略。
| 元数据 | 含义 |
|---|---|
| Type | 将流分为若干类别:原始的、虚拟的、生成的。 |
| Owner | 指示创建并拥有该流的用户流。 |
| Window | 描述流的窗口类型。 |
| Serializer | 指示序列化方法。 |
根据元数据中的类型字段,流被分为三种类型,介绍如下。
原始流直接由终端物理设备生成。例如,由终端传感器、监控摄像头、来自云的流媒体等生成的流。因此,图 5(a)中的视频流是原始流。
虚拟流引入了另一种类型的数据源。与原始流不同,它不依赖于任何物理设备。边缘系统中的每个节点都可以维护相同虚拟流的本地副本。因此,无需在节点之间传输虚拟流。图5(b)中所示的定时器流是一种典型的虚拟流,它每分钟提供一次当前时间。
•生成的流是由算子产生的输出,如图5(c)所示。换句话说,它始终依赖于现有流(即算子的输入)。我们将这些流称为该生成流的“父流”。例如,图3中提到的车牌流被归类为生成的流,而视频流是其父流。
原始流是系统中数据的原始来源。它们基本上分为两种:基于设备的和基于范围的。它们分别从物理设备列表和区域列表获取数据序列。例如,流所有者X部署了一组传感器,并能够从中获取数据序列,因此这是一个基于设备的流。另一种情况是,流所有者Y希望为车辆提供服务,但并不拥有车辆。那么Y可以根据一组区域创建一个基于范围的原始流。任何进入该区域的车辆都会自动成为该流的数据源,从而能够访问Y的服务。更多实现细节在第四节中介绍。
由于对无限流的聚合永远不会返回,因此在传统的流计算框架(如 Flink [6] 和 Beam[4])中,窗口的概念被广泛接受。它表示需要聚合的数据范围。在边缘流模型中,窗口的使用方式保持不变。图6展示了一个包含四个数据序列的流,我们重点关注其中一个。固定窗口将序列划分为固定宽度且不重叠的时间间隔。滑动窗口仅考虑起始点,并允许重叠。例如,图6(b)中的每个窗口都包含3 秒的数据,但每隔2秒就会启动一个新的窗口。
Serializer 字段指示流中数据序列的序列化方法。给定一个序列化器后,算子能够解码输入数据并正确访问其内容。常用的方法包括 JSON、Protobuf 和 Kyro。
为了阐明窗口的使用方法,我们以车辆计数任务为例。用户希望了解每小时每个摄像头捕获的汽车数量。他们将重用从代码1生成的PlateStream。如代码清单3所示,FWindow生成一个具有新流的
来自车牌流的一小时窗口。注意FWindow后面跟着一对括号用于非流参数(即“1h”)。然后通过管道传递给 ‘VCnt’算子以执行最终计数。显然,管道的使用方式也类似于文件系统中的用法。
require FWindow,VCnt,保存
import车牌流
计数结果 = FWindow("1小时") 车牌流 \| VCnt
存储结果 = 保存计数结果
清单3:车辆计数任务的代码
C. 算子设计
在边缘流模型中,有两种类型的算子,即重构和计算。重构操作符用于定义如何组织现有的数据序列,而不改变其中的数据。例如,联合算子收集每个输入流中的所有数据序列,并将它们视为一个新的流。代码3中的 FWindow算子也属于此类。它仅改变系统处理数据序列的方式。这一概念类似于C语言中的字符串视图方法。++
语言。
计算操作符则相反,它们从输入流生成新数据。它们对输入流中的每个数据序列应用函数。形式上,具有函数 f 的算子作用于包含三个数据序列{a,b, c}的流的过程如公式1所示。因此,函数是设计算子的关键。
Operator f({a, b, c})={f(a), f(b), f(c)} (1)
函数通过一组标准 API 访问每个数据序列中的数据包。其中两个最重要的 API 是 getNext 和 getWindow。它们分别对应两种不同类型的函数:映射和归约。映射函数从流中接收一个数据序列,并使用 getNext 访问该数据序列中的每个数据项,然后为输出流生成一个新的数据序列。归约函数则利用 getWindow 遍历数据序列中每个窗口内的数据包。它会等待窗口中的所有数据在一个物理节点上累积完毕后才开始处理。这些是大多数分布式系统所采用的基本组件[9][7][6]。这些 API 不限制算子的数据类型,从而支持异构数据源。数据序列中数据包的数据类型在算子中隐式声明,如下文示例所示(清单4 和 5)。
清单4中的代码片段提供了图3中Recog算子对应函数的简化实现。它是一个映射函数。代码以C++语言的初始化部分开始。接下来的声明部分描述了输入流和输出流。在本示例中,有一条由图片对象组成的输入流S_视频。输出流S_plate提供std::字符串对象。最后该部分使用C++代码实现计算逻辑。第一行中的 getNext函数从流中逐个弹出元素。“%%”、“%{”和 “%}”是用于分隔各部分的标点符号。
#include <字符串>
#include"My识别Lib" %%%inS_视频<图片, null, 文件>%outS_车牌<std::字符串, null, JSON>%%%{autoinPicture= S_ video.getNext();autooutPlate= PlateRecog(inPicture);S_ plate.pushItem(outPlate);%}
List 4: 识别函数实现
代码列表5 实现了车辆计数算子。输入流 S_plate 已通过固定窗口进行分段。输出流 S_result 提供无窗口的计数结果。两者均以 JSON 格式序列化。该函数旨在计算每个窗口中车牌的总数。getWindow 函数以归约式方式执行。它收集每个窗口中的数据并返回一个元素向量。
#include <字符串> %%%inS_plate<std::字符串, fixed, JSON>% outS_result<int, null,JSON>%%% {intcounter = 0;autoplates =S_ plate.getWindow();for(plate: plates){counter ++;}S_ result.pushItem(counter);%}
List 5: Vehicle 计数 ing function implementat ion
边缘流模型还支持算子为其输出流维护每数据序列状态。例如,用户希望了解从现在开始汽车的总数。他们应根据状态声明将局部变量计数器替换为全局变量:
%状态计数器<int, 0>
D. 分组方法
数据中心中的典型数据处理框架始终支持分组方法。例如,Flink 中的 keyBy 转换 [6] 会将流逻辑上划分为互不相交的分区。具有相同键的记录被分配到同一分区。
相同的分区。为了支持各种物联网应用,模型中也需要进行类似的操作。在某些情况下,用户必须按需在AStream中重新收集数据流。考虑一个物联网‐边缘‐物联网场景的示例。一个智能交通系统为每辆车辆提供其附近交通状况的信息。用户已经从图3所示的现有任务中获得了车牌流。代码6首先累积每个区域内的汽车数量。然后将结果传递给附近的车辆。LeftUnion操作符根据第一个参数联合第二个参数的数据序列。该过程涉及三个关键特征,即区域、键序列和规则。
require LeftUnion, Sum, Deliver
import计数结果, 区域集合, 车辆 每区域交通量 = LeftUnion区域集合 计数结果\| Sum
智能交通 = LeftUnion车辆 每区域交通量\|Deliver
列表 6:智能交通作业的代码
从字面上看,区域表示数据序列的来源。默认情况下,由物联网设备生成的数据序列的特征被设置为其位置。对于生成的操作符,输出数据序列的区域特征被赋值为所有输入区域的联合。来自云的原始流和虚拟流没有区域特征。
键特征是分配给数据序列的用户定义的字符串,通常用于说明该数据序列的内容。
每个数据序列的规则特征决定了其监听范围。这些范围由流所有者明确设置。只有符合规则的数据序列才会被转发给它。在边缘流模型中,定义规则有两个步骤。首先,所有规则都需要一个范围参数。正范围表示以该区域几何中心为中心的圆形区域,其值为以米为单位的半径。值 ‘0’表示仅限数据序列本身,这通常用于物联网‐边缘场景。负数表示接受所有可用数据序列。其次,设置一系列键以进一步选择数据序列。
图 7 展示了交通系统的示意图。该系统中有四条道路,即 Rd1˜Rd4。每条道路上都有两个摄像头在运行车辆计数作业。C1 和 C2 是在 Rd4 道路上行驶的两辆汽车。它们都希望弄清楚附近交通状况。如代码6所示,第一步是在每条道路上聚合计数结果。区域集合指示了从摄像头到道路的映射。然后,汽车根据其位置选择交通状况数据。每个汽车根据其位置定义一条规则。结果,C1和C2分别接收来自{Rd1, Rd2, Rd4}和{Rd2, Rd3, Rd4}的数据。
E. 流共享
与文件系统类似,边缘流模型也采用了权限控制的思想。在典型的文件系统中,文件权限决定了用户访问文件的能力。这种机制使多个用户能够在单一系统中正确地协作。
在边缘流模型中,每个流都有一个唯一所有者。它指的是创建并拥有该流的用户。所有者具有完全权限,可从中生成新流、修改其元数据以及删除该流。例如,代码2 中的原始流在IP地址192.168.10.*处有数据源。如果所有者在192.168.1.100部署新设备,则会在SourcePool列表中添加相应的源项。
所有者还可以与其他用户共享流。例如,在图8中有两个用户,A和B。B尝试构建图3中提到的车牌识别任务。然而,B没有足够的摄像设备,因此请求A共享一个流以丰富数据源。
当用户A开始与用户B共享该流时,B将获得该流的一个引用。随后,B可以从该流创建新流,但不能在边缘系统中修改或删除该流。由A管理的流的元数据也应记录此事件,以便后续关闭共享。通过流共享,现有流不仅可被创建者复用以构建自己的任务,还能在不同用户之间共享。这一过程类似于创建文件的软链接:文件内容在磁盘上仅存储一次,但在系统中具有多个入口。
IV. ESTREAM平台
基于边缘流模型,我们实现了一个名为EStream的全栈原型。在本节中,我们首先讨论系统需要考虑的挑战。然后我们将介绍EStream架构的概览,以及在系统中实现流的一些底层设计概念。下一小节将展示一个案例研究。最后一小节将探讨去中心化调度算法的设计。
A. 挑战
流处理是数据中心中典型的一种计算任务形式。多年来已有许多著名的框架被广泛应用,例如Flink [6]和Storm [35]。AStream [22]提供了一种将即席查询与在流上的长时运行查询集成的实用方法,从而实现计算的共享。然而,由于以下原因,这些模型无法应用于边缘计算场景。
首先,数据中心中的流处理框架建立在通过高速有线网络基础设施相互连接的集群之上。数据访问延迟几乎不受计算节点物理位置的影响。相比之下,边缘计算系统中的计算节点不再以完全图的形式连接。对于特定应用而言,整个网络中节点的拓扑结构通常形成一个层级结构,如图 1所示。数据中心在逻辑上位于根部,物联网设备构成叶节点,中间的计算节点则作为内部节点。基于该物理架构,我们需要确定如何映射后续的计算范式。换句话说,需要明确流是如何创建并经过这些节点的。这是将边缘流模型付诸实现的最重要挑战。
其次,由于边缘计算系统中的计算网络规模通常更大,数据中心中这些框架的主从架构变得效率低下。任务调度变得更加困难,因为存在大量的计算节点。此外,计算节点之间的通信开销显著。因此,很难根据远程边缘网络的实时变化及时调整调度策略。如果有多个用户共享数据流,也难以选择主节点的维护者。传统的资源管理算法,如 Yarn [38],、Mesos [17]和Omega[33],在此场景下无法正常工作。因此,EStream平台需要一种实用的 scheduling method。
第三,我们认为流共享对于边缘计算应用变得越来越重要。在此方案中,多个任务能够共享其计算图的公共部分,从而减少系统中的重复计算和数据传输开销。传统的流处理框架无法动态共享现有任务的一部分,因为它们的调度单元是任务而不是流。为了实现该目标,必须停止现有任务,修改计算图,并重新启动新的任务。这种方法在数据中心中可以接受,但在具有大量远程节点的边缘计算系统中,其代价显著增加。因此,在EStream系统中提供一种实用的方式来支持流共享功能也具有重要意义。
在接下来的小节中,我们将讨论如何解决这些问题。
B. 系统概述
在本小节中,我们概述了EStream架构,并描述了其中流的创建方式。
EStream系统中基本上有三种设备:产生数据序列的端点节点、提供计算和存储资源的计算节点,以及维护流元数据的监控节点。图9展示了它们的一个示例。部分 (a)描述了两个所有者A和B的作业组件。所有者A在流 S1 上施加算子O1,结果流 S2 与S3一起作为O2的输入共享给B。B的作业输出为S4。部分(b)展示了这些节点和流的整体系统拓扑结构。
端点节点包括物联网设备和云中的数据中心。它们为系统提供原始流的源。相比之下,虚拟流来自设备无关算法,而生成流是算子的输出,这些算子以现有流作为输入。它们总是从计算节点开始。图9 (b) 左侧的四个六边形是端点节点。绿色的属于所有者 B,对应流 S3。橙色的属于所有者 A,生成 S1 的三个数据序列。右侧的红色六边形 B‐云 表示所有者 B 的另一个端点节点,它是流 S4 的数据汇。中间的灰色方块代表系统中的四个计算节点。穿过它们的线条表示每条流中的数据序列。如图所示,原始流 S1 起始于端点节点 A#1˜#3,而生成流 S2(蓝线)起始于节点1。
除了端点和计算节点外,EStream 还引入了一些特殊的监控节点来维护必要的流信息。它们提供与流交互的服务,例如创建、删除和共享。在图 9 (b) 中,有两个监控节点,分别属于所有者 A 和 B。监控节点‐A 维护 S1 和 S2 的信息,而监控节点 B 维护 S3 和 S4,以及共享流 S2。
C. 流创建
流创建后,会被注册到其所有者提供的监控节点中。对于原始流,监控器会维护基于设备的流的终端设备列表,或基于范围的流的区域列表。此阶段不会触发数据传输或计算。类似地,由于虚拟流是按需构建的,监控节点仅记录生成这些虚拟流的算法。
相比之下,创建生成流更为复杂,如图10所示。在该示例中,流S有一个名为T的父流。最初,监控节点B维护流T的元数据。1 注册过程完成后,节点A必须向节点B请求流T。2 然后节点B对该事件执行身份验证。如果验证成功,它将协助确定T的当前位置。同时,节点A在本地注册一个共享流T。作为回应,节点B也会记录节点A的信息,以便将来终止共享。3 最后,EStream触发流S的数据传输和计算。通过这种方式,不同任务中的相同原始或生成流在物理系统中仅出现一次。因此,该机制实现了流共享在所有者之间的特性。
根据该过程,在从现有流创建流时,其监控器应在系统中定位它们。共有三种类型的流,即原始流、虚拟流和生成流。(1)要查找原始流,最直观的方法是访问监控节点中维护的端点节点列表。更好的方法是利用图1所示的拓扑结构来最小化通信开销。其基本思想是将流的监控节点视为根节点,而端点节点构成叶子节点。监控节点能够通过对图进行广度优先搜索来访问所有叶子节点。为了支持搜索过程,内部计算节点缓存了用于搜索的邻居列表。例如,在图10中,节点2为流T的广度优先搜索维护一个 {4, 5}作为子集,而节点1将为流S把该列表设置为{2, 4}。(2)虚拟流是按需创建的,因此其监控节点无需在系统中查找已存在的流。(3)生成流是基于来自现有流的数据序列构建的。为了找到生成流,其监控节点会递归地在系统中查找其所有父流。一旦找到所有原始数据源,就可以沿着数据传输方向轻松找到目标流。
例如,图10中的流S是一个具有一个名为T的父流的生成流。为了在系统中定位流S,监控节点A必须向节点 B查询为流T提供数据的节点(Node2, 3, 4, 5)。然后搜索过程从这些节点开始。沿着数据流的方向,很容易找到生成流S数据的子节点1, 2, 4。
D. 请求传播
前一小节回答了所有者创建新流时会发生什么。然而,对于系统中的任意一对节点,数据传输存在大量可能的路由路径。我们应该明确这些流中的数据序列如何选择其路径以通过系统中的计算节点。该问题分为两部分:每个数据序列的传输方向,以及其生成的位置。
首先,EStream为流中的每个数据序列定义了一个目标节点。该数据序列应找到通往其目标的最短路径。换句话说,目标就是方向,用于传递数据序列。某些流被创建为汇聚到某个位置,例如代码3中用于StoreResult的数据中心、代码6中用于SmartTraffic的终端车辆,以及图9 中S4对应的B‐云。这些节点正是流内数据序列的目标节点。其他流没有直观的目的地,例如代码6中的临时流 TrafficPerArea以及图9中的S2。对于此类流中的数据序列,其目标节点被设置为监控节点。正如我们之前所介绍的,每个流在云中只有一个监控节点,因此目标仍然是唯一的。如果一个流被共享给另一个所有者,其目标节点不会发生改变。例如,尽管图10中的流T是从监控节点B共享到节点A的,但其目标节点仍然是监控节点A。
除了方向之外,确定每个数据序列 first appear 在系统中的出现位置也非常重要。显然,原始流中的数据序列从特定的端点节点发起。在虚拟流中,它们仅在按需计算节点中出现。对于生成流而言,该问题等同于确定 where the computation starts,因为这些流是由算子生成的。
对于计算操作符,输入的数据序列会尽快被处理。映射式函数与归约式函数的区别在于,后者需要等待窗口中的数据积累完毕。对于重构操作符,存在两种可能的情况。如果该操作符不涉及任何分组方法,则它仅影响用户对这些数据序列的处理方式,底层系统将忽略它。例如, Union算子将两个或多个流合并为一个,仅在维护的元数据中留下记录
算法1:AreaFinding的伪代码
Area, Node, Stream
输出:用于收集数据序列的节点
如果 Area ⊂ Node[Stream].area 那么
返回 Node[Stream].parent
end foreach节点 ∈ Node[Stream].子节点do
Result=区域查找(Area, node, Stream)
如果 Result ≠ Node那么
return Result
end end
返回 Node
由流所有者完成,而不改变现有的数据序列。相比之下,分组算子需要将多个输入数据序列收集到一个物理节点中。在这种情况下,EStream使用它们目标节点的最近公共祖先节点来收集这些数据序列。因此,该过程的关键是根据规则特征选择适当的数据序列。如第III‐D小节所述,每条规则包含一个范围参数和一系列键序列。从EStream系统的角度来看,范围参数界定了一系列区域,每个区域中的输入数据序列被集中在一起。
为了快速找到匹配区域,EStream系统也为内部计算节点引入了区域特征。该区域及相关数据结构均为每流信息。该机制复用了第IV‐C小节中定义的每流树结构中的父子关系。对于基于设备的原始流,端点节点的父节点收集它们的位置,并计算一个最小凸多边形以覆盖所有这些位置作为其自身区域。递归地,计算节点的父节点将其区域记录为覆盖其所有子节点区域的最小凸k边形。为了控制辅助数据的空间开销,我们将多边形限制为最多 m条边。因此,每个计算节点中区域信息的空间开销最多为 O(m*N),其中N表示流的数量。该思想类似于区间树,区间树是一种流行的数据结构,用于查找与任意给定区间或点重叠的所有区间。
为了在系统中找到匹配区域,目标节点将首先询问所有与其相连的节点。如果该节点的区域能够覆盖,则在其子节点中传播请求;否则,它将向其父节点寻求帮助。该过程递归进行,直到系统找到具有最小面积的合适节点为止。注意,此处的区域指的是能够覆盖其所有子节点区域的最小凸多边形。因此,该节点具有的特性是:遵循该规则的数据序列的所有数据源均来自其子节点。EStream 正是利用此节点来启动收集任务。整个过程如算法1所示。
E. 案例研究
在本小节中,我们提供一个简化的案例研究,跟随图 9中的流所有者A和B,观察他们逐步发布任务时发生的情况。我们从图9中选取了几个物理节点,并在图11中进行了详细说明。图11展示了数据序列和算子在这些节点中的组织方式。圆形和菱形分别代表流和算子。灰色的方框代表多个端点和计算节点。
最初,A和B均没有流。每个计算节点将其区域设置为空集,表示尚未与任何设备连接。当所有者A和B通过终端物联网设备A#1˜A#3和B#1发布原始流时,这些传感器分别在原始流对象S1和S3中注册。每个物联网设备会向其父计算节点报告其所在区域。然后,计算节点将计算覆盖所有这些区域的最小多边形,并将其作为当前区域。递归地,计算节点的父节点也将采用相同方法更新其区域。即,对于节点1,其区域应覆盖A#2和A#3,而节点2应覆盖A#1、B#1以及节点1。至此,终端设备生成的数据包尚未传输,因为尚无需要这些数据的生成流。
然后A创建了一个带有算子O1的新流S2。由于S1是其父流,监控器必须首先找到S1的位置。在图9 (b)中,搜索过程从节点4开始,结束于节点A#1˜3。最近的两个计算节点,节点1和节点2,都将注册S1和S2流,并使用O1将它们连接起来。此时计算尚未发生。之后,A将S2共享给B。根据之前的讨论,这些计算节点中不会发生任何事情,但B现在能够访问流S2。最后,B构建了带有算子O2的流 S4,该算子以S2和S3作为输入。假设O2是一个具有副作用的操作符,例如将结果转储到数据库中。因此,各个节点中的数据传输和计算被触发。
F. 去中心化调度
我们已经知道哪些节点要执行算子,但如何在它们之间划分工作负载仍然不清楚。这需要一些实用的scheduling algorithms。我们在第四节‐A中分析的那样,在大规模边缘计算场景中,使用全局主节点进行调度是低效的。基于云管理平台(如KubeEdge[41],)的边缘计算系统在计算网络规模增长时将遇到可扩展性问题。因此,EStream选择采用去中心化方法来分配系统中的计算工作负载。
在流处理场景中,数据意外地在某些节点上积压是相当常见的。原因有很多:突发的数据激增、不稳定的网络状况、计算节点中的垃圾回收等。一种可能的方法是通知前一个节点降低数据传输速率,从而将压力反向传递给数据源。这种技术被称为反压(back‐pressure),已被 Flink [6], Storm[35]以及数据中心中的许多其他著名框架所采用。反压最终会传递到原始数据源。对于数据中心应用而言,这些数据源通常是可靠的存储系统,例如 Kafka [23]。然而,在边缘计算应用中,终端数据源通常是不具备大量数据存储能力的简单传感器。因此, EStream中的调度算法应选择另一个具有充足计算和存储资源的方向来转移压力。
图11 描述了多个节点之间的任务部署过程。S1 中的数据序列来自传感器 A#1˜A#3,并由算子 O1 处理。一个关键观察是,节点1和节点2都有从 S1 指向外的箭头,这表示如果 O1 无法在当前节点完成全部计算,则剩余的输入数据将被转发到下一个节点。next此处指的是通往 S2 中数据序列目标节点路径上的下一个节点。调度器通过决定应传递给下一节点的数据包比例,来调整每个节点的工作负载。
基于这一观察,我们在系统中采用了一种较为简单但有效的调度方法。本段提到的所有示例均参考图11所示的任务。简而言之,该调度算法旨在调整各节点间的工作负载,使得同一流中的数据包在每个节点中所花费的时间相同。例如,O1将S1中的数据包转换为S2。S1中数据包的生命周期包括总传输时间、在计算节点中的排队时间以及最终的计算时间。我们将流i在节点j上数据包的平均生命周期记为 L j i 。该算法通过重新分配工作负载,试图平衡L 1 S 1 、L 2 S 1 和 L 3 S 1 的值。
为实现该目标,已有多种尝试:首先,算法选择在同一数据流中传输延迟最大的数据包进行计算。例如,如果节点2同时从传感器 A#1 和 A#2 接收到两个数据包,则后者更有可能被执行。因为这是一个两跳数据包。其次,该算法倾向于将压力推向数据源。如果 L2 S1> L1 S1且L2 S1> L3 S1,算法会倾向于让节点1而不是节点3执行更多计算。这样,数据源就对工作负载的计算产生了“吸引力”。第三,生成的流会减轻其父流的压力,反之亦然。例如,在节点1中,假设S1和S2中数据包的基本生命周期分别为 l1S1和 l1S2,则L1 S1和L1 S2均被计算为 min{l1S1, l1S2}。这促进了计算的融合,从而提高了数据包的时间局部性。注意,数据汇也会对工作负载产生“吸引力”,因为这些数据包的生命周期会持续到它们到达接收器为止。数据源和数据汇都会将计算拉向自身一侧,从而缩短路径长度,就像一场拔河比赛。
简而言之,该方法的基本思想是触发节点之间的竞争,直到达到近似平衡。这是一种无需主节点的去中心化算法,并通过基于生命周期的工作负载压力调节避免了反压问题。需要注意的是,这仅仅是可能的调度方法之一。EStream框架并未限制实现其他类型的调度算法。
V. 评估
为了评估EStream的优势并证明其相较于基线系统的优越性,我们进行了全面的评估。具体而言,我们首先选择了一个边缘计算范式的代表性应用,即智能交通作为我们的测试用例。然后,我们使用流行的OMNet++仿真器 [37]实现了EStream系统和基线系统。为了使估算更加准确,我们还生成了一些性能分析数据,包括在真实硬件平台上任务的各种延迟和能耗。接着,我们将这些性能分析数据注入仿真器中作为配置参数。我们的评估方法的详细信息如下所述。
A. 测试用例
在评估中,我们采用智能交通系统作为测试用例。智能交通系统被认为是智慧城市的重要基石[16][21]。在一个典型的智能交通系统中,通常包含物联网设备、边缘服务器和云服务器。在我们的测试用例中,物联网设备可以是沿道路部署的监控摄像头,用于捕捉车辆的图片,并将其发送到边缘服务器进行进一步分析。此外,车辆本身也被视为物联网设备,它们接收来自附近边缘服务器的消息,例如道路拥堵状况。边缘服务器负责收集、处理和分发数据。它们通过低延迟局域网与附近的物联网设备交互,并通过高延迟核心网络与远程云服务器通信。我们在智能交通系统中引入了三个典型任务:
任务x:车辆检测。 车辆检测是智能交通系统中最基本的任务。边缘服务器用于检测由实时视频帧上传的所有车辆连接的监控摄像头。基于这些检测到的车辆图片,我们可以进一步进行多种高级分析。例如,可以统计车辆数量以预测交通拥堵,或识别检测到的车辆的车牌号码以定位某些通缉车辆。我们将车辆检测称为任务x。
作业a:车牌号码识别。 识别车牌号码是识别车辆最直接的方法。我们设置作业a,基于捕获的车辆图片来识别车牌号码。识别出的车牌号码随后上传至云服务器进行进一步分析,例如与一些被通缉的肇事逃逸车辆进行比对。
作业b:车辆属性识别。 除了车牌号码外,车辆属性 (如颜色、标志、型号,甚至细微的类间差异[45])在辅助识别和检索方面也具有重要意义。请注意,在本例中,作业b被视为一种紧急任务,其请求频率低于作业a。
B. 实验设置
1) 网络拓扑: 仿真网络的拓扑结构一般包含四层,从下到上依次为多个物联网设备、接入点、网络路由器和一个数据中心。默认情况下,我们有一个数据中心连接 10个路由器。每个路由器连接10个接入点。这些接入点构成一个蜂窝网络,服务于随机分布在空间中的物联网设备。从物联网设备的角度来看,其对接入点、路由器和数据中心的接入延迟分别设置为5ms、15ms和110ms,参考了之前的研究工作[29]。
2) 软件设置: 为了更好地估算每个任务的延迟、能耗以及启动时间,我们选择了一些真实程序进行性能分析。为了模拟任务x,我们采用基于darknet框架部署的 yolo‐v3目标检测[31]。类似地,我们选择一些现成的程序作为作业a和作业b。具体来说,我们使用一种流行的 OCR工具Tesseract [15]来识别车牌号码。对于车辆属性识别,我们遵循赵等人方法[45],并选用SSD算法 [25]来识别车辆属性,该算法基于Caffe框架部署。
3) 性能分析方法: 我们在真实硬件平台上运行准备好的程序以完成完整的性能分析。为了构建性能分析平台,我们采用一台配备i7‐6700K CPU、32GB内存和 GTX‐1080ti GPU的台式电脑作为边缘服务器。为了模拟云环境,我们使用一台强大的工作站,配备Xeon 6148 CPU、256GB内存和4块GTX‐2080Ti GPU。我们通过将每个任务的处理时间乘以机器的功耗来估算其能耗。需要注意的是,在同一边缘服务器上共置多个任务可能会影响性能,因为这些任务会竞争资源。我们已考虑这一点,并测试了不同可能组合下并发任务的延迟。
| EStream | Flink 0 | Flink 1 | Flink 2 | |
|---|---|---|---|---|
| 延迟 x+a (t0) | 295 毫秒 |
295 毫秒
341 毫秒 |
295 毫秒
336 毫秒 | 295 毫秒 |
| 延迟 x+a (t1) | 305 毫秒 | 312 毫秒 | 307 毫秒 | 449 毫秒 |
| 延迟 x+b (t1) | 276 毫秒 | 276 毫秒 | ||
| 能量 (t0) | 47 J | 47 J | 47 J | 47 J |
| 能量 (t1) | 64 J | 85 J | 67 J | 77 J |
表二:延迟和能量比较。
C. 流共享的好处
如第三节‐E部分所介绍的流共享机制,使我们能够以可忽略的开销重用流。为了量化其优势,我们通过模拟一些典型行为,将EStream系统与类似Flink的系统进行比较:在时间点t0,只有任务x和作业a在边缘系统中运行,任务x+a识别所有检测到的车辆的车牌号码。随后在时间点 t1,作业b 启动以识别检测到的车辆的车辆属性,也就是说,我们启动一个新的任务x+b。需要注意的是,当使用Flink实现智能交通系统时,处理弹出的作业b有多种方式:
Flink 0: 计算两次:从头创建一个新的任务x+b。它不会中断现有的任务x+a,但显然x部分在两个任务中被计算了两次。
Flink 1: 预先规划:最初有两个任务:任务x和作业 a,它们通过消息队列服务连接。当任务b到来时,它可以订阅任务x的结果,而不是将任务x重复计算两次。
Flink 2: 停止并重启:最初有一个任务x+a。当新的作业b到达时,系统应首先停止任务x+a,然后重新启动一个新的任务x+a& b。
对于EStream,我们可以利用第三节‐E部分中介绍的流共享机制,在多个任务以相同流作为输入时,降低延迟和能耗。
如表二所示,我们评估了处理这些任务的任务延迟和能耗。在 t0时,所有型号的延迟和能耗都相同,因为系统中仅存在任务x+a。在时间步 t1,作业b到达。可以看出, Flink 2的延迟x+a最高,因为它需要停止任务x+a并重新启动一个新的任务x+a&b。对于Flink 0,由于它对任务 x进行了两次计算,给边缘服务器带来了不必要的计算负担,这不仅影响了延迟x+a和x+b,还增加了能耗。Flink 1的性能与EStream最为接近。但由于其依赖消息队列服务来共享任务x的数据,因此产生了额外开销。
D. 去中心化调度
在第四节‐F部分,我们介绍了去中心化调度机制,该机制旨在解决分布式边缘系统中的可扩展性问题。我们将该算法与一种集中式方法进行了比较,后者通过定期跟踪各个节点上的计算状态,试图提供最优调度。这种方法受限于工作负载的规模,这是一个直观的结果,因此未在评估中呈现。
与第五节‐B小节中介绍的基本网络拓扑结构相比,我们将数据中心的数量增加到四个。计算网络由50个路由器组成,它们之间的连接可以嵌入平面,形成一个平面图。其他设置保持不变。平均而言,来自物联网的数据包需要经过一个接入点和2.9个路由器才能到达随机的数据中心。对于集中式方法,我们选择其中一个数据中心来调度任务。两种调度方法均基于EStream框架进行评估。
最初,每个数据中心运行一个x+a任务,需要来自所有物联网设备的数据序列。大约15秒时,其中两个数据中心将其任务更改为x+a&b。大约30秒时,我们复制了物联网设备的数量。大约45秒时,任务恢复为x+a,物联网设备数量也恢复到初始设置值。图12显示了初始任务x+ a的平均延迟。根据评估,去中心化调度的表现几乎与集中式方法相同。当任务发生变化时,我们的方法响应更加及时。当工作负载突然增加时,我们的方法能够减轻突发的影响。
E. 物联网-边缘-物联网场景中的可扩展性
EStream在物联网‐边缘‐云、云‐边缘‐物联网、物联网‐边缘‐物联网和物联网‐边缘场景中表现良好。在物联网‐边缘‐云和物联网‐边缘场景中,EStream在数据源附近执行计算,从而获得更多的计算资源并提高系统的可扩展性。在云‐边缘‐物联网场景中,其作用类似于内容分发网络。相同的内容在具有N个邻居的节点中最多被传输N次。这些优势是直观的,并且也适用于其他边缘计算系统,因此本小节仅提及物联网‐边缘‐物联网场景。
在物联网‐边缘‐物联网应用中,数据源和接收器均位于网络边缘。我们使用任务x并采用按接收端分组的方式进行对比,该场景即第III‐D小节中的智能交通案例,在 EStream中易于实现,但在其他平台中较难实现。对于大多数其他边缘计算框架而言,数据从数据源被收集到云或附近的数据中心,随后将聚合结果发送至接收器。我们将这种方法称为全云方法。我们在任务延迟和数据中心接收的数据流量方面将其与EStream进行比较。前一指标对应系统的性能,后一指标反映系统的可扩展性,原因在于数据中心的带宽和计算能力存在限制。
根据图13,EStream在延迟方面优于全云方法。全云方法在物联网设备与云之间具有固定的往返延迟,并且随着物联网设备数量的增加,云中的排队延迟呈线性增长。相比之下,EStream采用点对点的数据传输方式,大大降低了整体延迟。图14显示,EStream几乎仅使用远程云的计算资源。几乎所有传输都是从一个设备到另一个设备,遵循最近路由路径进行。大多数数据包从未到达云,因此数据中心周边的数据流量几乎减少至零。
VI. 结论
由于参与者层级多样以及场景复杂,边缘计算中的应用开发比传统云计算更加复杂。此外,这些应用中的新特性进一步为编程带来了更多挑战。我们尝试采用一种名为边缘流的简单编程模型来解决这些问题。该模型将数据流抽象为流,以涵盖不同类型的场景,并向程序员隐藏底层细节。此外,还引入了数据共享和分组方法,以支持实际部署中的新特性。我们进一步讨论了基于边缘流模型的原型设计中的实现细节,涵盖了任务部署、请求传播和调度等关键问题,以推动该模型的实际落地。最后,实验结果表明该框架能够高效运行。
致谢
本工作得到中国国家重点研发计划(编号 2018YFB1003304)和北京人工智能研究院(BAAI)的支持。
1208

被折叠的 条评论
为什么被折叠?



