物联网核心功能揭秘:设备影子深度解析与高级设计指南

需求

设备影子是干什么的呢?实际上就是实时维护设备的属性,供app查询属性。挑战点在于当设备量较大时,如何确保设备属性变更的低延时,同时保证重要的数据(设备在离线状态)的正确展示。

设计

首先要明确整个链路。数据的来源在于各种联网设备,当设备属性(电量、水量)变更的时候通过mqtt协议,发送设备属性变更的消息,然后将mqtt消息转换为Kafka消息,通过设备影子服务进行属性的维护。

为什么使用mqtt协议

mqtt相对http协议更轻量级,广泛用于物联网的场景,适合移动端的弱网场景。

这个链路中,Kafka的作用主要是削峰填谷,使消费者能按照一定的速度消费消息。

那么设备影子如何高效地消费消息呢?

首先设备影子作为Kafka的消费者,可以配置每次从Kafka的服务端拉取一定数量的消息批量消费,这个数量需要经过测试得到一个最优的值。

拿到一批的消息之后如何持久化呢?这里有两种方式,一种是异步消费每一条消息,一种是同步批量消费消息。如果是异步消费,大量的消息都需要写入db,db的压力会非常大,所以更佳的做法是将这一批次的消息批量写入数据库,减少数据库的io成本。

对于某些重要的属性,例如设备的在离线状态,需要正确地维护其状态。设想这样一种情况:某个设备先开机后迅速关机,会发送两条设备变更的消息,一条是该设备状态改为在线,一条是设备状态改为离线,由于网络等原因,后面一条消息先消费,前面一条消息后消费,设备的状态就会改为在线,实际设备已经离线了。对于这种情况,可以参考乐观锁的实现,消费消息的时候判断当前时间是否大于最近修改的时间,代表当前的消息是新的消息,可以写入并更改最近修改时间。

当前查询设备状态的qps在高峰时大概在2w左右,所以设备的状态需要缓存到redis中,那么在当前情况下,如何设计缓存一致性方案呢?

  • 先改db再删缓存,采用旁路更新的策略来修改缓存,在设备属性大量变更的场景下,会有一个问题:某个设备的缓存会一直删除,如果此时用户需要查询设备的状态,那会直接取数据库查,那么在高峰时,会有大量的流量打到数据库。
  • 所以这里采用的方案是:先改db再改缓存。这个方法有两个问题,第一个是在并发的时候,缓存会不一致,第二个问题是,当设备属性变更的时候,会有大量数据缓存到redis中。针对第一个问题,由于Kafka这个topic选择的分区键是设备的id,所以同一个设备的属性只会落到同一个消费者,而消费的方式也是同步的批量消费,没有并发修改的问题。对于第二个问题,redis中缓存大量的数据,缓存有设置过期的时间(5分钟),到了时间后会删除缓存。
  • 其他细节

    如何保证消息消费的有序性

    kafka的单个partition内的消息是有序的,发送消息的时候按照设备id作为分区键,同一个设备的消息只会在同一个分区,消费者消费的时候串行消费就能保证有序性

    如何保证消息不丢失

    对于生产端来说,设置acks参数,如果Kafka服务端未确认,需要重新发送;对于消费端来说,需要手动提交偏移量;对于Kafka服务端来说,存在副本和持久化机制。

    后续规划

    如果设备的量级提高十倍,如何进行优化?这个链路的瓶颈在于Kafka的异步消费,如果qps过高,会存在消息大量积压,所以需要增加topic的patition数量,同步增大partion对应的消费者数量。

    作者:h wd

    物联沃分享整理
    物联沃-IOTWORD物联网 » 物联网核心功能揭秘:设备影子深度解析与高级设计指南

    发表回复