Extract network logic to separate classes to speed up inspections

This commit is contained in:
Him188 2021-01-05 18:16:20 +08:00
parent 7d43ab20f8
commit 44053ae85f

View File

@ -1,5 +1,5 @@
/* /*
* Copyright 2019-2020 Mamoe Technologies and contributors. * Copyright 2019-2021 Mamoe Technologies and contributors.
* *
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证. * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link. * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
@ -75,7 +75,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
internal var _isConnecting: Boolean = false internal var _isConnecting: Boolean = false
override val isOnline: Boolean get() = _network.areYouOk() override val isOnline: Boolean get() = _network.areYouOk()
override val eventChannel: EventChannel<BotEvent> = final override val eventChannel: EventChannel<BotEvent> =
GlobalEventChannel.filterIsInstance<BotEvent>().filter { it.bot === this@AbstractBot } GlobalEventChannel.filterIsInstance<BotEvent>().filter { it.bot === this@AbstractBot }
val otherClientsLock = Mutex() // lock sync val otherClientsLock = Mutex() // lock sync
@ -91,10 +91,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
@OptIn(ExperimentalTime::class) @OptIn(ExperimentalTime::class)
@Suppress("unused") @Suppress("unused")
private val offlineListener: Listener<BotOfflineEvent> = private val offlineListener: Listener<BotOfflineEvent> =
this@AbstractBot.subscribeAlways(concurrency = Listener.ConcurrencyKind.LOCKED) { event -> this@AbstractBot.eventChannel.subscribeAlways(concurrency = Listener.ConcurrencyKind.LOCKED) { event ->
if (event.bot != this@AbstractBot) {
return@subscribeAlways
}
if (!event.bot.isActive) { if (!event.bot.isActive) {
// bot closed // bot closed
return@subscribeAlways return@subscribeAlways
@ -130,9 +127,33 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
} else serverList.removeAt(0) } else serverList.removeAt(0)
} }
var failed = false val success: Boolean
val time = measureTime { val time = measureTime { success = Reconnect().reconnect(event) }
tailrec suspend fun reconnect() {
if (success) {
logger.info { "Reconnected successfully in ${time.toHumanReadableString()}" }
}
}
is BotOfflineEvent.Active -> {
val cause = event.cause
val msg = if (cause == null) {
""
} else {
" with exception: $cause"
}
bot.logger.info("Bot is closed manually: $msg", cause)
bot.cancel(CancellationException("Bot is closed manually: $msg", cause))
}
is BotOfflineEvent.Force -> {
bot.logger.info { "Connection occupied by another android device: ${event.message}" }
bot.cancel(ForceOfflineException("Connection occupied by another android device: ${event.message}"))
}
}
}
private inner class Reconnect {
suspend fun reconnect(event: BotOfflineEvent): Boolean {
while (true) {
retryCatching<Unit>( retryCatching<Unit>(
configuration.reconnectionRetryTimes, configuration.reconnectionRetryTimes,
except = LoginFailedException::class except = LoginFailedException::class
@ -148,62 +169,33 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
relogin((event as? BotOfflineEvent.Dropped)?.cause) relogin((event as? BotOfflineEvent.Dropped)?.cause)
} }
launch { launch {
BotReloginEvent( BotReloginEvent(bot, (event as? BotOfflineEvent.CauseAware)?.cause).broadcast()
bot,
(event as? BotOfflineEvent.CauseAware)?.cause
).broadcast()
} }
return return true
}.getOrElse { }.getOrElse { exception ->
if (it is LoginFailedException && !it.killBot) { if (exception is LoginFailedException && !exception.killBot) {
logger.info { "Cannot reconnect." } logger.info { "Cannot reconnect." }
logger.warning(it) logger.warning(exception)
logger.info { "Retrying in 3s..." } logger.info { "Retrying in 3s..." }
delay(3000) delay(3000)
return@getOrElse return@getOrElse
} }
logger.info { "Cannot reconnect due to fatal error." } logger.info { "Cannot reconnect due to fatal error." }
bot.cancel(CancellationException("Cannot reconnect due to fatal error.", it)) bot.cancel(CancellationException("Cannot reconnect due to fatal error.", exception))
failed = true return false
return }
}
reconnect()
}
reconnect()
}
if (!failed) {
logger.info { "Reconnected successfully in ${time.toHumanReadableString()}" }
}
}
is BotOfflineEvent.Active -> {
val cause = event.cause
val msg = if (cause == null) {
""
} else {
" with exception: " + cause.message
}
bot.logger.info { "Bot is closed manually: $msg" }
bot.cancel(CancellationException(event.toString()))
}
is BotOfflineEvent.Force -> {
bot.logger.info { "Connection occupied by another android device: ${event.message}" }
bot.cancel(ForceOfflineException(event.toString()))
} }
} }
} }
/** /**
* **Exposed public API** * 仅用在 [login]
* [AbstractBot.relogin] && [BotNetworkHandler.init]
*/ */
final override suspend fun login() { private inner class Login {
@ThisApiMustBeUsedInWithConnectionLockBlock
suspend fun reinitializeNetworkHandler(cause: Throwable?) { private suspend fun doRelogin() {
suspend fun doRelogin() {
while (true) { while (true) {
_network = createNetworkHandler(this.coroutineContext) _network = createNetworkHandler(coroutineContext)
try { try {
_isConnecting = true _isConnecting = true
@OptIn(ThisApiMustBeUsedInWithConnectionLockBlock::class) @OptIn(ThisApiMustBeUsedInWithConnectionLockBlock::class)
@ -211,9 +203,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
return return
} catch (e: Exception) { } catch (e: Exception) {
if (e is LoginFailedException) { if (e is LoginFailedException) {
if (e.killBot) { if (e.killBot) throw e
throw e
}
} else { } else {
network.logger.error(e) network.logger.error(e)
} }
@ -228,7 +218,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
} }
} }
suspend fun doInit() { private suspend fun doInit() {
retryCatching(5) { count, lastException -> retryCatching(5) { count, lastException ->
if (count != 0) { if (count != 0) {
if (!isActive) { if (!isActive) {
@ -246,12 +236,15 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
} }
} }
@ThisApiMustBeUsedInWithConnectionLockBlock
private suspend fun reinitializeNetworkHandler(cause: Throwable?) {
// logger.info("Initializing BotNetworkHandler") // logger.info("Initializing BotNetworkHandler")
if (::_network.isInitialized) { if (::_network.isInitialized) {
_network.cancel(CancellationException("manual re-login", cause = cause)) _network.cancel(CancellationException("manual re-login", cause = cause))
BotReloginEvent(this, cause).broadcast() BotReloginEvent(this@AbstractBot, cause).broadcast()
doRelogin() doRelogin()
return return
} }
@ -260,6 +253,7 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
doInit() doInit()
} }
suspend fun doLogin() {
logger.info { "Logging in..." } logger.info { "Logging in..." }
if (::_network.isInitialized) { if (::_network.isInitialized) {
network.withConnectionLock { network.withConnectionLock {
@ -272,6 +266,16 @@ internal abstract class AbstractBot<N : BotNetworkHandler> constructor(
} }
logger.info { "Login successful" } logger.info { "Login successful" }
} }
}
/**
* **Exposed public API**
* [AbstractBot.relogin] && [BotNetworkHandler.init]
*/
final override suspend fun login() {
Login().doLogin()
}
protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N protected abstract fun createNetworkHandler(coroutineContext: CoroutineContext): N