共计 3624 个字符,预计需要花费 10 分钟才能阅读完成。
导语
2021 腾讯游戏年度发布会在线上举办。往年,发布会以“超级数字场景”策略理念为外围,传递对游戏认知、产业边界的建设性思考,并通过 60 余款游戏产品与内容集中公布,展示腾讯游戏为玩家带来的丰盛体验与多元价值。
本次发布会再次抉择了云开发 CloudBase 作为技术选型之一,以极低的老本实现了实时弹幕零碎,并保障稳固运行,为游戏爱好者带来了优质互动体验。下文将重点介绍项目组应用云开发实现弹幕性能的全过程。
“各部门留神,后方高能!”
一、业务背景
2021 腾讯游戏年度发布会开发了专属小程序,蕴含直播、抽奖、观看回放等性能,其中所有的弹幕性能均基于云开发的实时数据推送实现。
在进行弹幕性能的技术选型前,开发同学梳理了业务场景:
- 弹幕实时互动
- 容许大量的弹幕失落
- 仅发布会直播当晚应用
- 敏感信息 / 关键字过滤
在综合思考老本、稳定性、与小程序适配性等多个方面后,我的项目最终抉择了云开发的实时数据推送性能,早在 去年的发布会里,项目组就应用了云开发的实时数据推送来实现直播节目单进度揭示等性能,在此基础上,把弹幕也对立搬上云开发。
二、技术实际
开发思路
一开始想间接把全副用户的弹幕汇合间接监听,但官网限度单次监听数据不能大于 5000 条,且监听数据条越多初始化性能越差,超出下限会抛错并进行监听。最初设计为:用户弹幕插入汇合 a,监听数据汇合 b,应用云函数的定时器定期合并弹幕,并更新到对应的正在监听的数据记录上(如图)。
这样保障了用户监听的数据记录为恒定数量,这里采纳 10 条记录(循环数组)汇总弹幕数据,每秒更新以后工夫戳的所有弹幕到 index = timestamp%10 的数据记录上,同时弹幕刷新频率固定为 1s,加重前端因为数据频繁改变而一直 callback/ 渲染的性能耗费。
代码演示
用户发送弹幕局部代码:
exports.main = async (event, context) => {
// ... 省略局部鉴权 / 黑名单 / 校验内容平安逻辑
let time = Math.ceil(new Date().getTime() / 1000);
// 插入弹幕
let res = await db.collection('danmu_all').add({
data: {
openid,
content,
time,
},
});
return {err: 0, msg: 'ok'};
};
弹幕合并解决:
exports.main = async (event, context) => {
// .... 省略一部分非关键代码
// 只取其中 100 条弹幕,可动静调整
let time = Math.ceil(new Date().getTime() / 1000) - 1;
const result = await db
.collection('danmu_all')
.where({time}).limit(100).get();
let msg = [];
for (let i of result.data) {
msg.push({
openid: i.openid,
content: i.content,
});
}
// 更新循环数组的对应地位
db
.collection('watch_collection')
.where({index: time % 10})
.update({data: {msg,time},
});
return msg;
}
前端解决音讯告诉,留神不要反复 watch。其中如果关上了云开发的匿名登录,那 H5 端的页面同样能够应用同步弹幕性能:
this.watcher = db.collection('watch_collection').watch({onChange: function(snapshot) {for (let i of snapshot.docChanges) {
// 疏忽非更新的信息
if (!i.doc || i.queueType !== 'update') {continue;}
switch (i.doc.type) {
// ... 省略其余类型的音讯解决
case 'danmu':
// 弹幕渲染
livePage.showServerBarrage(i.doc.msg);
break;
}
}
},
});
至此,整个弹幕的外围性能曾经齐全实现。
二次优化
跑了一段时间后发现偶现抛弃几秒内的弹幕,前面查看执行日志,发现即便配置定时器为每秒执行一次,理论生产中也不是严格每秒执行一次,有时候会跳过 1 - 3 秒去执行,这里另外应用了 redis 去标记以后解决的进度,即便有跳过的秒数,也能往前回溯未解决的工夫进行补录。其中云函数应用 redis 的教程能够查看官网 云函数应用 redis 教程。
用户发送弹幕局部代码增加标记代码:
exports.main = async (event, context) => {
// ... 省略局部鉴权跟校验内容平安代码
// ... 省略插入代码
// 标记合并工作
await redisClient.zadd('danmu_task', time, time+'')
};
弹幕合并解决,留神:要 redis5.0 以上的才反对 zpopmin 命令,如需购买,须要选对版本。
exports.main = async (event, context) => {
// 以后秒
let time = Math.ceil(new Date().getTime() / 1000) - 1;
while (true) {
// 弹出最小的工作
let minTask = await redisClient.zpopmin('danmu_task');
// 以后无工作
if (minTask.length <= 0) {return;}
// 以后秒的工作,往回塞,并完结
if (parseInt(minTask[0]) > time) {redisClient.zadd('danmu_task', minTask[1], minTask[0]);
return;
}
// 执行合并工作
await danmuMerge(time);
}
};
平安逻辑上也做了肯定的策略,如本地先渲染发送的弹幕,客户端收到弹幕推送时,判断 openid 为本人时候不渲染,这样即便用户的弹幕被过滤掉也能在本地展示,保留肯定的用户体验。
另外,单个云函数的实例下限是 1000,如果确定当晚流量比拟大,能够思考用多个云函数摊派流量。
治理后盾的实现
同时,利用 watch 性能能够做到治理后盾同步实时刷新客户端的弹幕,达到治理的目标,同一份代码前端和治理端都能复用:
节选局部治理后盾代码:
methods: {stop() {this.watcher.close();
},
},
beforeDestroy() {this.watcher.close();
},
mounted() {
this.app = this.$store.state.app;
this.db = this.app.database();
let that = this;
this.watcher = this.db.collection('danmu_merge').watch({onChange(snapshot) {for (let d of snapshot.docChanges) {for (let v of d.msg) {that.danmu.unshift(v);
}
}
if (that.danmu.length > 500) {that.danmu = that.danmu.slice(0, 499);
}
},
});
汇合的读权限设置在实时数据推送里同样失效,如果权限是设置为仅可读用户本人的数据,则监听的时候无奈监听到非用户本人创立的数。
Tips
过后没留神到 watch 对数据库权限限度的问题,数据库权限默认为仅创建者可读写,循环数组第一次初始化是开发过程中在客户端创立,默认增加了以后用户的 openid,导致其余用户无奈读取到 merge 的数据,解决办法:删除 openid 字段或设置权限为全副人可读。
汇合的读权限设置在实时数据推送里同样失效,如果权限是设置为仅可读用户本人的数据,则监听的时候无奈监听到非用户本人创立的数。
三、我的项目成绩与价值
基于云开发的云函数、实时数据推送、云数据库等能力,我的项目全程安稳运行,即使在发布会当晚流量峰值的时候,弹幕的写入运行稳固。在监听方面(读),watch 的性能可能稳固反对百万级同时在线。
最终, 2 名研发仅用 2 天就实现了弹幕零碎的开发和调试。而在费用方面,撑持整个我的项目弹幕零碎运行的总费用仅为100 元左右,次要集中在数据库读写和云函数调用(目前监听数据库实时数据性能处于收费阶段,不会计算到数据库读取费用上),抛去其余模块的费用,理论弹幕模块可能仅耗费了小几十块钱,费用大大低于预期,绝对比传统即时通讯等计划节俭超过数十倍。
总体上,我的项目采纳云开发,具备以下劣势:
- 自带弹性扩缩容,能够抗住刹时高并发流量,保障直播顺利进行;
- 费用便宜,只收取云函数调用和数据库读写费用,实时数据推送收费应用,非常适合我的项目;
- 平安稳固,我的项目的拜访都基于云开发自带的微信公有链路实现,保障安全性;
- 自由度高,可能符合其余开发框架和服务。