边缘计算:原理、技术与实践
上QQ阅读APP看书,第一时间看更新

3.2 无线接入的通信服务协议

随着物联网设备数量的持续增加,终端设备之间、终端设备与边缘设备之间的通信对物联网和边缘计算系统的效率及可靠性有十分重要的影响。物联网通信协议分为两大类:负责子网内设备间的组网及通信的接入协议,以及负责设备通过互联网进行数据交换及通信的通信协议。不同的物联网通信协议具有不同的性能、数据速率、覆盖范围、功率和内存,而且每一种协议都有各自的优点和缺点。有些通信协议只适合小型家用电器,也有些通信协议则可以用于大型智慧城市项目。

前文介绍的无线通信技术(如Wi-Fi、LPWAN等)是从物理层或数据链路层协议的层面出发,实现不同的无线网络性能以满足各类物联网应用的需求。

可以看出,网络层想要达到统一的接入协议几乎是不可能完成的任务。因此,需要在应用层建立对数据和服务的统一通信机制,来建立通用场景中的物联网设备间、物联网设备与边缘设备之间的高效数据传递。本小节从应用层协议出发,介绍物联网边缘计算系统中几种不同的通信服务协议。

3.2.1 MQTT

MQTT(Message Queue Telemerty Transport)是IBM开发的一种即时通信的二进制协议,主要用于服务器和那些低功耗的物联网设备之间的通信。它位于TCP协议的上层,除了提供“发布-订阅”这一基本功能外,也提供一些其他特性,例如不同的消息投递保障,通过存储最后一个被确认接收的消息来实现重连后的消息恢复。MQTT非常轻量级,从设计和实现层面都适合用于不稳定的网络环境中。

MQTT协议定义了两种实体类型:消息代理和客户端。消息代理作为服务器从客户端接收消息,然后将这些消息路由到相关的目标客户端。客户端连接到消息代理,与消息代理进行交互,并发送和接收消息,该连接可以是简单的TCP/IP连接,也可以是用于发送敏感消息的加密TLS连接。客户端可以是物联网的终端设备,也可以是服务器上处理数据的应用程序。客户端通过将某个主题的消息发送给消息代理,消息代理将消息转发给所有订阅该主题的客户端。因为MQTT消息是按主题进行组织的,所以应用程序开发人员能灵活地指定某些客户端只能与某些消息交互。

例如,物联网设备在“sensor_data”主题范围内发布采集的传感器数据,并订阅“config_change”主题,边缘的数据处理应用程序会订阅“sensor_data”主题,时刻关注物联网设备传来的最新数据并处理,管理控制台应用程序接收系统管理员的命令来调整传感器的配置(比如灵敏度和采样频率),并将这些更改发布到“config_change”主题。如此,物联网设备能够及时接收到配置的改动并作出相应修改。可以看出,这种异步传输的特性非常适用于动态、泛在的物联网边缘计算的场景。

3.2.2 AMQP

AMQP(Advanced Message Queuing Protocol)是一个提供统一消息服务的应用层标准高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。基于此协议的客户端与消息中间件可传递消息,并不受开发语言等条件的限制。AMQP使得遵从该规范的客户端应用和消息中间件服务器的全功能互操作成为可能。AMQP消息队列主要有以下几种应用场景:异步处理、跨系统的异步通信、应用解耦、死信队列、分布式事务、流量缓冲以及日志处理等。

AMQP可以实现一种在全行业广泛使用的标准消息中间件技术,以便降低企业和系统集成的开销,并且向大众提供工业级的集成服务。它令消息中间件的能力最终被网络本身所具有,并且通过消息中间件的广泛使用发展出一系列有用的应用程序。AMQP定义的网络协议和代理服务主要包括一套确定的消息交换功能(高级消息交换协议模型),以及一个网络线级协议(数据传输格式),客户端应用可以通过这些协议与消息代理和AMQP模型进行交互通信。

AMQP主要包含以下几种元素:向交换器发布消息的生产者、从消息队列中消费消息的消费者、用于保存消息并发送给消费者的消息队列、每个消息被投入到的消息载体队列、携带具体传输内容的消息、接收生产者发送的消息并转发给消息队列的交换器、交换器进行消息投递所依据的路由关键字、用作不同用户的权限分离的虚拟主机、AMQP的服务端Broker、网络连接、连接管理器、信道以及把交换器和消息队列按照路由规则绑定起来的绑定器。

AMQP实现通信的步骤如图3-14所示。

①建立连接。由生产者和消费者分别连接到Broker的物理节点上。

②建立消息信道。信道是建立在连接之上的,一个连接可以建立多个信道,生产者连接虚拟主机建立信道,消费者连接到相应的消息队列上建立信道。

③发送消息。由生产者发送消息到Broker中的交换器。

④路由转发。交换器收到消息后,根据一定的路由策略,将消息转发到相应的消息队列中去。

