优化消息平滑算法,修复极端情况下消息延迟的问题;自定义模板SDK升级到v1.0.1

This commit is contained in:
John Smith
2026-04-26 13:57:48 +08:00
parent b4995cc358
commit 7c2bb53cf7
2 changed files with 54 additions and 22 deletions

View File

@@ -9,7 +9,7 @@
}(typeof self !== 'undefined' ? self : this, function() { }(typeof self !== 'undefined' ? self : this, function() {
const exports = {} const exports = {}
const VERSION = '1.0.2' const VERSION = '1.0.3'
/** /**
* 取SDK版本 * 取SDK版本
* @returns {string} "1.0.0" * @returns {string} "1.0.0"
@@ -232,6 +232,9 @@
} }
} }
// 用来统计进队列时间间隔
const ENQUEUE_INTERVALS_MAX_TIME_RANGE = 3000
const ENQUEUE_INTERVALS_MAX_LENGTH = 10
// 发送消息时间间隔范围 // 发送消息时间间隔范围
const MSG_MIN_INTERVAL = 80 const MSG_MIN_INTERVAL = 80
const MSG_MAX_INTERVAL = 1000 const MSG_MAX_INTERVAL = 1000
@@ -265,18 +268,31 @@
} else { } else {
let curTime = new Date() let curTime = new Date()
let interval = curTime - this._lastEnqueueTime let interval = curTime - this._lastEnqueueTime
// 真实的进队列时间间隔模式大概是这样2500, 300, 300, 300, 2500, 300, ... this._enqueueIntervals.push(interval)
// B站消息有缓冲会一次发多条消息。这里把波峰视为发送了一次真实的WS消息所以要过滤掉间隔太小的
if (interval > 1000 || this._enqueueIntervals.length < 5) { // 统计最近ENQUEUE_INTERVALS_MAX_TIME_RANGE内的间隔最多ENQUEUE_INTERVALS_MAX_LENGTH个
this._enqueueIntervals.push(interval) let keepFrom = this._enqueueIntervals.length - 1
if (this._enqueueIntervals.length > 5) { let minKeepFrom = Math.max(this._enqueueIntervals.length - ENQUEUE_INTERVALS_MAX_LENGTH, 0)
this._enqueueIntervals.splice(0, this._enqueueIntervals.length - 5) let prevIdxPassedTime = 0
for (; keepFrom > minKeepFrom; keepFrom--) {
let itInterval = this._enqueueIntervals[keepFrom]
prevIdxPassedTime += itInterval
if (prevIdxPassedTime > ENQUEUE_INTERVALS_MAX_TIME_RANGE) {
break
} }
// 这边估计得尽量大只要不太早把消息缓冲发完就是平滑的。有MESSAGE_MAX_INTERVAL保底不会让消息延迟太大
// 其实可以用单调队列求最大值,偷懒不写了
this._estimatedEnqueueInterval = Math.max(...this._enqueueIntervals)
} }
// 上次入队时间还是要设置,否则会太早把消息缓冲发完,然后较长时间没有新消息 if (keepFrom > 0) {
this._enqueueIntervals.splice(0, keepFrom)
}
// 这边估计得尽量大只要不太早把消息缓冲发完就是平滑的。有MSG_MAX_INTERVAL保底不会让消息延迟太大
let maxEnqueueInterval = this._enqueueIntervals[0]
for (let interval_ of this._enqueueIntervals) {
if (interval_ > maxEnqueueInterval) {
maxEnqueueInterval = interval_
}
}
this._estimatedEnqueueInterval = maxEnqueueInterval
this._lastEnqueueTime = curTime this._lastEnqueueTime = curTime
} }
@@ -325,7 +341,7 @@
// 消息没发完,计算下次发消息时间 // 消息没发完,计算下次发消息时间
let sleepTime let sleepTime
if (numToEmit === 1) { if (numToEmit === 1) {
// 队列中消息数很少,随便定个[MESSAGE_MIN_INTERVAL, MESSAGE_MAX_INTERVAL]的时间 // 队列中消息数很少,随便定个[MSG_MIN_INTERVAL, MSG_MAX_INTERVAL]的时间
sleepTime = estimatedNextEnqueueRemainTime / this._queue.length sleepTime = estimatedNextEnqueueRemainTime / this._queue.length
sleepTime *= 0.5 + Math.random() sleepTime *= 0.5 + Math.random()
if (sleepTime > MSG_MAX_INTERVAL) { if (sleepTime > MSG_MAX_INTERVAL) {

View File

@@ -62,6 +62,9 @@ import MembershipItem from './MembershipItem'
import PaidMessage from './PaidMessage' import PaidMessage from './PaidMessage'
import * as constants from './constants' import * as constants from './constants'
// 用来统计进队列时间间隔
const ENQUEUE_INTERVALS_MAX_TIME_RANGE = 3000
const ENQUEUE_INTERVALS_MAX_LENGTH = 10
// 要添加的消息类型 // 要添加的消息类型
const ADD_MESSAGE_TYPES = [ const ADD_MESSAGE_TYPES = [
constants.MESSAGE_TYPE_TEXT, constants.MESSAGE_TYPE_TEXT,
@@ -278,18 +281,31 @@ export default {
} else { } else {
let curTime = new Date() let curTime = new Date()
let interval = curTime - this.lastEnqueueTime let interval = curTime - this.lastEnqueueTime
// 真实的进队列时间间隔模式大概是这样2500, 300, 300, 300, 2500, 300, ... this.enqueueIntervals.push(interval)
// B站消息有缓冲会一次发多条消息。这里把波峰视为发送了一次真实的WS消息所以要过滤掉间隔太小的
if (interval > 1000 || this.enqueueIntervals.length < 5) { // 统计最近ENQUEUE_INTERVALS_MAX_TIME_RANGE内的间隔最多ENQUEUE_INTERVALS_MAX_LENGTH个
this.enqueueIntervals.push(interval) let keepFrom = this.enqueueIntervals.length - 1
if (this.enqueueIntervals.length > 5) { let minKeepFrom = Math.max(this.enqueueIntervals.length - ENQUEUE_INTERVALS_MAX_LENGTH, 0)
this.enqueueIntervals.splice(0, this.enqueueIntervals.length - 5) let prevIdxPassedTime = 0
for (; keepFrom > minKeepFrom; keepFrom--) {
let itInterval = this.enqueueIntervals[keepFrom]
prevIdxPassedTime += itInterval
if (prevIdxPassedTime > ENQUEUE_INTERVALS_MAX_TIME_RANGE) {
break
} }
// 这边估计得尽量大只要不太早把消息缓冲发完就是平滑的。有MESSAGE_MAX_INTERVAL保底不会让消息延迟太大
// 其实可以用单调队列求最大值,偷懒不写了
this.estimatedEnqueueInterval = Math.max(...this.enqueueIntervals)
} }
// 上次入队时间还是要设置,否则会太早把消息缓冲发完,然后较长时间没有新消息 if (keepFrom > 0) {
this.enqueueIntervals.splice(0, keepFrom)
}
// 这边估计得尽量大只要不太早把消息缓冲发完就是平滑的。有MESSAGE_MAX_INTERVAL保底不会让消息延迟太大
let maxEnqueueInterval = this.enqueueIntervals[0]
for (let interval_ of this.enqueueIntervals) {
if (interval_ > maxEnqueueInterval) {
maxEnqueueInterval = interval_
}
}
this.estimatedEnqueueInterval = maxEnqueueInterval
this.lastEnqueueTime = curTime this.lastEnqueueTime = curTime
} }