• 利用Apache Pulsar的实时边缘计算


           由于网络和基础设施的限制,企业发现越来越难在数据中心或云中移动和处理所有设备产生的数据而不出现延迟或性能问题。因此,边缘应用正在兴起。根据Gartner的估计,到2025年,75%的企业数据将在数据中心或云之外创建和处理。

          什么是边缘计算?边缘应用在数据源上或附近运行,如物联网设备和本地边缘服务器,以及边缘执行。边缘计算使计算、存储、缓存、管理、警报、机器学习和路由发生在数据中心和云之外。各个行业的组织,如零售业、农业、制造业、运输业、医疗保健和电信业,正在采用边缘计算,以实现更低的延迟、更好的带宽可用性、更低的基础设施成本和更快的决策。

          在这篇文章中,将了解到开发边缘计算的一些挑战,以及为什么Apache Pulsar是解决方案。还将通过一个循序渐进的例子了解如何使用Pulsar构建边缘计算。

    1. 关键挑战


    虽然边缘计算的去中心化性质提供了许多好处,但也带来了挑战。一些关键的挑战包括。

    边缘计算通常需要支持各种设备、协议、语言和数据格式。
    边缘计算用的通信需要与来自传感器、日志和应用的事件流异步进行,速度很快但不均衡。
    根据设计,数据的边缘生产者需要多样化的消息集群部署。
    根据设计,边缘计算在地理上是分布的、异质的。

    2. 解决方案


           为了克服构建边缘应用的关键挑战,需要一个可适应的、混合的、地理复制的、可扩展的和开源的解决方案。一个被广泛采用的开源项目提供了一个参与社区的支持,以及边缘应用所需的适配器、连接器和扩展的丰富生态系统。在过去20年中与不同的技术和开源项目合作后,我相信Apache Pulsar解决了边缘应用的需求。

    Apache Pulsar是一个开源的、云原生的、分布式的消息传递和流媒体平台。自从Pulsar在2018年成为Apache软件基金会的顶级项目以来,其社区参与度、生态系统增长和全球采用率都急剧上升。Pulsar有能力解决边缘计算的许多挑战,因为。

    Apache Pulsar支持快速信息传递、元数据和许多数据格式,支持各种模式。
    Pulsar支持Go、C++、Java、Node.js、Websockets和Python等丰富的客户端库。此外,还有社区发布的Haskell、Scala、Rust和.Net的开源客户端,以及Apache Flink和Apache Spark的流处理库。
    Pulsar支持多种消息传递协议,包括MQTT、Kafka、AMQP和JMS。
    Pulsar的地理复制功能解决了分布式设备位置的问题。
    Pulsar是云原生的,可以在任何云、企业内部或Kubernetes环境中运行。它还可以缩小规模,在边缘网关和英伟达Jetson Xavier NX等强大设备上运行。


           在今天的例子中,我们在NVIDIA Jetson Xavier NX上构建了边缘应用程序,该设备为我们提供了足够的动力来运行边缘Apache Pulsar独立经纪商、多个网络摄像头和深度学习边缘应用程序,而且马力十足。边缘设备包含384个NVIDIA CUDA®内核和48个Tensor内核,6个64位ARM内核,以及8GB的128位LPDDR4x内存。在树莓派4s和英伟达Jetson Nanos等更受限制的设备上运行Pulsar,仍足以满足快速边缘事件流的需要。

    image

    NVIDIA Jetson Xavier NX参数配置

    AI性能

    21 TOPS

    GPU

    384-core NVIDIA Volta GPU 和 48 Tensor Cores

    CPU

    6-core NVIDIA Carmel ARM®v8.2 64-bit CPU
    6MB L2 + 4MB L3

    内存

    8 GB 128-bit LPDDR4x
    59.7GB/s

    存储

    16 GB eMMC 5.1

    功耗

    10 W|15 W|20 W

    PCIe

    1 x1 (PCIe Gen3) + 1 x4 (PCIe Gen4), total 144 GT/s*
    最多6个摄像头(通过虚拟通道可以最多支持24个)

    CSI摄像头

    最多6个摄像头(通过虚拟通道可以最多支持24个)
    12 lanes (3x4 or 6x2) MIPI CSI-2
    D-PHY 1.2 (up to 30 Gbps)

    视频编码

    2x 4K60 | 4x 4K30 | 10x 1080p60 | 22x 1080p30 (H.265)
    2x 4K60 | 4x 4K30 | 10x 1080p60 | 20x 1080p30 (H.264)

    视频解码

    2x 8K30 | 6x 4K60 | 12x 4K30 | 22x 1080p60 | 44x 1080p30 (H.265)
    2x 4K60 | 6x 4K30 | 10x 1080p60 | 22x 1080p30 (H.264)

    显示

    2 multi-mode DP 1.4/eDP 1.4/HDMI 2.0

    深度学习加速器

    2个 NVDLA 引擎

    视觉加速器

    7路 VLIW 视觉处理器

    网络

    10/100/1000 BASE-T Ethernet

    结构尺寸

    69.6 mm x 45 mm
    260-pin SO-DIMM connector


    3. 架构


    现在我们已经涵盖了解决方案的物理架构,让我们把重点放在如何在逻辑上构建传入数据。对于那些不熟悉Pulsar的人,每个主题都属于一个租户和一个命名空间,如下图所示。

    15470565-1639834523684

           这些逻辑结构允许我们根据各种标准将数据分组,如数据的原始来源和不同的业务。一旦我们决定了我们的租户、命名空间和主题,我们需要确定我们将需要哪些字段来收集分析所需的额外数据。

    接下来,我们需要确定我们数据的格式。它可以与原始格式相同,或者我们可以对其进行转换以满足传输、处理或存储的要求。我们需要问自己一些架构上的问题。再加上在很多情况下,我们的设备、设备、传感器、操作系统或运输迫使我们选择一个特定的数据格式。

          对于今天的应用,我们将使用JSON,它几乎对任何语言都是无处不在的,而且是人类可读的。Apache Avro,一种二进制格式,也是一个不错的选择,但对于这些博客,我们将保持简单。

    现在,数据格式已经选定,我们可能需要用超出传感器、机器学习分类、日志或其他来源产生的额外字段来丰富原始数据。添加IP地址、mac地址、主机名、创建时间戳、执行时间,以及一些关于设备健康的字段,如磁盘空间、内存和CPU。如果你认为没有必要,或者你的设备已经广播了设备健康状况,你可以添加更多或删除一些。至少,这些字段可以帮助调试,特别是当你有成千上万的设备时。因此,我总是喜欢包括它们,除非严格的带宽限制使之无法实现。

         我们需要为我们的事件记录找到一个主键或唯一标识符。通常情况下,物联网数据没有一个自然的标识。我们可以在创建记录时用UUID生成器来合成一个。

    现在我们有了一个字段列表,我们需要为我们的数据制定一个模式,并确定字段名、类型、默认值和可忽略性。一旦我们定义了一个模式,我们可以用JSON模式或者用字段建立一个类,然后我们就可以用Pulsar SQL来查询我们的主题数据。我们还可以利用该模式来运行Apache Flink SQL的连续SQL

         对于物联网应用,通常希望为这些事件使用一个具有时间序列能力的主数据存储。我推荐Aerospike、InfluxDB或ScyllaDB。我们可以根据用例和需求,通过Pulsar IO sinks或其他机制来处理。如果需要,我们可以使用Spark连接器、Flink连接器或NiFi连接器。

    我们的最终事件将看起来像下面例子中的JSON:

    {
         "uuid": "xav_uuid_video0_lmj_20211027011044",
         "camera": "/dev/video0",
         "ipaddress": "192.168.1.70",
         "networktime": 4.284832000732422,
         "top1pct": 47.265625,
         "top1": "spotlight, spot",
         "cputemp": "29.0",
         "gputemp": "28.5",
         "gputempf": "83",
         "cputempf": "84",
         "runtime": "4",
         "host": "nvidia-desktop",
         "filename": "/home/nvidia/nvme/images/out_video0_tje_20211027011044.jpg",
         "imageinput": "/home/nvidia/nvme/images/img_video0_eqi_20211027011044.jpg",
         "host_name": "nvidia-desktop",
         "macaddress": "70:66:55:15:b4:a5",
         "te": "4.1648781299591064",
         "systemtime": "10/26/2021 21:10:48",
         "cpu": 11.7,
         "diskusage": "32367.5 MB",
         "memory": 82.1
    }

    4. 边缘-生产者


          让我们在NVIDIA Jetson Xavier NX上测试一些库、语言和客户端,看看哪一个最适合我们的用例。在对一些在Ubuntu上运行的带有NVIDIA Jetson Xavier NX的ARM版本的库进行原型设计后,它们产生的信息符合我的应用程序的需要。这些并不是唯一的,但对于这个边缘平台来说是非常好的选择。

    Go Lang Pulsar Producer
    Python 3.x Websocket 生产者
    Python 3.x MQTT 生产者
    Java 8 Pulsar Producer

         Go语言生产者

    package mainimport (
            "context"
            "fmt"
            "log"
            "github.com/apache/pulsar-client-go/pulsar"
            "github.com/streamnative/pulsar-examples/cloud/go/ccloud"
           "github.com/hpcloud/tail"
    )func main() {
        client := ccloud.CreateClient()    producer, err := client.CreateProducer(pulsar.ProducerOptions{
            Topic: "jetson-iot",
        })
        if err != nil {
            log.Fatal(err)
        }
        defer producer.Close()    t, err := tail.TailFile("demo1.log", tail.Config{Follow:true})
            for line := range t.Lines {
            if msgId, err := producer.Send(context.Background(), 
    &pulsar.ProducerMessage{
                Payload: []byte(line.Text),
            }); err != nil {
                log.Fatal(err)
            } else {
                fmt.Printf("jetson:Published message: %v-%s \n", 
    msgId,line.Text)
            }
        }
    }

    Python3 websocket 生产者

    import requests, uuid, websocket, base64, jsonuuid2 = uuid.uuid4()
    row = {}
    row['host'] = 'nvidia-desktop'
    ws = websocket.create_connection( 'ws://server:8080/ws/v2/producer/persistent/public/default/energy')
    message = str(json.dumps(row) )
    message_bytes = message.encode('ascii')
    base64_bytes = base64.b64encode(message_bytes)
    base64_message = base64_bytes.decode('ascii')
    ws.send(json.dumps({ 'payload' : base64_message, 'properties': { 'device' : 'jetson2gb', 'protocol' : 'websockets' },'key': str(uuid2), 'context' : 5 }))
    response =  json.loads(ws.recv())
    if response['result'] == 'ok':
                print ('Message published successfully')
    else:
                print ('Failed to publish message:', response)
    ws.close()

    Java Pulsar 生产者

    public static void main(String[] args) throws Exception {
         JCommanderPulsar jct = new JCommanderPulsar();
         JCommander jCommander = new JCommander(jct, args);
         if (jct.help) {
             jCommander.usage();
             return;
         }
         PulsarClient client = null;
         if (jct.issuerUrl != null && jct.issuerUrl.trim().length() > 0) {
             try {
                 client = PulsarClient.builder().serviceUrl(jct.serviceUrl.toString()).authentication(
                 AuthenticationFactoryOAuth2.clientCredentials(new URL(jct.issuerUrl.toString()), new URL(jct.credentialsUrl.toString()), jct.audience.toString())).build();
             } catch(PulsarClientException e) {
                 e.printStackTrace();
             } catch(MalformedURLException e) {
                 e.printStackTrace();
             }
         }
         else {
             try {
                 client = PulsarClient.builder().serviceUrl(jct.serviceUrl.toString()).build();
             } catch(PulsarClientException e) {
                 e.printStackTrace();
             }
         }
         UUID uuidKey = UUID.randomUUID();
         String pulsarKey = uuidKey.toString();
         String OS = System.getProperty("os.name").toLowerCase();
         String message = "" + jct.message;
         IoTMessage iotMessage = parseMessage("" + jct.message);
         String topic = DEFAULT_TOPIC;
         if (jct.topic != null && jct.topic.trim().length() > 0) {
             topic = jct.topic.trim();
         }
         ProducerBuilder < IoTMessage > producerBuilder = client.newProducer(JSONSchema.of(IoTMessage.class)).topic(topic).producerName("jetson").
         sendTimeout(5, TimeUnit.SECONDS);
         Producer < IoTMessage > producer = producerBuilder.create();
         MessageId msgID = producer.newMessage().key(iotMessage.getUuid()).value(iotMessage).property("device", OS).property("uuid2", pulsarKey).send();
         producer.close();
         client.close();
         producer = null;
         client = null;
    }
    private static IoTMessage parseMessage(String message) {
         IoTMessage iotMessage = null;
         try {
             if (message != null && message.trim().length() > 0) {
                 ObjectMapper mapper = new ObjectMapper();
                 iotMessage = mapper.readValue(message, IoTMessage.class);
                 mapper = null;
             }
         }
         catch(Throwable t) {
             t.printStackTrace();
         }
         if (iotMessage == null) {
             iotMessage = new IoTMessage();
         }
         return iotMessage;
    }

    java -jar target/IoTProducer-1.0-jar-with-dependencies.jar --serviceUrl pulsar://nvidia-desktop:6650 --topic 'iotjetsonjson' --message "...JSON…"

          现在我们决定如何在设备上执行我们的应用程序。它可以使用系统自带的调度器,如cron或一些插件。我经常使用cron,MiNiFi代理,一个shell脚本,或者作为一个服务连续运行应用程序。你将不得不调查你的设备和传感器,以获得最佳的任务调度。

    5. 验证数据和监控


    现在,我们有一个连续的事件流进入我们的Pulsar集群,我们可以验证数据并监控我们的进展。最简单的方法是使用StreamNative云管理器为我们的统一消息数据提供一个全新的网络界面,如下图所示。我们还可以选择查看Pulsar的指标端点,如这里所记录的。

    15470569-1639834712541

    Check Stats via REST

    http://:8080/admin/v2/persistent/public/default/mqtt-2/stats http://:8080/admin/v2/persistent/public/default/mqtt-2/internalStats

    Check Stats via Admin CLI
    bin/pulsar-admin topics stats-internal persistent://public/default/mqtt-2
    Find Subscriptions to Your Topic

    http://nvidia-desktop:8080/admin/v2/persistent/public/default/mqtt-2/subscriptions

    Consume From Subscription via REST

    http://nvidia-desktop:8080/admin/v2/persistent/public/default/mqtt-2/subscription/mqtt2/position/10

    Consume Messages via CLI
    bin/pulsar-client consume "persistent://public/default/mqtt-2" -s "mqtt2" -n 5
    Query Topics via Pulsar SQL
    select * from pulsar."public/default".iotjetsonjson;

    有了这些,我们已经建立了一个边缘计算应用程序,它可以以事件速度进行数据流,并将成千上万的其他应用程序的数据流加入你的Apache Pulsar集群。

    今天先到这儿,希望对云原生,技术领导力, 企业管理,系统架构设计与评估,团队管理, 项目管理, 产品管管,团队建设 有参考作用 , 您可能感兴趣的文章:
    领导人怎样带领好团队
    构建创业公司突击小团队
    国际化环境下系统架构演化
    微服务架构设计
    视频直播平台的系统架构演化
    微服务与Docker介绍
    Docker与CI持续集成/CD
    互联网电商购物车架构演变案例
    互联网业务场景下消息队列架构
    互联网高效研发团队管理演进之一
    消息系统架构设计演进
    互联网电商搜索架构演化之一
    企业信息化与软件工程的迷思
    企业项目化管理介绍
    软件项目成功之要素
    人际沟通风格介绍一
    精益IT组织与分享式领导
    学习型组织与企业
    企业创新文化与等级观念
    组织目标与个人目标
    初创公司人才招聘与管理
    人才公司环境与企业文化
    企业文化、团队文化与知识共享
    高效能的团队建设
    项目管理沟通计划
    构建高效的研发与自动化运维
    某大型电商云平台实践
    互联网数据库架构设计思路
    IT基础架构规划方案一(网络系统规划)
    餐饮行业解决方案之客户分析流程
    餐饮行业解决方案之采购战略制定与实施流程
    餐饮行业解决方案之业务设计流程
    供应链需求调研CheckList
    企业应用之性能实时度量系统演变

    如有想了解更多软件设计与架构, 系统IT,企业信息化, 团队管理 资讯,请关注我的微信订阅号:

    MegadotnetMicroMsg_thumb1_thumb1_thu[2]

    作者:Petter Liu
    出处:http://www.cnblogs.com/wintersun/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 该文章也同时发布在我的独立博客中-Petter Liu Blog。

  • 相关阅读:
    winform编程设定listview选中行
    更新客户信息
    在Flutter中使用Android、iOS的原生 View
    怎么卸载nodejs?
    JavaScript实现简单的图片瀑布流插件
    通过代码重用攻击绕过现代XSS防御
    炫酷的播放粒子效果,你也可以学会!使用Web动画API制作
    14行实现js原生语法前端模板引擎
    什么是ESLint?
    js中require和import的区别
  • 原文地址:https://www.cnblogs.com/wintersun/p/15756167.html
Copyright © 2020-2023  润新知