Leader/Follower 模式的典型应用

16 阅读1分钟

技术价值与优势

  1. 避免惊群效应 (Thundering Herd Problem): 如果没有这种 Leader/Follower 机制,高并发下的多个 401 响应会导致所有线程同时发起 Token 刷新请求,瞬间压垮服务器或造成客户端资源浪费。
  2. 结果一致性 (Consistency): 所有在同一时间窗口内的 Follower 都会获得与 Leader 完全相同的结果(Token),保证了并发环境下状态的一致性。
  3. 高效的同步屏障: 利用 FutureTask 作为同步屏障,既实现了阻塞等待,又自带状态管理(Running/Completed/Cancelled),无需手动编写复杂的 wait/notify 逻辑。
class SignAuthenticator : Authenticator {

    companion object {
        private const val TAG = "SignAuthenticator"
        private const val REFRESH_TIMEOUT_SECONDS = 30L
    }

    private val refreshingTask = AtomicReference<FutureTask<Boolean>?>(null)

    override fun authenticate(route: Route?, response: Response): Request? {
        val refreshSuccess = refreshToken()
        logger.i(TAG, "authenticate :: refreshSuccess = $refreshSuccess")

        // 忽略重签请求
        if (SignManager.isResignRequest(response.request())) {
            logger.i(TAG, "authenticate :: Ignoring resign request")
            return null
        }

        return if (refreshSuccess) {
            runCatching { response.close() }
            val (signedRequest, signSuccess) = SignManager.generateNewRequest(response.request(), true)
            if (signSuccess) {
                signedRequest
            } else {
                null
            }
        } else {
            null
        }
    }

    private fun refreshToken(): Boolean {
        // 创建新的任务
        val newTask = FutureTask { SignManager.refreshKeySync() }

        // 尝试成为 Leader
        if (refreshingTask.compareAndSet(null, newTask)) {
            try {
                newTask.run() // 在当前线程同步执行
                return newTask.get()
            } catch (_: Exception) {
                logger.e(TAG, "authenticate :: Refresh execution failed")
                return false
            } finally {
                // 任务结束,重置状态,允许下一次刷新
                refreshingTask.compareAndSet(newTask, null)
            }
        } else {
            // Follower:获取当前正在运行的任务
            val currentTask = refreshingTask.get() ?: return false

            logger.i(TAG, "authenticate :: Waiting for existing refresh (Follower)")
            return try {
                // 等待 Leader 的结果
                currentTask.get(REFRESH_TIMEOUT_SECONDS, TimeUnit.SECONDS)
            } catch (_: Exception) {
                logger.w(TAG, "authenticate :: Wait failed")
                false
            }
        }
    }
}