開發者不騙開發者,你跟我說這隻要100塊?

導語

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元左右,主要集中在數據庫讀寫和雲函數調用(目前監聽數據庫實時數據功能處於免費階段,不會計算到數據庫讀取費用上),拋去其他模塊的費用,實際彈幕模塊可能僅消耗了小几十塊錢,費用大大低於預期,相對比傳統即時通訊等方案節省超過數十倍。

總體上,項目採用雲開發,具備以下優勢:

  • 自帶彈性擴縮容,可以抗住瞬時高併發流量,保障直播順利進行;
  • 費用便宜,只收取雲函數調用和數據庫讀寫費用,實時數據推送免費使用,非常適合項目;
  • 安全穩定,項目的訪問都基於雲開發自帶的微信私有鏈路實現,保證安全性;
  • 自由度高,能夠契合其他開發框架和服務。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章