添加防止无限重连的保险措施、开放平台心跳添加随机延迟

pull/157/head
John Smith 9 months ago
parent 5441cb7cd5
commit 1772caebe3

@ -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…
Cancel
Save