App 的开发是在安装了Docker 系统的开发计算机上完成的。当你完成了App 的调试以后,有几种方式上传到云端平台,或者是边缘设备中。
1 使用save/load 命令方式
save 命令是将docker 中的image 打成一个包 导出到docker的外面。而load 是对应的导入命令。
2 使用export/import 命令方式
export 是将一个容器的内容导出成为映像(image),对应地可以通过import 的导入,可以看出来,save 保存的是image ,而export 保存的是容器。 load不能对载入的镜像重命名,而docker import可以为镜像指定新名称。
上面这两种方式简单直接,对于edge 设备现场维护软件升级是有用的,维护人员可以带一个U 盘,存放开发好的image 直接上传到edge 的docker 中。
在设备制造时可以使用该方式预安装基础微服务程序。
3 使用共有或者私有仓库
对于云端而言,如果每个App 都需要用户上传,是很麻烦的。如果有多个用户使用同样的App 的时候就更是如此。也不便于App 开发的升级迭代和版本更新。所以,docker 也采用了映像仓库的方法来存储映像。它就是docker 仓库,最大的docker 仓库是https://hub.docker.com/ 仓库有时也被称为是注册服务器。
我们可以通过pull 命令将docker 仓库中的image 下拉到自己的docker 中。如果你已经在hub.docker.com 注册了,你也可以将自己的image 推送(push) 到hub.docker.com 上。
由于网络原因,我们在pull Image 的时候,从Docker Hub上下载会很慢。所以,我们在自己的云平台上建立了一个私有的仓库。具体的方法是:
1 .运行docker 命令启动私有仓库的容器
docker run -d -p 5000:5000 -v /root/my_registry:/tmp/registry registry
这条命令会在docker中安装registry容器并启动它运行 ,registry容器创建本地的私有仓库服务。默认情况下,会将仓库创建在容器的/tmp/registry目录下,镜像文件存放在本地文件目录的指定路径上(例如,放在本地目录/root/my_registry下)。
2 向私有仓库push镜像
当你push和pull App 映像的时候,是如何来区分是推送到公共仓库还是私有仓库的呢? 其实是通过image 的名称来实现的。
例如:
docker push 127.0.0.1:5000/modular2:latest
docker push 192.168.32.108:5000/gpio:latest
注意:如果要成功地push ,image create 的时候,名称就应该是 127.0.0.1:5000/modular2:latest 。不然的话,push时会出现:
yao@yao-dc:~$ docker push 127.0.0.1:5000/modular2:latest
The push refers to repository [127.0.0.1:5000/modular2]
An image does not exist locally with the tag: 127.0.0.1:5000/modular2
3 查询私有仓库
可以使用http 命令来读取私有云上的image.在浏览器的地址栏输入:
http://192.168.31.108:5000/v2/_catalog
4 下载映像
docker pull 192.168.31.108:5000/modular2:latest
从上可见,docker 的映像仓库机制对于工业应用而言是非常便捷的方式。可以在工厂内部设立一个私有仓库,分布在工厂任何地方的边缘设备或者云端应用平台可以下拉更新软件。
在ModularIOT 中,App采用了远程过程调用RPC 通信机制,我们称为mRPC。
远程过程调用(Remote Procedure Call,缩写为 RPC)是一种通信协议,一个计算机上的程序远程调用另一台计算机上的过程。在modular IOT 中,每个微服务都提供一些RPC,同时将返回RPC 的结果。
实现RPC 的方式有许多种,既可以基于内存共享的方式实现,也可以通过某种通信协议来实现。原始的RPC 协议是在TCP 协议上实现。ModularIOT 使用websocket 和 RabbitMQ 消息系统来实现RPC 。 设备端使用websocket 与边缘设备或者云端通信。App 之间使用rabbitMQ 消息系统。由coreservice 将websocket 协议的json格式消息转换成为proto 格式,并且在rabbitmq 消息系统中发布。
支持 websocket,rabbitmq,MQTT 三种消息方式
终端设备使用websocket 协议接入,App 通过 RabbitMQ 消息系统接入。
高效率的消息体序列化
rabbitMQ消息体使用protocol Buffer 序列化/反序列化,websocket 消息体既可以使用json格式,也可以使用protoBuffer
支持流RPC(stream RPC)
mRPC 支持流RPC。实际上有两种流RPC 模式
-调用数据流
连续的RPC 调用,形成数据流。
-结果数据流
-一次RPC 调用,多个结果形成数据流
数据流RPC 支持广播方式。
多个App 可以读取其它App 发起的数据流。
统一的消息体格式
为了能够在不同的消息系统能够方便地互联互通。我们设计了一个统一的消息体。
Method
From <消息源App 名称 >
To <消息目的地 App 名称>
Code <编码>
Parameters < 参数>
RabbitMQ 是一个由 Erlang 语言开发的 AMQP 的开源消息系统。AMQP是Advanced Message Queue,高级消息队列协议的简写。
RabbitMQ 基本上是一个发布/订阅方式的消息系统。只不过在rabbitmq 中,订阅称为消费(Consumer)。
rabbitMQ 的docker 化部署
rabbitMQ 是作为一个容器在系统中运行的。
RabbitMQ 上的mRPC 的实现
rabbitMQ 消息系统通过消息队列queue发布消息,所以消费该队列的App都能接收到该队列的消息。这和MQTT的机制是类似的。但是消息系统并没有对queue的命名和在消息体提出语义规定。为了在RabbitMQ 上实现modularIOT 的mRPC 协议,我们对队列的设置和消息体做了具体的规定
在modularIOT 的mRPC Over Rabbit具体做法是
1 每个App会等待两个队列
Consumer
Consumer
2 当某个App 需要调用另外App 的RPC 时
publish
对应的App将会收到
pubulish
注意:这个时候的appname 是发送RPC 的那个App的名称。
3. 消息体
显然,为了回送结果,需要知道消息来自于何处,为此我们定义在消息体中,消息体的格式为
Method
From <发送者名称>
To < 接收者名称>
Code <代码>
Parameters 字节数组
为了高效率的传输数据,消息体采用的google 的protocol buffer 序列化方式实现。
例如
val[0]=0
val[1]=0
g:=pb.GenericRPC{
Method:"analog.start",
From:InstanceName,
To:MicroServer1,
Code:0,
Parameter:val,
}
PublishGenericRPC(ch,MicroServer1+".RPC",&g)