201 lines
5.9 KiB
Markdown
201 lines
5.9 KiB
Markdown
# 重构总结 - 回归简单可靠的架构
|
||
|
||
## 修复的问题
|
||
|
||
**用户反馈**: "让处理异常,你个狗日的把逻辑,流程都改错了。"
|
||
|
||
**根本原因**: 在添加异常处理和 gRPC 可靠性改进时,引入了 StreamManager 抽象层,导致:
|
||
1. RegisterParty 失败但代码继续执行
|
||
2. StreamManager 日志完全缺失
|
||
3. 流程变复杂,引入新问题
|
||
|
||
## 本次重构的原则
|
||
|
||
**保留的好东西**(来自 gRPC 官方推荐)✅:
|
||
1. gRPC Keep-Alive 配置(20s PING, 5s timeout, 永不 idle)
|
||
2. Android 网络状态监听(resetConnectBackoff)
|
||
3. registerParty 错误检查和重试
|
||
4. markPartyReady 重试机制
|
||
|
||
**删除的坏东西**(过度设计)❌:
|
||
1. StreamManager.kt 整个文件
|
||
2. 复杂的 init 块监听 reconnection 事件
|
||
3. 回调式的流管理
|
||
|
||
**恢复的简单逻辑**(工作的代码)✅:
|
||
1. 直接用 jobManager.launch + grpcClient.subscribeSessionEvents().collect
|
||
2. 在 collect 外包一层 flow { }.retryWhen { } 实现自动重连
|
||
3. 保持原有的事件处理逻辑不变
|
||
|
||
## 代码变更详情
|
||
|
||
### 1. TssRepository.kt
|
||
|
||
#### 删除 StreamManager 相关代码:
|
||
```kotlin
|
||
// 删除了
|
||
- import com.durian.tssparty.data.remote.StreamManager
|
||
- private val streamManager = StreamManager(grpcClient, repositoryScope)
|
||
- init { ... streamManager.restartAllStreams() }
|
||
```
|
||
|
||
#### 恢复简单的事件订阅:
|
||
```kotlin
|
||
// 之前(复杂)
|
||
streamManager.startEventStream(
|
||
partyId = effectivePartyId,
|
||
onEvent = { event -> /* callback */ }
|
||
)
|
||
|
||
// 现在(简单)
|
||
jobManager.launch(JOB_SESSION_EVENT) {
|
||
flow {
|
||
grpcClient.subscribeSessionEvents(effectivePartyId).collect { emit(it) }
|
||
}
|
||
.retryWhen { cause, attempt ->
|
||
Log.w(TAG, "Event stream failed (attempt ${attempt + 1}), retrying...")
|
||
delay(min(attempt + 1, 30) * 1000L)
|
||
true // 永远重试
|
||
}
|
||
.collect { event ->
|
||
// 直接处理事件(保持原有逻辑)
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 恢复简单的消息路由:
|
||
```kotlin
|
||
// Part 1: 发送消息(重命名为 JOB_MESSAGE_SENDING)
|
||
jobManager.launch(JOB_MESSAGE_SENDING) {
|
||
tssNativeBridge.outgoingMessages.collect { message ->
|
||
grpcClient.routeMessage(...)
|
||
}
|
||
}
|
||
|
||
// Part 2: 接收消息(使用 JOB_MESSAGE_COLLECTION + retryWhen)
|
||
jobManager.launch(JOB_MESSAGE_COLLECTION) {
|
||
flow {
|
||
grpcClient.subscribeMessages(sessionId, effectivePartyId).collect { emit(it) }
|
||
}
|
||
.retryWhen { cause, attempt ->
|
||
Log.w(TAG, "Message stream failed (attempt ${attempt + 1}), retrying...")
|
||
delay(min(attempt + 1, 30) * 1000L)
|
||
true // 永远重试
|
||
}
|
||
.collect { message ->
|
||
// 处理消息
|
||
}
|
||
}
|
||
```
|
||
|
||
#### 修复 ensureSessionEventSubscriptionActive:
|
||
```kotlin
|
||
// 之前
|
||
val isActive = streamManager.isEventStreamActive()
|
||
|
||
// 现在
|
||
val isActive = jobManager.isActive(JOB_SESSION_EVENT)
|
||
```
|
||
|
||
### 2. 删除的文件
|
||
|
||
- `app/src/main/java/com/durian/tssparty/data/remote/StreamManager.kt`
|
||
|
||
### 3. 保留的 gRPC 改进
|
||
|
||
#### GrpcClient.kt - Keep-Alive 配置(保留)✅:
|
||
```kotlin
|
||
val builder = ManagedChannelBuilder
|
||
.forAddress(host, port)
|
||
.usePlaintext()
|
||
.keepAliveTime(20, TimeUnit.SECONDS) // 每 20 秒 PING
|
||
.keepAliveTimeout(5, TimeUnit.SECONDS) // 5 秒超时
|
||
.keepAliveWithoutCalls(true) // 没有活跃 RPC 也 PING
|
||
.idleTimeout(Long.MAX_VALUE, TimeUnit.DAYS) // 永不超时
|
||
```
|
||
|
||
#### GrpcClient.kt - 网络监听(保留)✅:
|
||
```kotlin
|
||
fun setupNetworkMonitoring(context: Context) {
|
||
val callback = object : ConnectivityManager.NetworkCallback() {
|
||
override fun onAvailable(network: Network) {
|
||
channel?.resetConnectBackoff() // 立即重连
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
## 架构对比
|
||
|
||
### 旧设计(复杂但出错)❌:
|
||
```
|
||
TssRepository
|
||
├─ StreamManager (新增的抽象层)
|
||
│ ├─ startEventStream()
|
||
│ ├─ startMessageStream()
|
||
│ └─ restartAllStreams()
|
||
│
|
||
├─ init { listen reconnection → streamManager.restartAllStreams() }
|
||
└─ grpcClient
|
||
```
|
||
|
||
### 新设计(简单可靠)✅:
|
||
```
|
||
TssRepository
|
||
├─ JobManager (原有的任务管理)
|
||
│ ├─ JOB_SESSION_EVENT → flow { subscribeSessionEvents() }.retryWhen { }
|
||
│ ├─ JOB_MESSAGE_COLLECTION → flow { subscribeMessages() }.retryWhen { }
|
||
│ └─ JOB_MESSAGE_SENDING → outgoingMessages.collect { }
|
||
│
|
||
└─ grpcClient (带 Keep-Alive + Network Monitoring)
|
||
```
|
||
|
||
## 为什么新设计更好
|
||
|
||
1. **更少的抽象层**: 直接用 jobManager.launch,不需要 StreamManager
|
||
2. **自动重连**: Flow.retryWhen 在流失败时自动重新发起 RPC
|
||
3. **保持原有逻辑**: 事件处理代码保持不变,只在外面包一层 retryWhen
|
||
4. **更好的日志**: 直接在 collect { } 里打日志,不会丢失
|
||
5. **符合 Kotlin 风格**: Flow transformation 比 callback 更符合 Kotlin 惯用法
|
||
|
||
## 测试重点
|
||
|
||
1. ✅ 编译成功(已验证)
|
||
2. ⏳ RegisterParty 成功(需要测试)
|
||
3. ⏳ 事件订阅成功(看到 "Starting session event subscription" 日志)
|
||
4. ⏳ 创建 2-of-3 会话成功
|
||
5. ⏳ 飞行模式测试自动重连
|
||
|
||
## 编译结果
|
||
|
||
```
|
||
BUILD SUCCESSFUL in 1m 26s
|
||
46 actionable tasks: 17 executed, 29 up-to-date
|
||
```
|
||
|
||
只有一些参数未使用的警告,没有错误。
|
||
|
||
## 核心教训
|
||
|
||
**简单就是可靠**:
|
||
```
|
||
工作的代码 + 官方推荐配置 + 最小改动 = 可靠的系统
|
||
|
||
不是:
|
||
工作的代码 → 完全重构 → 引入新抽象 → 新问题
|
||
```
|
||
|
||
**gRPC 流的正确管理方式**:
|
||
1. 流断开时,用 Flow.retryWhen 自动重新发起 RPC(不是"恢复")
|
||
2. 不需要复杂的 StreamManager,Kotlin Flow 本身就是流管理器
|
||
3. Keep-Alive 防止连接假死
|
||
4. Network Monitoring 加速重连
|
||
|
||
## 下一步
|
||
|
||
准备测试!使用 build-install-debug.bat 安装到设备,验证:
|
||
1. RegisterParty 是否成功
|
||
2. 事件流是否正常工作
|
||
3. 2-of-3 创建是否成功
|
||
4. 网络断开重连是否自动恢复
|