• Mediasoup在node.js下多线程实现


    mediasoup基于socket.io的交互消息来完成join-room的请求过程。Join的过程,实际就是获取stream的过程,也就是视频加载时间(video-load-speed)。在RTMP系统,视频加载时间是秒开。Mediasoup给出的第一个frame是I-frame,但由于交互的消息较多(8条),node.js是单线程处理框架,并发处理能力明显不足。即使50个并发,也有超过20%的用户视频加载时间>1000ms。

    Join-room的过程,由8条消息组成:

    #1: roomEventHandler:createRoom,roomId=demo-ch-12

    #2: roomEventHandler:join,roomId=demo-ch-12

    - addPeer:tXYB1vc5tzNaPQ3TAAFX,demo-ch-12-10.146.11.63 peers=46

    - {"event":"User joined","roomId":"demo-ch-12","name":"user_270","ip":"10.146.1.169","peers":1,"routerId":"6e981d85-386b-4fab-b693-9c3fd733d228"}

    #3: roomEventHandler:getRouterRtpCapabilities,roomId=demo-ch-12

    #4: roomEventHandler:createWebRtcTransport,roomId=demo-ch-12

    #5: roomEventHandler:getProducers,roomId=demo-ch-12

    #6: roomEventHandler:consume,roomId=demo-ch-12

    #7: roomEventHandler:consume,roomId=demo-ch-12

    #8: roomEventHandler:connectTransport,roomId=demo-ch-12

    Exit-room过程,仅一条消息:

    #1: roomEventHandler:exitRoom,roomId=demo-ch-12

    - removePeer:tXYB1vc5tzNaPQ3TAAFX,demo-ch-12-10.146.11.63 peers=45

    - {"event":"exitRoom","reason":{}}

    Mediasoup-client在获取到webrtc streaming data后,交给chrome等浏览器提供的api进行render渲染。这个还需要几十ms时间才能真正观看到视频。

    在对node.js框架下的webrtc-server进行多进程MP/多线程MT改造前,先要对消息处理模块socket.io和webrtc流媒体处理模块mediasoup-worker进行介绍。

    app = express()

    httpsServer = httpolyglot.createServer(credentials, app)

    httpsServer.listen(config.listenPort)

    iowsSvr = sockio(httpsServer, {cors:true, cookie:{name:"iows",sameSite:"strict",maxAge:86400} })

    httpServer是一个单进程,基于它的socket.io-event处理程序是多线程。

    1. for (let i = 0; i < numWorkers; i++) {
    2. let worker = await mediasoup.createWorker({
    3. logLevel: config.mediasoup.worker.logLevel,
    4. logTags: config.mediasoup.worker.logTags,
    5. rtcMinPort: minPort,
    6. rtcMaxPort: maxPort
    7. }) //一个mediasoup-worker fork出一个子进程。
    8. }

    首先尝试多线程MT方案,用node.js的worker_threads模块。

    1. const { Worker, isMainThread, parentPort, workerData } = require('worker_threads')
    2. function iowsSvrListener (ioSvr) {
    3.   ioSvr.on('connection', (socket) => {
    4.     if (isMainThread){
    5.       const wsWorker = new Worker(__filename, { workerData: socket })
    6.       wsWorker.on('exit', () => {
    7.         console.warn("wsWorker exit.")
    8.         wsWorker.terminate()
    9.       })
    10.       wsWorker.on('message', (msg) => {
    11.         console.debug("wsWorker recv:", msg)
    12.         let socket = msg.socket
    13.         socket.onAny(async (eventName, args, cbFunc) => {
    14.           await roomEventHandler(eventName, args, socket, cbFunc)
    15.         })
    16.         socket.on('disconnect', async (args) => {
    17.           await roomEventHandler('disconnect', args, socket)
    18.         })
    19.       })
    20.     }
    21.     else {
    22.       parentPort.postMessage(workerData)
    23.       setInterval(() => {
    24.       }, 60e3)
    25.     }
    26.   })
    27. }

    上面这段代码,运行报错:

      - exitHandler:[object Promise],error=function noop() { } could not be cloned.

      >> socket is a complicated object which can not be passed to worker.

    实际上,socket.io就是多线程框架,因此,我认为在多线程里面再去实现一个multi-threads是没有意义的。

    下面再尝试一下多进程,用const cluster = require('cluster')

    由于mediasoup-worker也是多进程,因此就成为socket.io server + mediasoup-worker的架构,这种框架,就大大增加SFU这种webrtc-server处理的复杂性。比如一个broadcaster在processor#1上进行直播,有一个room#1,一个观众从processor#2上来,他根本就无法join到room#1。这种情况就得要求processor#1上的mediasoup-worker pipe到processor#2上来才行。当然还有一个办法是processor#1上的socket.io forward msg to processor#2上的socket.io进行处理(内部转发),消息处理流程显得复杂了,多进程带来的效率提升恐怕也会被吃掉。

    最终的结论就是,只能维持当前单进程socket.io server + 多进程mediasoup-worker的框架不变。如果想要进一步提升单台SFU-server的join速度,只能采用Go语言的协程(goroutine)机制了,但这能带来多大的性能提升呢?毕竟node.js的socket.io server性能也不差!而且,整体来说,mediasoup worker能够处理多大的视频流量,这受限于VPS服务器的bandwidth。

    1 Gbps的网络,单进程mediasoup-worker就可以轻松处理(cpu load < 60%),可支持到200 peers(video + audio)。那么一个5Gbps的网络,满负荷运行需要5颗cpu,加上socket.io需要一颗cpu,那么6 core cpu的VPS就行了。

  • 相关阅读:
    [Mac软件]Leech for Mac v3.2 - 轻量级mac下载工具
    全网最细,真实企业性能测试落地实施,一文带你快速打通...
    交换机与路由技术-30-标准ACL
    黑盒测试的5种方法介绍
    Lua语法讲解
    OpenCV4(C++)—— 直方图
    Python批量导入及导出项目中所安装的类库包到.txt文件(补充)
    【微信小程序】使用npm并引入vant-weapp组件库
    Java Integer bitCount()方法具有什么功能呢?
    实用:AE/PR 视频交换格式哪家强?
  • 原文地址:https://blog.csdn.net/freeman1975/article/details/132631877