⑤消息接收。消费者会监听相应的消息队列,一旦队列中有可以消费的消息,就将消息发送给消费者端。

⑥消息确认。当消费者完成某一条消息的处理之后,需要发送一条ACK消息给对应的消息队列。消息队列收到ACK信息后,才会认为消息处理成功,并将消息从队列中移除;如果在对应的信道断开后,消息队列没有收到这条消息的ACK信息,该消息将被发送给另外的信道。

至此一个消息的发送接收流程就走完了。消息的确认机制提高了通信的可靠性。

000

图3-14 AMQP协议架构

3.2.3 Kafka

Kafka是一个高吞吐量的发布-订阅消息系统和流处理平台,类似于服务器集群中的日志系统,在企业开发中有广泛的应用。Kafka以主题的形式为消息流提供了持久消息存储,Kafka中的每一个消息都包含一个键、值以及时间戳,采用的是一种傻瓜代理/智能消费的模式,Kafka只记录未读消息,并相应地为所有的消息保留一定的时间窗口,同时,消费者各自负责记录自身读取消息的实际位置。因此,通过适合的客户端代码,Kafka可以以非常小的代价支持大量的消费者和数据。如图3-15所示,Kafka本身需要借助外部服务(如ZooKeeper)来实现各类具体功能。

000

图3-15 Kafka协议架构

Kafka的主要特性如下:

①通过O(1)的磁盘数据结构提供消息的持久化,这种结构即使对于TB量级的消息存储也能够保持长时间的稳定性能。

②高吞吐量,即使是非常普通的硬件Kafka,也可以支持每秒数百万的消息。

③支持通过Kafka服务器和消费机集群来分区消息。

④支持Hadoop并行数据加载。

Kafka本身包含消息代理,这也是其最受欢迎的部分,因此也常用于流式处理。除此之外,Kafka也引入了Kafka Streams,作为Spark、Beam、Google Cloud Data Flow和Spring Cloud Data Flow等流平台备选方案。Kafka的主要应用场景包含网站行为跟踪、日志聚合、流式处理以及事件溯源等。消息路由是其经典应用场景之一,其中,以下消息路由场景最适宜Kafka:

①某个流不需要复杂的路由且至少包含一次按分区的顺序路由。

②当应用需要访问流的历史记录,至少包含一次按分区的顺序路由,与其他传统的消息中间件不同,Kafka提供更持久的消息存储,客户端可以根据需要进行事件回溯。

③流式处理。

④事件溯源。

3.2.4 STOMP

STOMP(Streaming Text Oriented Messaging Protocol)是面向流文本的消息传输协议。作为WebSocket通信标准,STOMP提供一个可互操作的连接格式,允许客户端与任意STOMP消息代理(Broker)进行交互。协议简单且易于实现,几乎所有的编程语言都有STOMP的客户端实现。尽管如此,STOMP在消息大小和处理速度方面并无优势。由于在许多“发布-订阅”架构中,信息交换是基于文本的,所以许多协议选择简单地将整个信息转化为文本,从而降低复杂性并提高了可读性,当然带来的代价就是需要在消息接收后执行额外的计算任务。

通常来讲,由于HTTP是一个单工的协议,服务器不能主动发送消息给客户端,导致HTTP在处理实时性要求高的应用时效率不高。为了提高效率,使用全双工的WebSocket协议可以让服务器主动推送消息,但由于WebSocket协议是底层协议而不是应用层协议,未对有效载荷的格式进行规范,导致我们需要自己定义消息体格式、解析消息体,实现成本高,而通过将WebSocket协议与STOMP协议结合则可以有效解决上述问题。具体地,相比于HTTP协议,全双工的WebSocket协议中服务器与客户端都可以发送消息,且消息体更轻量。相比于WebSocket,STOMP不需要自己规定消息的格式以及对消息的格式做解析,由于STOMP是一个统一的标准,有很多库与厂商都对STOMP协议进行了支持,成本低,扩展性高。

STOMP客户端可以同时运行两种模式:作为生产者,通过SEND框架将消息发送给服务器的某个服务;作为消费者,通过订阅制定一个目标服务,通过消息框架,从服务器接收消息。

STOMP协议的结构与HTTP结构相似,由三部分组成:命令、header、消息体。命令与header使用UTF-8格式,命令主要包括SEND、SUBSCRIBE、MESSAGE、CONNECT、CONNECTED等;header类似HTTP,有content-length、content-type等;消息体与HTTP相似,可以是二进制也可以是文本。

STOMP建立连接时和HTTP、WebSocket类似,首先要确认双方都支持STOMP协议,通过建立连接来确认;由于STOMP连接是一个长连接,协议定义了发送心跳来监测STOMP连接是否存活:在CONNECT命令消息中加入心跳header来建立连接就开启了心跳,如果在建立连接时没有心跳header,默认当作不发心跳,也不接收其他用户发送的心跳。