基于Predix云远程控制边缘节点的实践

2023-10-12 22:20

本文主要是介绍基于Predix云远程控制边缘节点的实践,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

基于Predix云远程控制边缘节点的实践

在工业互联网的核心场景中,除了使机器拥有感知,不断上传自身的运行数据外,通过云端远程控制边缘节点则是另一个非常重要的场景。因为,只有实现了从云端到设备端反向通信和控制,才能将云端经由大数据运算、优化过的推荐策略反馈到边缘设备端,从而达到优化运营管理、提高设备使用率、降低设备的非计划性停机时间等用户期待的结果。

Predix EdgeManager - 云端远程控制边缘节点的解决方案

Predix EdgeManager是Predix平台中云端远程控制边缘节点的全生命周期解决方案,它可以和Predix Machine完美集成,完成如下功能,

  • 用户权限管理
  • 提供设备注册与分组管理
  • 设备配置、算法的下发
  • 设备状态监控,报警
  • 设备端命令执行
  • 设备端软件依赖管理
  • 设备端软件版本管理

Predix EdgeManager能提供全生命周期的解决方案,但是,目前只针对Predix北美用户开放,中国Predix试用计划的用户还没法申请、试用Predix EdgeManager的服务。

如果只需要从云端远程控制边缘节点的功能,我们完全可以自己实现一个WebSocket服务器推送到Predix平台,从云端下发指令给设备端执行。本文就详细介绍一下,如何基于Predix云通过WebSocket协议远程控制边缘节点的实践

WebSocker服务器软件架构

实现一个WebSocket服务器一点都不困难,因为各种高级语言都有相应的开发框架,我们可以在几分钟内就能完成。真正需要注意的是,当WebSocket服务器已经连接了多个客户端的场景下,如何传递命令到相应的WebSocket客户端,而不是广播给所有的客户端。下图就是我们的一个示例,用Go语言实现的WebSocket服务器。

WebSocket服务器架构如下图所示,


我们可以看到,

  1. WebSocket服务器运行在Predix云平台上。
  2. 客户端通过WebSocket协议与Predix云平台上的WebSocket服务器通信。
  3. 服务器端实现了agentbrokerREST Interface几个核心组件。
  4. 服务器为每个WebSocket连接创建一个agent实例。agent实例一方面负责同WebSocket客户端进行双向通信,另一方面,agent还接收broker下发的命令并转发到客户端。
  5. 服务器创建一个broker实例。该broker实例负责注册每个agent实例,并在接收到具体的命令后,转发给相应的agent实例。也就是说,我们通过broker实例实现了传递命令到相应的WebSocket客户端,而不是广播给所有客户端的功能。
  6. REST Interface实现了/agentsGET方法。因此,用户可以通过浏览器发送相应的请求给broker实例,而broker实例则会转发命令给相应的agent实例。
  7. 同时,REST Interface会为每个请求创建一个callback管道,agent实例将客户端执行命令的结果通过该callback管道返回给REST Interface。因此,用户在浏览器中就可以看到边缘节点执行命令的结果。

Go语言代码实例

介绍完架构设计,让我们看看代码实现。

下载代码示例

git clone https://github.com/pxie/go.in.practices.git
cd go.in.practices/websocket/

实现WebSocket服务器

首先,我们需要实现一个运行在Predix平台的WebSocket服务器,而且实现两个URL的处理函数,

  1. 处理WebSocket客户端通过wss://<主机名>/register注册到服务器的请求
  2. 处理用户通过浏览器访问https://<主机名>/agents获取客户端系统信息的请求

注:严格意义上说,我们实现的服务器同时提供WebSocket接口和RESTful接口,不只是WebSocket服务器。

