美文网首页
licode(2) Basic Example 后端解析

licode(2) Basic Example 后端解析

作者: 梦落迹南天 | 来源:发表于2019-11-26 13:47 被阅读0次

    整体

    服务端从basicServer.js开始, 在此以之前的Basic Example的客户端发送的CreteToken为例子, 服务端的http框架监听到该请求后开始处理, 对发送过来的消息进行解析之后,调用getOrCreateRoom创建roomtoken.

    //licode\extras\basic_example\basicServer.js
    
    app.post('/createToken/', (req, res) => {
      console.log('Creating token. Request body: ', req.body);
    
      const username = req.body.username;
      const role = req.body.role;
    
      let room = defaultRoomName;
      let type;
      let roomId;
      let mediaConfiguration;
    
      if (req.body.room) room = req.body.room;
      if (req.body.type) type = req.body.type;
      if (req.body.roomId) roomId = req.body.roomId;
      if (req.body.mediaConfiguration) mediaConfiguration = req.body.mediaConfiguration;
    
      const createToken = (tokenRoomId) => {
        N.API.createToken(tokenRoomId, username, role, (token) => {
          console.log('Token created', token);
          res.send(token);
        }, (error) => {
          console.log('Error creating token', error);
          res.status(401).send('No Erizo Controller found');
        });
      };
    
      if (roomId) {
        createToken(roomId);
      } else {
        getOrCreateRoom(room, type, mediaConfiguration, createToken); //调用到此处创建房间,创建token
      }
    });
    

    getOrCreateRoom中使用了N.API.getRooms()去向nuve获取房间列表,校验当前的房间是否已经创建,如果创建了则使用该room去创建token, 如果没有则使用N.API.createRoom()创建房间之后再去创建token.

    const getOrCreateRoom = (name, type = 'erizo', mediaConfiguration = 'default',
      callback = () => {}) => {
      if (name === defaultRoomName && defaultRoom) {
        callback(defaultRoom);
        return;
      }
    
    //向NUVE发请求获取房间列表,如果该room存在,使用这个room创建token,
    //不存在,创建room之后再创建token
      N.API.getRooms((roomlist) => {
        let theRoom = '';
        const rooms = JSON.parse(roomlist);
        for (let i = 0; i < rooms.length; i += 1) {
          const room = rooms[i];
          if (room.name === name &&
                    room.data &&
                    room.data.basicExampleRoom) {
            theRoom = room._id;
            callback(theRoom);//create token
            return;
          }
        }
        const extra = { data: { basicExampleRoom: true }, mediaConfiguration };
        if (type === 'p2p') extra.p2p = true;
    
        N.API.createRoom(name, (roomID) => {
          theRoom = roomID._id;
          callback(theRoom);//create token
        }, () => {}, extra);
      });
    };
    

    此处的N.API是一个用来发送请求到后端nuve的工具类,提供了用户的业务后台到nuve的通讯接口,licode中的设计中,对于一些基础的操作,createroken, createroom,deleteroom都通过nuve进行处理, 开发者的业务后台只需要调用并同步信息即可,如以下两个函数, 主要是用send发送请求到nuve端

    N.API = (function (N) {
        getRooms = function (callback, callbackError, params) {
            send(callback, callbackError, 'GET', undefined, 'rooms', params);
        };
    
    
        createRoom = function (name, callback, callbackError, options, params) {
            if (!options) {
                options = {};
            }
    
            send(function (roomRtn) {
                var room = JSON.parse(roomRtn);
                callback(room);
            }, callbackError, 'POST', {name: name, options: options}, 'rooms', params);
        };
    }
    

    nuve的起点在文件nuve.js中,其上面也是用express作为监听的http框架,去处理发送过来的请求,以createroom为例,如下所示,一旦有请求,首先触发对应请求的鉴权,该鉴权就不细看了,其会通过请求头的service_id获取一个service对象, 通过service进行签名校验之后鉴权结束,这个service相当于在nuve的一个session, 可通过post,get向nuve请求创建后返回id,后续请求需要带上该id(注:但该Basic Example中的是预创建的)

    // licode\nuve\nuveAPI\nuve.js
    
    //鉴权
    app.post('*', nuveAuthenticator.authenticate);
    
    - createroom

    鉴权通过后触发roomsResource.createRoom进行处理

    // licode\nuve\nuveAPI\nuve.js
    
    //监听创建房间的请求
    app.post('/rooms', roomsResource.createRoom);
    

    roomsResource.createRoom中使用roomRegistry.addRoom()往monogo中写房间,写库成功后将roomid加入service.rooms数组中进行管理

    exports.createRoom = (req, res) => {
      let room;
    
      const currentService = req.service;
      //...省略...
    
      req.body.options = req.body.options || {};
    
      if (req.body.options.test) {
      //...省略...
      }
      else {
        room = { name: req.body.name };
        if (req.body.options.p2p) {
          room.p2p = true;
        }
        if (req.body.options.data) {
          room.data = req.body.options.data;
        }
        if (typeof req.body.options.mediaConfiguration === 'string') {
          room.mediaConfiguration = req.body.options.mediaConfiguration;
        }
        
        //addroom之后进行
        roomRegistry.addRoom(room, (result) => {
          currentService.rooms.push(result); //将房间id(save返回的主键)放入数组
          serviceRegistry.addRoomToService(currentService, result);//将房间与service关联起来
          log.info(`message: createRoom success, roomName:${req.body.name}, ` +
            `serviceId: ${currentService.name}, p2p: ${room.p2p}`);
          res.send(result);
        });
      }
    };
    
    - createtoken

    创建完房间之后,basicServer发送请求创建token的请求,nuve监听到之后执行,先通过doInit找到房间,然后去创建token

    exports.create = (req, res) => {
    //获取room后执行创建token的回调
      doInit(req, (currentService, currentRoom) => {
        if (currentService === undefined) {
          log.warn('message: createToken - service not found');
          res.status(404).send('Service not found');
          return;
        } else if (currentRoom === undefined) {
          log.warn(`message: createToken - room not found, roomId: ${req.params.room}`);
          res.status(404).send('Room does not exist');
          return;
        }
    //创建token
        generateToken(req, (tokenS) => {
          if (tokenS === undefined) {
            res.status(401).send('Name and role?');
            return;
          }
          if (tokenS === 'error') {
            log.error('message: createToken error, errorMgs: No Erizo Controller available');
            res.status(404).send('No Erizo Controller found');
            return;
          }
          log.info(`message: createToken success, roomId: ${currentRoom._id}, ` +
            `serviceId: ${currentService._id}`);
          res.send(tokenS);
        });
      });
    };
    

    在generateToken()中,会将roomid,username等写入到token中,还会分配ErizoController, 将erizoController的IP和port写入到token中

    const generateToken = (req, callback) => {
    //....省略..... 
    
      token = {};
      token.userName = user;
      token.room = currentRoom._id;
      token.role = role;
      token.service = currentService._id;
      token.creationDate = new Date();
      token.mediaConfiguration = 'default';
    
    //....省略..... 
    
    //分配ErizoController, 将erizoController的IP和port写入到token中
          cloudHandler.getErizoControllerForRoom(currentRoom, (ec) => {
          if (ec === 'timeout' || !ec) {
            callback('error');
            return;
          }
    
          token.secure = ec.ssl;
          if (ec.hostname !== '') {
            token.host = ec.hostname;
          } else {
            token.host = ec.ip;
          }
    
          token.host += `:${ec.port}`;
    
          tokenRegistry.addToken(token, (id, err) => {
            if (err) {
              return callback('error');
            }
            const tokenAdded = getTokenString(id, token);
            return callback(tokenAdded);
          });
    
    };
    
    

    获取tErizoController的过程如下,首先通过room->erizoControllerId去获取erizoController, 如果没有的话,通过策略从队列中获取erizoController, 如果没有策略,则遍历队列,根据状态返回一个可用的erizoController队列,获取其首元素进行使用。

    const getErizoControllerForRoom = (room, callback) => {
      const roomId = room._id;
    
    //通过room->erizoControllerId去获取erizoController
      roomRegistry.getRoom(roomId, (roomResult) => {
        const id = roomResult.erizoControllerId;
        if (id) {
          erizoControllerRegistry.getErizoController(id, (erizoController) => {
            if (erizoController) {
              callback(erizoController);
            } else {
              roomResult.erizoControllerId = undefined;
              roomRegistry.updateRoom(roomResult._id, roomResult);
              getErizoControllerForRoom(roomResult, callback);
            }
          });
          return;
        }
    
        let attempts = 0;
        let intervalId;
    
    // 如果room->erizoControllerId是空的,从erizoController获取
        getEcQueue((ecQueue) => {
          intervalId = setInterval(() => {
            let erizoController;
            if (getErizoController) {
    //通过策略获取
              erizoController = getErizoController(room, ecQueue);
            } else {
    //从队列获取
              erizoController = ecQueue[0];
            }
            const erizoControllerId = erizoController ? erizoController._id : undefined;
    
            if (erizoControllerId !== undefined) {
              assignErizoController(erizoControllerId, room, (assignedEc) => {
                callback(assignedEc);
                clearInterval(intervalId);
              });
            }
    
            if (attempts > TOTAL_ATTEMPTS_EC_READY) {
              clearInterval(intervalId);
              callback('timeout');
            }
            attempts += 1;
          }, INTERVAL_TIME_EC_READY);
        });
      });
    };
    
    

    相关文章

      网友评论

          本文标题:licode(2) Basic Example 后端解析

          本文链接:https://www.haomeiwen.com/subject/zgljwctx.html