物联网核心功能揭秘:设备影子深度解析与高级设计指南
需求
设备影子是干什么的呢?实际上就是实时维护设备的属性,供app查询属性。挑战点在于当设备量较大时,如何确保设备属性变更的低延时,同时保证重要的数据(设备在离线状态)的正确展示。
设计
首先要明确整个链路。数据的来源在于各种联网设备,当设备属性(电量、水量)变更的时候通过mqtt协议,发送设备属性变更的消息,然后将mqtt消息转换为Kafka消息,通过设备影子服务进行属性的维护。
为什么使用mqtt协议?
mqtt相对http协议更轻量级,广泛用于物联网的场景,适合移动端的弱网场景。
这个链路中,Kafka的作用主要是削峰填谷,使消费者能按照一定的速度消费消息。
那么设备影子如何高效地消费消息呢?
首先设备影子作为Kafka的消费者,可以配置每次从Kafka的服务端拉取一定数量的消息批量消费,这个数量需要经过测试得到一个最优的值。
拿到一批的消息之后如何持久化呢?这里有两种方式,一种是异步消费每一条消息,一种是同步批量消费消息。如果是异步消费,大量的消息都需要写入db,db的压力会非常大,所以更佳的做法是将这一批次的消息批量写入数据库,减少数据库的io成本。
对于某些重要的属性,例如设备的在离线状态,需要正确地维护其状态。设想这样一种情况:某个设备先开机后迅速关机,会发送两条设备变更的消息,一条是该设备状态改为在线,一条是设备状态改为离线,由于网络等原因,后面一条消息先消费,前面一条消息后消费,设备的状态就会改为在线,实际设备已经离线了。对于这种情况,可以参考乐观锁的实现,消费消息的时候判断当前时间是否大于最近修改的时间,代表当前的消息是新的消息,可以写入并更改最近修改时间。
当前查询设备状态的qps在高峰时大概在2w左右,所以设备的状态需要缓存到redis中,那么在当前情况下,如何设计缓存一致性方案呢?
其他细节
如何保证消息消费的有序性?
kafka的单个partition内的消息是有序的,发送消息的时候按照设备id作为分区键,同一个设备的消息只会在同一个分区,消费者消费的时候串行消费就能保证有序性
如何保证消息不丢失?
对于生产端来说,设置acks参数,如果Kafka服务端未确认,需要重新发送;对于消费端来说,需要手动提交偏移量;对于Kafka服务端来说,存在副本和持久化机制。
后续规划
如果设备的量级提高十倍,如何进行优化?这个链路的瓶颈在于Kafka的异步消费,如果qps过高,会存在消息大量积压,所以需要增加topic的patition数量,同步增大partion对应的消费者数量。
作者:h wd