EasyNVR作为一款稳定的流媒体服务平台,已经在很多场景得到了应用,比如智慧城市、智慧校园等方面,很多场景都会有几百路甚至几千路的摄像头接入,因此我们也需要对这些高接入量的场景进行测试。
在测试EasyNVR时发现在同时拉200路非按需的流时,播放视频时会出现卡顿的现象,需要将流减少到100路左右播放才不会卡顿,这个卡顿非常影响项目的使用。
起初我们以为是内核的原因,因为接入的两百路流只有第一路是使用obs推的,其它都是使用nvr内核分发的第一路rtsp流,所以猜测是不是内核本身分发的限制导致卡顿,于是我们重新开了两个内核,从每个内核上拉100路流,测试后发现还是会卡顿,排除了内核的原因。
我们开始在go上层代码推流的地方查找问题,发现在收到视频帧回调时会判断是不是关键帧,如果是关键帧会把它保存文件,又猜测是不是这里保存文件耗时导致的卡顿,接着将这里的代码进行了注释,发现还是不对。我们又将推流里所有可能影响卡顿的地方都进行了优化,能使用协程的地方都启用了协程,能优化的文件操作都进行了优化,测试还是会卡顿。
最后在没有任何思绪的查找中发现了一个回调方法:client.OnlineCallBack(1),找到这个函数定义的地方后发现此函数有两个功能,第一设置通道在线状态,第二查询数据库里通道状态,如果此时的通道状态和数据库里的不一致会更新数据库里的通道在线、离线状态,于是立马将数据库操作注释后又跑了一遍,发现卡顿现象消失了,终于定位到了问题所在。
当时在这里设置回调更新数据库里的通道状态,是因为在EasyNVR的通道列表有个根据在线、离线条件筛选数据的功能:
为了筛选和分页功能所以在推流的地方设置了回调更新数据库,但是现在测试200路非按需的流,回调视频帧时每秒有25帧,也就是每秒会查询5000次数据库,所以造成了卡顿。
这个问题的解决方案有两个:
方法一:
去除OnlineCallBack回调方法里的更新数据库,用定时任务的方法设定一个时间间隔,定时查询数据库里的状态和内存中的状态做比对,不一样的则进行更新,但是这种方法有两个缺点: 1、数据库里的状态不是实时更新。 2、定时任务需要查询数据库里的所有通道,所以接入的通道过多时还是会有大量的数据库查询。
方法二:
还是在OnlineCallBack回调方法里更新数据库,但是在更新之前先判断上次的状态,和上次的状态不一样才调用OnlineCallBack更新状态,这样就只会在每个通道上下线时才会更新数据库,而且在更新之前也不需要进行数据库查找比对了。
这个方法既能实时更新数据库里的状态又减少了大量的数据库操作,所以选择了第二种方法。在修改后测试拉了200路非按需流,并同时在EasyNVR上播放了16路通道没有出现卡顿现象,卡顿问题解决。