mirror of
https://github.com/xfgryujk/blivechat.git
synced 2025-03-28 21:10:49 +08:00
添加防止无限重连的保险措施、开放平台心跳添加随机延迟
This commit is contained in:
parent
5441cb7cd5
commit
1772caebe3
api
frontend/src
api/chat
lang
views
services
@ -41,6 +41,7 @@ class ContentType(enum.IntEnum):
|
||||
|
||||
class FatalErrorType(enum.IntEnum):
|
||||
AUTH_CODE_ERROR = 1
|
||||
TOO_MANY_RETRIES = 2
|
||||
|
||||
|
||||
def make_message_body(cmd, data):
|
||||
|
@ -27,7 +27,7 @@ export default class ChatClientDirectOpenLive extends ChatClientOfficialBase {
|
||||
super.stop()
|
||||
|
||||
if (this.gameHeartbeatTimerId) {
|
||||
window.clearInterval(this.gameHeartbeatTimerId)
|
||||
window.clearTimeout(this.gameHeartbeatTimerId)
|
||||
this.gameHeartbeatTimerId = null
|
||||
}
|
||||
this.endGame()
|
||||
@ -39,7 +39,7 @@ export default class ChatClientDirectOpenLive extends ChatClientOfficialBase {
|
||||
}
|
||||
|
||||
if (this.gameId && this.gameHeartbeatTimerId === null) {
|
||||
this.gameHeartbeatTimerId = window.setInterval(this.sendGameHeartbeat.bind(this), GAME_HEARTBEAT_INTERVAL)
|
||||
this.gameHeartbeatTimerId = window.setTimeout(this.onSendGameHeartbeat.bind(this), GAME_HEARTBEAT_INTERVAL)
|
||||
}
|
||||
return true
|
||||
}
|
||||
@ -102,6 +102,13 @@ export default class ChatClientDirectOpenLive extends ChatClientOfficialBase {
|
||||
return true
|
||||
}
|
||||
|
||||
onSendGameHeartbeat() {
|
||||
// 加上随机延迟,减少同时请求的概率
|
||||
let sleepTime = GAME_HEARTBEAT_INTERVAL - (2 * 1000) + (Math.random() * 3 * 1000)
|
||||
this.gameHeartbeatTimerId = window.setTimeout(this.onSendGameHeartbeat.bind(this), sleepTime)
|
||||
this.sendGameHeartbeat()
|
||||
}
|
||||
|
||||
async sendGameHeartbeat() {
|
||||
if (!this.gameId) {
|
||||
return false
|
||||
|
@ -122,6 +122,7 @@ export default class ChatClientOfficialBase {
|
||||
res = false
|
||||
console.error('initRoom exception:', e)
|
||||
if (e instanceof chatModels.ChatClientFatalError) {
|
||||
this.stop()
|
||||
this.msgHandler.onFatalError(e)
|
||||
}
|
||||
}
|
||||
@ -188,6 +189,17 @@ export default class ChatClientOfficialBase {
|
||||
this.retryCount++
|
||||
this.totalRetryCount++
|
||||
console.warn(`掉线重连中 retryCount=${this.retryCount}, totalRetryCount=${this.totalRetryCount}`)
|
||||
|
||||
// 防止无限重连的保险措施。30次重连大概会断线500秒,应该够了
|
||||
if (this.totalRetryCount > 30) {
|
||||
this.stop()
|
||||
let error = new chatModels.ChatClientFatalError(
|
||||
chatModels.FATAL_ERROR_TYPE_TOO_MANY_RETRIES, 'The connection has lost too many times'
|
||||
)
|
||||
this.msgHandler.onFatalError(error)
|
||||
return
|
||||
}
|
||||
|
||||
window.setTimeout(this.wsConnect.bind(this), this.getReconnectInterval())
|
||||
}
|
||||
|
||||
|
@ -96,6 +96,17 @@ export default class ChatClientRelay {
|
||||
this.retryCount++
|
||||
this.totalRetryCount++
|
||||
console.warn(`掉线重连中 retryCount=${this.retryCount}, totalRetryCount=${this.totalRetryCount}`)
|
||||
|
||||
// 防止无限重连的保险措施。30次重连大概会断线500秒,应该够了
|
||||
if (this.totalRetryCount > 30) {
|
||||
this.stop()
|
||||
let error = new chatModels.ChatClientFatalError(
|
||||
chatModels.FATAL_ERROR_TYPE_TOO_MANY_RETRIES, 'The connection has lost too many times'
|
||||
)
|
||||
this.msgHandler.onFatalError(error)
|
||||
return
|
||||
}
|
||||
|
||||
window.setTimeout(this.wsConnect.bind(this), this.getReconnectInterval())
|
||||
}
|
||||
|
||||
@ -176,6 +187,7 @@ export default class ChatClientRelay {
|
||||
break
|
||||
}
|
||||
case COMMAND_FATAL_ERROR: {
|
||||
this.stop()
|
||||
let error = new chatModels.ChatClientFatalError(data.type, data.msg)
|
||||
this.msgHandler.onFatalError(error)
|
||||
break
|
||||
|
@ -111,6 +111,7 @@ export class UpdateTranslationMsg {
|
||||
}
|
||||
|
||||
export const FATAL_ERROR_TYPE_AUTH_CODE_ERROR = 1
|
||||
export const FATAL_ERROR_TYPE_TOO_MANY_RETRIES = 2
|
||||
|
||||
export class ChatClientFatalError extends Error {
|
||||
constructor(type, message) {
|
||||
|
@ -154,6 +154,9 @@ export default {
|
||||
p4: '4. Add browser source in OBS',
|
||||
p5: '5. Enter the previously copied room URL at URL, and enter the previously copied CSS at custom CSS'
|
||||
},
|
||||
room: {
|
||||
fatalErrorOccurred: 'A fatal error has occurred. Please manually refresh the page to reconnect'
|
||||
},
|
||||
chat: {
|
||||
moderator: 'moderator',
|
||||
guardLevel1: 'governor',
|
||||
|
@ -154,6 +154,9 @@ export default {
|
||||
p4: '4. OBSでブラウザを新規作成する',
|
||||
p5: '5. プロパティでこぴーしたURLを入力し、カスタムCSSでスタイルジェネレータのCSSを入力する'
|
||||
},
|
||||
room: {
|
||||
fatalErrorOccurred: '致命的なエラーが発生しました。ページを手動で更新して再接続してください'
|
||||
},
|
||||
chat: {
|
||||
moderator: 'モデレーター',
|
||||
guardLevel1: '総督',
|
||||
|
@ -154,6 +154,9 @@ export default {
|
||||
p4: '4. 在OBS中添加浏览器源',
|
||||
p5: '5. URL处输入之前复制的房间URL,自定义CSS处输入之前复制的CSS'
|
||||
},
|
||||
room: {
|
||||
fatalErrorOccurred: '发生了一个致命错误,请手动刷新页面以重新连接'
|
||||
},
|
||||
chat: {
|
||||
moderator: '管理员',
|
||||
guardLevel1: '总督',
|
||||
|
@ -349,7 +349,12 @@ export default {
|
||||
message: error.toString(),
|
||||
duration: 10 * 1000
|
||||
})
|
||||
this.chatClient.stop()
|
||||
this.onAddText(new chatModels.AddTextMsg({
|
||||
authorName: 'blivechat',
|
||||
authorType: constants.AUTHOR_TYPE_ADMIN,
|
||||
content: this.$t('room.fatalErrorOccurred'),
|
||||
authorLevel: 60,
|
||||
}))
|
||||
|
||||
if (error.type === chatModels.FATAL_ERROR_TYPE_AUTH_CODE_ERROR) {
|
||||
// Read The Fucking Manual
|
||||
|
@ -114,7 +114,15 @@ class LiveClientManager:
|
||||
client_room_manager.del_room(room_key)
|
||||
|
||||
|
||||
class TooManyRetries(Exception):
|
||||
"""重试次数太多"""
|
||||
|
||||
|
||||
def _get_reconnect_interval(_retry_count: int, total_retry_count: int):
|
||||
# 防止无限重连的保险措施。30次重连大概会断线500秒,应该够了
|
||||
if total_retry_count > 30:
|
||||
raise TooManyRetries(f'total_retry_count={total_retry_count}')
|
||||
|
||||
# 不用retry_count了,防止意外的连接成功,导致retry_count重置
|
||||
interval = min(1 + (total_retry_count - 1) * 2, 20)
|
||||
# 加上随机延迟,防止同时请求导致雪崩
|
||||
@ -225,6 +233,14 @@ class OpenLiveClient(blivedm.OpenLiveClient):
|
||||
return False
|
||||
return True
|
||||
|
||||
def _on_send_game_heartbeat(self):
|
||||
# 加上随机延迟,减少同时请求的概率
|
||||
sleep_time = self._game_heartbeat_interval + random.uniform(-2, 1)
|
||||
self._game_heartbeat_timer_handle = asyncio.get_running_loop().call_later(
|
||||
sleep_time, self._on_send_game_heartbeat
|
||||
)
|
||||
asyncio.create_task(self._send_game_heartbeat())
|
||||
|
||||
async def _send_game_heartbeat(self):
|
||||
if self._game_id in (None, ''):
|
||||
logger.warning('game=%d _send_game_heartbeat() failed, game_id not found', self._game_id)
|
||||
@ -385,6 +401,14 @@ class ClientRoom:
|
||||
|
||||
class LiveMsgHandler(blivedm.BaseHandler):
|
||||
def on_client_stopped(self, client: LiveClientType, exception: Optional[Exception]):
|
||||
if isinstance(exception, TooManyRetries):
|
||||
room = client_room_manager.get_room(client.room_key)
|
||||
if room is not None:
|
||||
room.send_cmd_data(api.chat.Command.FATAL_ERROR, {
|
||||
'type': api.chat.FatalErrorType.TOO_MANY_RETRIES,
|
||||
'msg': 'The connection has lost too many times'
|
||||
})
|
||||
|
||||
_live_client_manager.del_live_client(client.room_key)
|
||||
|
||||
def _on_danmaku(self, client: WebLiveClient, message: dm_web_models.DanmakuMessage):
|
||||
|
Loading…
Reference in New Issue
Block a user