前言
在一个风和日丽的早上,嘴边叼着吸管,吮吸着9.9一杯的瑞兴咖啡,驱散着乏味的工作日早上,电脑页面停留在企业微信聊天框,时不时敲着键盘随性地回复着聊天搭子的闲聊,眼神瞟着周围,防止领导突然空降身后,对我发出友好的问候;哦豁,坐我旁边的团队技术经理开完会走过来了,我连忙切掉企业微信的聊天框,切换到程序员的专属大宝贝~IDEA的开发界面,不得不佩服我单身多年的手速,基操勿六。
探讨开始
项目介绍
技术经理刚一坐下来,就简单复述了这次会议的内容,部门决定要开发一个智能物联网升降机项目,在以往设备管理服务云平台通用功能的基础上,新起了一个项目去定制开发使升降机实现联网行为,核心业务是围绕着设备的行为展开的,通过对接设备管理服务云平台,接收并解析设备管理服务云平台转发过来的设备数据报文,通过计算策略,计算升降机的实时状态,通过Websokcet连接通道,实时推送设备的状态数据到客户端,同时统计设备的运行数据,生成设备的日报表数据;
项目的特殊性
不同于以往的业务场景,都是由网关收集设备传感器的状态数据,并传输到控制器中进行处理和存储,然后转发处理后的状态数据到云端,云端应用只做储存的逻辑,这次网关接收到升降机的状态信号量,就根据上报策略转发到云端,不做处理跟存储的动作了,需要云端应用自行解析由网关转发的升降机状态报文,就比如升降机当前楼层的计算逻辑,升降机从非平层变成平层状态,需要查看升降机的上次运行状态,如果是上行的话,当前楼层+1,反之,当前楼层-1,又好比升降机运行时间的计算逻辑,当升降机从运动状态变成了静止,需要计算发生运动时的时间戳到当前静止状态的时间戳的间隔,记为一次完整的运行周期,升降机有多个信号量,那个信号量触发了,就按照网关的采集上报转发到云端。
技术经理问我,小陈,你有什么处理方案吗,针对这次项目需求?
程序处理计算状态数据+Redis的方案可行吗?
我们接收到升降机的状态数据报文,解析对应的报文,根据计算逻辑,实时计算出升降机的实时状态数据以及运行数据,组装当前设备上报的时间戳作为最新设备上报时间,缓存到redis里面,经理你觉得可行吗?
经理看了看我,反问我道,那个程序执行的顺序该怎么保证呢?现在设备上报报文频率是很高的,按照现在网关的采集上报频率,可能会出现一秒钟上报几次状态数据的场景,在现在多节点服务部署的情况下,如果采用程序处理计算的方式,计算状态数据结果后,缓存的先后顺序无法得到保证,会导致状态数据错乱,该方案不可行。
程序处理计算状态数据+分布式锁+Redis的方案可行吗?
基于上面的问题,我们加入分布式锁的解决方案,我们接收到升降机的状态数据报文,解析对应的报文,把升降机的设备编码,作为分布式锁的粒度,默认一分钟的锁过期时间,给当前报文实时计算出升降机的实时状态数据以及运行数据,组装当前设备上报的时间戳作为最新设备上报时间,缓存到redis里面的程序处理加上分布式锁,经理你觉得可行吗?
经理又看了看我,反问我道,你是不是没听清楚我前面说的前提,现在设备上报报文频率是很高的,按照现在网关的采集上报频率,可能会出现一秒钟上报几次状态数据的场景,如果你直接加锁,线程一直在竞争等待获取分布式锁,不光业务处理缓慢,还可能会引起服务崩溃的情况,该方案不可行。
Lua+Redis的方案可行吗?
经理看我沉思了半天,没再说出什么改进的方案来,于是开始娓娓道来,做电商项目的小伙伴们经常会遇到一个场景,那就是减库存的业务场景,在多节点高并发的场景下,可能会出现秒杀超卖的异常情况,比较常见的解决方案就是把判断库存扣减库存的操作都写在Lua脚本,然后丢给Redis去执行操作,如果库存等于0了,直接返回库存不可用了,我们现在的业务场景也是类似的,我们可以把计算设备状态数据的计算逻辑都放在Lua脚本里,然后通过Redis去执行操作,那Lua脚本是什么?又是怎么实现这样子的效果的?
Lua 脚本功能是 Reids在 2.6 版本的最大亮点, 通过内嵌对 Lua 环境的支持, Redis 解决了长久以来不能高效地处理 CAS (check-and-set)命令的缺点, 并且可以通过组合使用多个命令, 轻松实现以前很难实现或者不能高效实现的模式。
Lua脚本是类似Redis事务,有一定的原子性,不会被其他命令插队,可以完成一些Redis事务性的操作。 这点是关键,可以保证我们处理设备上报的状态数据的执行顺序是有先后保证的。
我听完惊为天人,没想到还有此解决方案,从来都没听过Lua脚本的我,也没接触过什么电商项目,自然就没遇到过类似的场景了;
突然想到Redis 6.0之后不是开始支持多线程了吗?在Redis多线程场景下,Lua执行的先后顺序还能得到保证吗?我反问经理道。
Redis 多线程非彼“多线程”
经理听到我的疑惑,调侃道,你小子看技术版本更新,看一半丢一半是吧,虽然Redis 6.0 引入了多线程支持,但需要注意的是,它的多线程并不是指服务器能够并行处理多个客户端请求。实际上,Redis 6.0 的多线程主要用于后台任务,比如对持久化文件的压缩、异步数据刷新等。关于主线程顺序执行命令,Redis 主线程仍然是按顺序执行接收到的命令请求。这意味着每个客户端请求的处理是顺序进行的,不会并行处理多个客户端请求。因此,多线程主要用于提升后台任务的效率和响应性,而对于客户端请求的处理依然是单线程顺序执行的,所以就不存在你说的那种情况,
总结
经过此次项目技术选型的历程,让我对技术服务于业务有了更深层次的理解,实现业务的技术方案是多样性的,但是选对技术选型,往往是基于多因素去深思熟虑的。