💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
[TOC] ## 创建房间 ### 创建token 服务端从**basicServer.js**开始, 在此以之前的**Basic Example**的客户端发送的**CreteToken**为例子, 服务端的**http**框架监听到该请求后开始处理, 对发送过来的消息进行解析之后,调用**getOrCreateRoom**创建**room**和**token**. ``` //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数组中进行管理 ``` ~~~tsx 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); }); }); }; ```