Monibuca v4.6.5 发布 —— 开源 Go 语言流媒体服务器
发布摘要 启动工程增加对流的发布订阅的单元测试和基准测试 修复发布和订阅并发创建流产生的panic 修复IdleTimeout无效问题 修复订阅者阻塞导致读写并发问题 preview插件修复https默认端口 gb28181插件PR合并 升级模块 升级后版本 engine 4.13.5 gb28181 4.3.6 hls 4.3.1 preview 4.1.2 record 4.3.7 详细说明 单元测试和基准测试 测试用例所在目录:github.com/langhuihui/monibuca工程下的test目录 推荐使用vscode打开工程,方便一键测试单个用例,也可以通过vscode侧边栏里的单元测试栏目管理。 TestPubAndSub funcTestPubAndSub(t*testing.T){ t.Cleanup(FreeEngine) UseEngine() t.Run("publish",func(t*testing.T){ t.Parallel() varpubUnitTestPublisher unitTestPlugin.Publish("test/001",&pub) }) t.Run("subscribe",func(t*testing.T){ t.Parallel() varsubUnitTestSubsciber sub.TB=t err:=unitTestPlugin.Subscribe("test/001",&sub) iferr!=nil{ t.Fatal(err) }else{ sub.PlayRaw() } }) } 该用例会启动一个发布者和一个订阅者,发布者将会写入假数据,订阅者读取后会校验数据是否正确。 BenchmarkPubAndSub funcBenchmarkPubAndSub(b*testing.B){ b.Cleanup(FreeEngine) UseEngine() fori:=0;i<10;i++{ i:=i gofunc(iint){ varpubUnitTestPublisher unitTestPlugin.Publish(fmt.Sprintf("testb/%d",i),&pub) }(i) gob.RunParallel(func(pb*testing.PB){ varsubUnitTestSubsciber sub.TB=b err:=unitTestPlugin.Subscribe(fmt.Sprintf("testb/%d",i),&sub) iferr!=nil{ //b.Fatal(err) }else{ sub.PlayRaw() } }) } time.Sleep(time.Second*10) } 该用例为基准测试,启动10个发布者,每个流会批量订阅,以测试并发性能以及稳定性。 TestSlowSubscriber funcTestSlowSubscriber(t*testing.T){ t.Cleanup(FreeEngine) UseEngine() varpubUnitTestPublisher unitTestPlugin.Publish("test/slow",&pub) varsuberSlowSubsciber unitTestPlugin.Subscribe("test/slow",&suber) suber.PlayRaw() } 这个用例会模拟一个订阅者被阻塞(sleep)后导致发布者写入的位置追上,此时engine通过标记这个写入点废弃来避免并发读写问题,废弃后订阅者如果被唤醒就会自动停止订阅。 通常出现这种情况是订阅者进行了某种耗时操作,比如写文件,或者网络阻塞等。 其他修复问题 并发创建流 修复如下: funcfindOrCreateStream(streamPathstring,waitTimeouttime.Duration)(s*Stream,createdbool){ p:=strings.Split(streamPath,"/") iflen(p)<2{ log.Warn(Red("StreamPathFormatError:"),streamPath) returnnil,false } actual,loaded:=Streams.LoadOrStore(streamPath,&Stream{ Path:streamPath, AppName:p[0], StreamName:strings.Join(p[1:],"/"), StartTime:time.Now(), +Logger:log.LocaleLogger.With(zap.String("stream",streamPath)), +timeout:time.NewTimer(waitTimeout), }) ifs:=actual.(*Stream);loaded{ s.Debug("StreamFound") returns,false }else{ -s.timeout=time.NewTimer(waitTimeout) s.Subscribers.Init() -s.Logger=log.LocaleLogger.With(zap.String("stream",streamPath)) -s.Info("created") s.actionChan.Init(1) +s.Info("created") gos.run() returns,true } } 原理:当并发调用findOrCreateStream函数时,前者尚未来得及对Logger赋值,后者就调用了s.Debug导致空指针错误。 修复读写并发问题 这个问题在前面的单元测试中已经提到,就是订阅者阻塞引起的。 订阅者阻塞后,读取点移动很慢或者不移动,导致写入点追上 由于RingBuffer是由链表结构实现,因此很容易将节点剥离主环 废弃后,这个订阅者将无法再读取主环内容,也将遭到抛弃 抛弃 https默认端口 默认https端口已经设置为8443,preview插件在选择WebTransport的时候跳转错了端口号。 https证书已经嵌入到程序里面,可以直接设置host文件127.0.0.1 local.monibuca.com访问https://local.monibuca.com:8443/preview/ gb28181 合并PR #95 本次PR主要修改了定时任务相关功能,包括: 定时删除超时设备 修改注册有效期配置默认值为3600s 设备状态变更处理 具体可以看代码变动