// server.go
func main() {...http.HandleFunc("/", home)http.HandleFunc("/agents", func(w http.ResponseWriter, r *http.Request) {agentsHandler(broker, w, r)})// create agent, and register it to brokerhttp.HandleFunc("/register", func(w http.ResponseWriter, r *http.Request) {serveWs(broker, w, r)})

实现broker,并初始化一个实例

其次,我们需要实现brokerbroker实例通过register管道接收新创建的agent实例,接着存储到conns键值对中完成agent注册功能。在WebSocket服务器的初始化过程中,通过goroutine来运行一个broker的实例。在本例中,我们使用Go语言的内部类型map存储所有的键值对。如果要实现分布式无状态的横向扩展,我们可以非常方便的将agent实例的信息存储到外部存储服务中,例如Predix云平台的Redis服务。

// broker.go
type broker struct {// register channel to add new websocket connect to brokerregister chan map[string]*agent// channel to get message from http server to brokerhttpCh chan *message// record all websocket connsconns map[string]*agent
}// server.go
func main() {broker := newBroker()go broker.start()

实现agent

再次,我们需要实现agent。每当有新的WebSocket客户端注册到服务器端,就会创建一个agent实例。接着,agent实例会通过register管道把自己注册到broker中。最后,agent会启动一个goroutine接收broker下发的命令,并完成与WebSocket客户端的交互。

// agent.go
type agent struct {// broker to know every agentb *broker// connect to remote client via websocketconn *websocket.Conn// channel get message from brokeragentCh chan *message
}func serveWs(b *broker, w http.ResponseWriter, r *http.Request) {conn, err := upgrader.Upgrade(w, r, nil)ag := &agent{b: b, conn: conn, agentCh: make(chan *message, 256)}agentID := uuid.NewV4().String()// build map to register agentreg := make(map[string]*agent)reg[agentID] = agb.register <- reg// one goroutine to handle communication between agent to remote client, and brokergo ag.start()
}

实现WebSocket客户端

最后,我们需要实现WebSocket客户端,它首先完成向WebSocket服务器的注册,然后就不断监听WebSocket服务器下发的消息,并根据消息的内容,执行相应的处理函数,最终将处理结果通过WebSocket连接返回给WebSocket服务器。

// client/client.go
func main() {addr := "websocket-server.run.aws-jp01-pr.ice.predix.io"u := url.URL{Scheme: "wss", Host: addr, Path: "/register"}log.Printf("connect to %s", u.String())conn, _, err := websocket.DefaultDialer.Dial(u.String(), nil)if err != nil {log.Fatal("connect to websocker server error.", err)}defer conn.Close()ticker := time.NewTicker(10 * time.Second)defer ticker.Stop()for {select {case <-ticker.C:_, msg, err := conn.ReadMessage()if err != nil {log.Fatalln("read message from websocket error.", err)}exec(string(msg), conn)}}
}

推送到Predix云平台

和推送其他应用一样,我们用cf push就可以完成WebSocket服务器的推送。

推送完成的结果如下所示,

requested state: started
instances: 1/1
usage: 170M x 1 instances
urls: websocket-server.run.aws-jp01-pr.ice.predix.io   <-- 推送app后获得的URL
last uploaded: Tue Sep 26 04:20:50 UTC 2017
stack: cflinuxfs2
buildpack: https://github.com/cloudfoundry/go-buildpack.gitstate     since                    cpu    memory         disk           details
#0   running   2017-09-26 12:21:52 PM   0.0%   5.5M of 170M   7.7M of 350M

获取当前所有已注册的agent信息

我们可以通过浏览器访问https://websocket-server.run.aws-jp01-pr.ice.predix.io/agents来获取当前已注册的agent信息。和预期的一样,因为没有任何WebSocket客户端连接到服务器端,所以返回的结果是没有agent已经注册。

注册WebSocket客户端

因为,每个人推送到Predix云平台上WebSocket服务器的URL都是不一样的。因此,我们需要修改客户端的代码,指向正确的地址并重新编译。

// client/client.go
func main() {// 修改为正确的地址addr := "websocket-server.run.aws-jp01-pr.ice.predix.io"}// 保存client.go文件
// 运行go build命令完成编译

当编译完成后,我们就可以运行client/路径下的./client可执行文件,将agent注册到broker

这时,我们再次访问/agents页面就会得到agent相关的信息。

[{"AgentID":"298ee2a5-d43b-426c-b42c-04c14a6aae41","RemoteAddr":"10.120.3.10:8446"}]

查询WebSocket客户端的系统信息

最后,我们可以通过浏览器访问/agents?id=<AgentID>来查询WebSocket客户端的系统信息,包括执行WebSocket客户端的操作系统,CPU信息和主机信息。通过这个请求,我们从浏览器发送命令到指定的agent实例,然后agent将命令转发给WebSocket客户端,客户端执行相应的命令,并把命令执行的结果返回给浏览器展示。

> GET /agents?id=bc93384d-e46b-4508-ab7e-18ec55d9652d HTTP/1.1
> Host: websocket-server.run.aws-jp01-pr.ice.predix.io
> User-Agent: curl/7.43.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Length: 1073
< Content-Type: text/plain; charset=utf-8
< Date: Tue, 26 Sep 2017 04:57:14 GMT
< X-Vcap-Request-Id: 9cba4b18-e4ba-42fb-5cea-afca45385fd0
<
OS: darwin
CPU: [{"cpu":0,"vendorId":"GenuineIntel","family":"6","model":"70","stepping":1,"physicalId":"","coreId":"","cores":4,"modelName":"Intel(R) Core(TM) i7-4870HQ CPU @ 2.50GHz","mhz":2500,"cacheSize":256,"flags":["fpu","vme","de","pse","tsc","msr","pae","mce","cx8","apic","sep","mtrr","pge","mca","cmov","pat","pse36","clfsh","ds","acpi","mmx","fxsr","sse","sse2","ss","htt","tm","pbe","sse3","pclmulqdq","dtes64","mon","dscpl","vmx","smx","est","tm2","ssse3","fma","cx16","tpr","pdcm","sse4.1","sse4.2","x2apic","movbe","popcnt","aes","pcid","xsave","osxsave","seglim64","tsctmr","avx1.0","rdrand","f16c","smep","erms","rdwrfsgs","tsc_thread_offset","bmi1","avx2","bmi2","invpcid","fpu_csds","syscall","xd","1gbpage","em64t","lahf","lzcnt","rdtscp","tsci"],"microcode":""}]
host: {"hostname":"C02S8EWGG8WP","uptime":14451,"bootTime":1506387382,"procs":308,"os":"darwin","platform":"darwin","platformFamily":"","platformVersion":"10.11.6","kernelVersion":"15.6.0","virtualizationSystem":"","virtualizationRole":"","hostid":"b370db23-7244-3441-b95d-384442442565"}

注:/agents?id=<AgentID>获取的是WebSocket客户端的系统信息,而不是Predix云平台上agent实例的系统信息。

通过这个示例,我们完整演示了如何从云端传递命令到相应的WebSocket客户端,并将执行的结果反馈回云端的全过程。

小结

通过本文我们了解到,

  1. Predix EdgeManager提供了云端远程控制边缘节点的全生命周期解决方案,目前只对北美用户开放。
  2. 基于Predix云,通过WebSocket协议实现远程控制边缘节点是非常简单的。
  3. 在用Go语言实现的版本中,我们大量的使用了Go语言内置的管道和goroutine,做到通过消息通信来共享内存,而不是通过共享内存来完成消息通信。
  4. 本示例只完成了agent注册和云端命令下发到客户端的功能,并没有实现WebSocket持久连接和agent注销功能,大家可以自行完成。

作者:谢品,上海创新坊首席架构师,GE数字集团

专注于工业互联网,云计算,大数据,高性能分布式存储领域,对Cloud Foundry和传统应用向云端,特别是向Predix迁移有丰富的经验,曾供职于VMware,EMC,Autodesk等知名软件公司云计算部门。

这篇关于基于Predix云远程控制边缘节点的实践的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/198835

相关文章

Redis实现分布式锁全解析之从原理到实践过程

《Redis实现分布式锁全解析之从原理到实践过程》:本文主要介绍Redis实现分布式锁全解析之从原理到实践过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、背景介绍二、解决方案(一)使用 SETNX 命令(二)设置锁的过期时间(三)解决锁的误删问题(四)Re

在 Spring Boot 中实现异常处理最佳实践

《在SpringBoot中实现异常处理最佳实践》本文介绍如何在SpringBoot中实现异常处理,涵盖核心概念、实现方法、与先前查询的集成、性能分析、常见问题和最佳实践,感兴趣的朋友一起看看吧... 目录一、Spring Boot 异常处理的背景与核心概念1.1 为什么需要异常处理?1.2 Spring B

SpringBoot请求参数接收控制指南分享

《SpringBoot请求参数接收控制指南分享》:本文主要介绍SpringBoot请求参数接收控制指南,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Spring Boot 请求参数接收控制指南1. 概述2. 有注解时参数接收方式对比3. 无注解时接收参数默认位置

Spring Boot 整合 SSE的高级实践(Server-Sent Events)

《SpringBoot整合SSE的高级实践(Server-SentEvents)》SSE(Server-SentEvents)是一种基于HTTP协议的单向通信机制,允许服务器向浏览器持续发送实... 目录1、简述2、Spring Boot 中的SSE实现2.1 添加依赖2.2 实现后端接口2.3 配置超时时

Python使用getopt处理命令行参数示例解析(最佳实践)

《Python使用getopt处理命令行参数示例解析(最佳实践)》getopt模块是Python标准库中一个简单但强大的命令行参数处理工具,它特别适合那些需要快速实现基本命令行参数解析的场景,或者需要... 目录为什么需要处理命令行参数?getopt模块基础实际应用示例与其他参数处理方式的比较常见问http

Spring Security+JWT如何实现前后端分离权限控制

《SpringSecurity+JWT如何实现前后端分离权限控制》本篇将手把手教你用SpringSecurity+JWT搭建一套完整的登录认证与权限控制体系,具有很好的参考价值,希望对大家... 目录Spring Security+JWT实现前后端分离权限控制实战一、为什么要用 JWT?二、JWT 基本结构

Android实现两台手机屏幕共享和远程控制功能

《Android实现两台手机屏幕共享和远程控制功能》在远程协助、在线教学、技术支持等多种场景下,实时获得另一部移动设备的屏幕画面,并对其进行操作,具有极高的应用价值,本项目旨在实现两台Android手... 目录一、项目概述二、相关知识2.1 MediaProjection API2.2 Socket 网络

Java Optional的使用技巧与最佳实践

《JavaOptional的使用技巧与最佳实践》在Java中,Optional是用于优雅处理null的容器类,其核心目标是显式提醒开发者处理空值场景,避免NullPointerExce... 目录一、Optional 的核心用途二、使用技巧与最佳实践三、常见误区与反模式四、替代方案与扩展五、总结在 Java

Spring Boot循环依赖原理、解决方案与最佳实践(全解析)

《SpringBoot循环依赖原理、解决方案与最佳实践(全解析)》循环依赖指两个或多个Bean相互直接或间接引用,形成闭环依赖关系,:本文主要介绍SpringBoot循环依赖原理、解决方案与最... 目录一、循环依赖的本质与危害1.1 什么是循环依赖?1.2 核心危害二、Spring的三级缓存机制2.1 三

Python 中的 with open文件操作的最佳实践

《Python中的withopen文件操作的最佳实践》在Python中,withopen()提供了一个简洁而安全的方式来处理文件操作,它不仅能确保文件在操作完成后自动关闭,还能处理文件操作中的异... 目录什么是 with open()?为什么使用 with open()?使用 with open() 进行