# MQ 推送事件处理:命令响应模式 + Channel 实践指南
## 📋 目录
1. [设计概述](#设计概述)
2. [核心概念](#核心概念)
3. [架构设计](#架构设计)
4. [实现方案](#实现方案)
5. [使用示例](#使用示例)
6. [最佳实践](#最佳实践)
7. [设计原则](#设计原则)
8. [常见问题](#常见问题)
9. [与其他模式对比](#与其他模式对比)
---
## 🎯 设计概述
### 为什么使用命令响应模式?
MQ 推送事件处理的特点:
- ✅ **外部触发**:MQTT/其他消息队列推送
- ✅ **需要操作结果**:处理成功/失败需要明确反馈
- ✅ **顺序保证**:同一记录的操作需要按顺序处理
- ✅ **可能并发**:多个推送消息可能同时到达
- ✅ **操作追踪**:需要知道每个操作的状态和结果
### 命令响应模式的优势
相比传统的观察者模式:
- ✅ **明确的请求-响应**:每个推送事件有明确的处理结果
- ✅ **顺序保证**:通过 Channel 队列保证操作顺序
- ✅ **操作追踪**:每个命令有唯一ID,可以追踪状态
- ✅ **错误处理**:完善的错误处理和反馈机制
- ✅ **易于扩展**:新增命令类型只需添加新的命令类
### Channel 的优势
- ✅ **队列语义**:天然支持命令队列
- ✅ **顺序保证**:单消费者模式保证顺序处理
- ✅ **背压处理**:支持缓冲区配置
- ✅ **协程集成**:与 Kotlin 协程完美集成
---
## 💡 核心概念
### 1. 命令模式(Command Pattern)
```
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
│ 推送事件 │ ──→ │ 命令封装 │ ──→ │ 命令队列 │
│ (MQTT) │ │ (Command) │ │ (Channel) │
└─────────────┘ └──────────────┘ └─────────────┘
↓
┌──────────────┐
│ 命令处理器 │
│ (Processor) │
└──────────────┘
↓
┌──────────────┐
│ 操作结果 │
│ (Result) │
└──────────────┘
```
### 2. 请求-响应模式(Request-Response Pattern)
```
Producer (推送事件)
↓
Command (命令封装)
↓
Channel (命令队列)
↓
Processor (命令处理)
↓
Result (操作结果)
↓
Response Channel (响应通道)
↓
Producer (收到响应)
```
### 3. Channel 队列模式
```kotlin
// ✅ 单消费者模式(保证顺序)
val commandChannel = Channel<Command>(Channel.UNLIMITED)
actorScope.launch {
for (command in commandChannel) {
// 串行处理,保证顺序
processCommand(command)
}
}
```
---
## 🏗️ 架构设计
### 整体架构
```
┌─────────────────────────────────────────────────────────┐
│ MQ 推送事件处理系统 │
├─────────────────────────────────────────────────────────┤
│ │
│ 1. 消息接收层: │
│ ├─ MQTT Handler │
│ ├─ Message Parser │
│ └─ Event Validator │
│ │
│ 2. 命令管理层: │
│ ├─ Command Factory(命令工厂) │
│ ├─ Command Channel(命令队列) │
│ └─ Command Router(命令路由) │
│ │
│ 3. 命令处理层: │
│ ├─ AddRecordHandler │
│ ├─ UpdateRecordHandler │
│ ├─ DeleteRecordHandler │
│ └─ GenericHandler │
│ │
│ 4. 结果反馈层: │
│ ├─ Result Channel(结果通道) │
│ ├─ Result Notifier(结果通知) │
│ └─ Error Handler(错误处理) │
│ │
└─────────────────────────────────────────────────────────┘
```
### 数据流向
```
1. MQTT 推送消息到达
↓
2. 解析推送事件(Event Parser)
↓
3. 创建命令对象(Command Factory)
↓
4. 发送到命令队列(Command Channel,按记录ID分组)
↓
5. 命令处理器串行处理(Command Processor)
↓
6. 返回操作结果(Result Channel)
↓
7. 结果通知(Result Notifier)
```
### 顺序保证机制
```
记录ID = "123"
├─ 命令1: AddRecord(id="123") → Channel[123] → 处理中...
├─ 命令2: UpdateRecord(id="123") → Channel[123] → 等待中...
└─ 命令3: DeleteRecord(id="123") → Channel[123] → 等待中...
✅ 每个记录ID有独立的 Channel,保证同一记录的操作按顺序处理
✅ 不同记录ID的操作可以并行处理
```
---
## 🔧 实现方案
### 方案1:完整命令响应模式实现
#### 1. 命令定义(Command)
```kotlin
/**
* ✅ 推送事件命令(密封类)
*/
sealed class PushEventCommand : Comparable<PushEventCommand> {
abstract val commandId: String
abstract val recordId: String
abstract val timestamp: Long
abstract val context: PushEventContext
abstract val responseChannel: Channel<CommandResult>
// ✅ 按时间戳排序(同记录按顺序)
override fun compareTo(other: PushEventCommand): Int {
if (recordId != other.recordId) {
return recordId.compareTo(other.recordId)
}
return timestamp.compareTo(other.timestamp)
}
}
/**
* ✅ 添加记录命令
*/
data class AddRecordCommand(
override val commandId: String,
override val recordId: String,
override val timestamp: Long,
override val context: PushEventContext,
override val responseChannel: Channel<CommandResult>,
val eventData: CloudSyncEventData
) : PushEventCommand()
/**
* ✅ 更新记录命令
*/
data class UpdateRecordCommand(
override val commandId: String,
override val recordId: String,
override val timestamp: Long,
override val context: PushEventContext,
override val responseChannel: Channel<CommandResult>,
val eventData: CloudSyncEventData
) : PushEventCommand()
/**
* ✅ 删除记录命令
*/
data class DeleteRecordCommand(
override val commandId: String,
override val recordId: String,
override val timestamp: Long,
override val context: PushEventContext,
override val responseChannel: Channel<CommandResult>,
val eventData: CloudSyncEventData
) : PushEventCommand()
```
#### 2. 命令上下文(Command Context)
```kotlin
/**
* ✅ 推送事件上下文
*/
data class PushEventContext(
val eventId: String, // 事件唯一ID
val source: String, // 事件来源(MQTT/HTTP等)
val timestamp: Long, // 事件时间戳
val messageId: String? = null, // MQTT 消息ID
val userId: String? = null, // 用户ID
val homeId: String? = null, // 家庭ID
val metadata: Map<String, Any> = emptyMap() // 扩展元数据
)
```
#### 3. 命令管理器(Command Manager)
```kotlin
/**
* ✅ 推送事件命令管理器
*
* 职责:
* 1. 接收推送事件
* 2. 封装为命令
* 3. 发送到命令队列(按记录ID分组)
* 4. 返回操作结果
*/
class PushEventCommandManager {
private const val TAG = "PushEventCommandManager"
private val actorScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
// ✅ 命令队列(按记录ID分组)
private val commandChannels = mutableMapOf<String, Channel<PushEventCommand>>()
private val channelMutex = Mutex()
// ✅ 命令处理器
private val commandProcessor = CommandProcessor()
// ✅ 命令工厂
private val commandFactory = CommandFactory()
init {
Logger.i(TAG, "PushEventCommandManager initialized")
}
/**
* ✅ 处理 MQTT 推送事件(主入口)
*/
suspend fun handlePushEvent(
mqttMessage: MqttMessageBean
): PushEventResult {
return try {
// 1. 解析事件
val eventData = CloudSyncEventParser.parseCloudSyncEvent(mqttMessage)
?: return PushEventResult.Failed(
message = "Failed to parse MQTT message",
recordId = "",
error = IllegalArgumentException("Invalid message format")
)
Logger.i(TAG, "handlePushEvent: eventType=${eventData.eventType}, recordId=${eventData.recordId}, operateType=${eventData.operateType}")
// 2. 创建命令
val command = commandFactory.createCommand(eventData)
// 3. 发送到队列并等待结果
val result = sendCommandAndWait(command)
// 4. 转换为 PushEventResult
when (result) {
is CommandResult.Success -> {
PushEventResult.Success(
message = result.message,
recordId = command.recordId,
skipped = result.skipped
)
}
is CommandResult.Failed -> {
PushEventResult.Failed(
message = result.message,
recordId = command.recordId,
error = result.error
)
}
is CommandResult.Rejected -> {
PushEventResult.Rejected(
reason = result.reason,
recordId = command.recordId
)
}
}
} catch (e: Exception) {
Logger.e(TAG, "handlePushEvent exception: ${e.message}")
PushEventResult.Failed(
message = "Unexpected error: ${e.message}",
recordId = "",
error = e
)
}
}
/**
* ✅ 发送命令到队列并等待结果
*/
private suspend fun sendCommandAndWait(
command: PushEventCommand
): CommandResult {
// ✅ 获取或创建命令队列(按记录ID分组)
val channel = getOrCreateChannel(command.recordId)
// ✅ 发送命令到队列
channel.send(command)
// ✅ 等待响应结果(带超时)
return withTimeoutOrNull(30000) { // 30秒超时
command.responseChannel.receive()
} ?: CommandResult.Failed(
error = TimeoutException("Command processing timeout"),
message = "Command processing timeout after 30 seconds"
)
}
/**
* ✅ 获取或创建命令队列(按记录ID分组,保证顺序)
*/
private suspend fun getOrCreateChannel(
recordId: String
): Channel<PushEventCommand> {
return channelMutex.withLock {
commandChannels.getOrPut(recordId) {
val channel = Channel<PushEventCommand>(Channel.UNLIMITED)
// ✅ 启动命令处理器(每个记录ID一个处理器,保证顺序)
actorScope.launch {
processCommandsForRecord(recordId, channel)
}
Logger.d(TAG, "Created command channel for recordId=$recordId")
channel
}
}
}
/**
* ✅ 处理指定记录的命令(串行处理,保证顺序)
*/
private suspend fun processCommandsForRecord(
recordId: String,
channel: Channel<PushEventCommand>
) {
Logger.d(TAG, "Started command processor for recordId=$recordId")
try {
for (command in channel) {
try {
// ✅ 处理命令
val result = commandProcessor.processCommand(command)
// ✅ 发送结果
command.responseChannel.send(result)
command.responseChannel.close()
Logger.d(TAG, "Command processed: commandId=${command.commandId}, result=${result::class.simpleName}")
} catch (e: Exception) {
Logger.e(TAG, "Error processing command: commandId=${command.commandId}, error=${e.message}")
// ✅ 发送错误结果
command.responseChannel.send(
CommandResult.Failed(
error = e,
message = e.message ?: "Unknown error"
)
)
command.responseChannel.close()
}
}
} catch (e: Exception) {
Logger.e(TAG, "Command processor error for recordId=$recordId: ${e.message}")
} finally {
// ✅ 清理资源
channelMutex.withLock {
commandChannels.remove(recordId)
}
Logger.d(TAG, "Command processor stopped for recordId=$recordId")
}
}
/**
* ✅ 清理资源(登出时调用)
*/
fun clear() {
channelMutex.withLock {
// 关闭所有 Channel
commandChannels.values.forEach { it.close() }
commandChannels.clear()
}
actorScope.cancel()
Logger.i(TAG, "PushEventCommandManager cleared")
}
}
```
#### 4. 命令工厂(Command Factory)
```kotlin
/**
* ✅ 命令工厂
*
* 职责:
* 1. 根据事件类型创建对应的命令
* 2. 设置命令上下文
* 3. 创建响应 Channel
*/
class CommandFactory {
private const val TAG = "CommandFactory"
/**
* ✅ 创建命令(根据事件数据)
*/
fun createCommand(
eventData: CloudSyncEventData
): PushEventCommand {
// ✅ 创建上下文
val context = PushEventContext(
eventId = UUID.randomUUID().toString(),
source = "MQTT",
timestamp = System.currentTimeMillis(),
messageId = eventData.key,
homeId = eventData.homeId,
metadata = mapOf(
"eventType" to eventData.eventType,
"bizType" to eventData.bizType,
"modifyTime" to eventData.modifyTime
)
)
// ✅ 创建响应 Channel
val responseChannel = Channel<CommandResult>(Channel.RENDEZVOUS)
// ✅ 根据操作类型创建命令
return when (eventData.operateType) {
RecordOperateDef.OPERATE_TYPE_ADD -> {
AddRecordCommand(
commandId = context.eventId,
recordId = eventData.recordId,
timestamp = context.timestamp,
context = context,
responseChannel = responseChannel,
eventData = eventData
)
}
RecordOperateDef.OPERATE_TYPE_UPDATE -> {
UpdateRecordCommand(
commandId = context.eventId,
recordId = eventData.recordId,
timestamp = context.timestamp,
context = context,
responseChannel = responseChannel,
eventData = eventData
)
}
RecordOperateDef.OPERATE_TYPE_DELETE -> {
DeleteRecordCommand(
commandId = context.eventId,
recordId = eventData.recordId,
timestamp = context.timestamp,
context = context,
responseChannel = responseChannel,
eventData = eventData
)
}
else -> {
Logger.w(TAG, "Unknown operateType=${eventData.operateType}, creating GenericCommand")
GenericCommand(
commandId = context.eventId,
recordId = eventData.recordId,
timestamp = context.timestamp,
context = context,
responseChannel = responseChannel,
eventData = eventData
)
}
}
}
}
```
#### 5. 命令处理器(Command Processor)
```kotlin
/**
* ✅ 命令处理器
*
* 职责:
* 1. 执行命令
* 2. 返回操作结果
* 3. 错误处理
*/
class CommandProcessor {
private const val TAG = "CommandProcessor"
private val dbManger = RecordDbManger.getInstance(MicroContext.getApplication())
private val recordSyncService = MicroContext.findServiceByInterface<RecordDataSyncService>(
RecordDataSyncService::class.java.name
)
/**
* ✅ 处理命令
*/
suspend fun processCommand(
command: PushEventCommand
): CommandResult {
Logger.i(TAG, "Processing command: commandId=${command.commandId}, recordId=${command.recordId}, type=${command::class.simpleName}")
return when (command) {
is AddRecordCommand -> handleAddCommand(command)
is UpdateRecordCommand -> handleUpdateCommand(command)
is DeleteRecordCommand -> handleDeleteCommand(command)
is GenericCommand -> handleGenericCommand(command)
}
}
/**
* ✅ 处理添加命令
*/
private suspend fun handleAddCommand(
command: AddRecordCommand
): CommandResult {
return try {
// 1. 检查本地是否存在
val localRecord = dbManger.getFileByRecordId(command.recordId)
if (localRecord != null) {
Logger.i(TAG, "handleAddCommand: record already exists, skipped: recordId=${command.recordId}")
return CommandResult.Success(
message = "Record already exists, skipped",
data = localRecord,
skipped = true
)
}
// 2. 从云端同步记录
Logger.d(TAG, "handleAddCommand: fetching record from cloud: recordId=${command.recordId}")
val cloudRecord = recordSyncService?.getCloudRecordById(command.recordId)
?: return CommandResult.Failed(
error = Exception("Record not found in cloud"),
message = "Record not found in cloud: recordId=${command.recordId}"
)
// 3. 添加到本地数据库
dbManger.insertRecord(cloudRecord)
Logger.i(TAG, "handleAddCommand: record added successfully: recordId=${command.recordId}")
CommandResult.Success(
message = "Record added successfully",
data = cloudRecord,
skipped = false
)
} catch (e: Exception) {
Logger.e(TAG, "handleAddCommand exception: recordId=${command.recordId}, error=${e.message}")
CommandResult.Failed(
error = e,
message = "Failed to add record: ${e.message}"
)
}
}
/**
* ✅ 处理更新命令
*/
private suspend fun handleUpdateCommand(
command: UpdateRecordCommand
): CommandResult {
return try {
// 1. 检查本地是否存在
val localRecord = dbManger.getFileByRecordId(command.recordId)
if (localRecord == null) {
Logger.i(TAG, "handleUpdateCommand: local record not found, skipped: recordId=${command.recordId}")
return CommandResult.Success(
message = "Local record not found, skipped",
data = null,
skipped = true
)
}
// 2. 时间戳比较(只处理更新的数据)
val modifyTime = command.eventData.modifyTime.toLongOrNull() ?: 0L
val localModifyTime = localRecord.updateAt ?: 0L
if (modifyTime <= localModifyTime) {
Logger.d(TAG, "handleUpdateCommand: older timestamp, skipped: recordId=${command.recordId}, local=$localModifyTime, new=$modifyTime")
return CommandResult.Success(
message = "Older timestamp, skipped",
data = localRecord,
skipped = true
)
}
// 3. 从云端同步最新记录
Logger.d(TAG, "handleUpdateCommand: fetching latest record from cloud: recordId=${command.recordId}")
val cloudRecord = recordSyncService?.getCloudRecordById(command.recordId)
?: return CommandResult.Failed(
error = Exception("Record not found in cloud"),
message = "Record not found in cloud: recordId=${command.recordId}"
)
// 4. 更新本地记录
dbManger.updateRecord(cloudRecord)
Logger.i(TAG, "handleUpdateCommand: record updated successfully: recordId=${command.recordId}")
CommandResult.Success(
message = "Record updated successfully",
data = cloudRecord,
skipped = false
)
} catch (e: Exception) {
Logger.e(TAG, "handleUpdateCommand exception: recordId=${command.recordId}, error=${e.message}")
CommandResult.Failed(
error = e,
message = "Failed to update record: ${e.message}"
)
}
}
/**
* ✅ 处理删除命令
*/
private suspend fun handleDeleteCommand(
command: DeleteRecordCommand
): CommandResult {
return try {
// 1. 检查本地是否存在
val localRecord = dbManger.getFileByRecordId(command.recordId)
if (localRecord == null) {
Logger.i(TAG, "handleDeleteCommand: local record not found, skipped: recordId=${command.recordId}")
return CommandResult.Success(
message = "Local record not found, skipped",
data = null,
skipped = true
)
}
// 2. 删除本地记录(包括相关文件)
RecordFileDeleteManager.deleteFilesAsync(command.recordId)
dbManger.deleteRecord(command.recordId)
Logger.i(TAG, "handleDeleteCommand: record deleted successfully: recordId=${command.recordId}")
CommandResult.Success(
message = "Record deleted successfully",
data = null,
skipped = false
)
} catch (e: Exception) {
Logger.e(TAG, "handleDeleteCommand exception: recordId=${command.recordId}, error=${e.message}")
CommandResult.Failed(
error = e,
message = "Failed to delete record: ${e.message}"
)
}
}
/**
* ✅ 处理通用命令(未知类型)
*/
private suspend fun handleGenericCommand(
command: GenericCommand
): CommandResult {
Logger.w(TAG, "handleGenericCommand: unknown command type, recordId=${command.recordId}")
return CommandResult.Rejected(
reason = "Unknown command type: ${command.eventData.operateType}"
)
}
}
```
#### 6. 操作结果(Command Result)
```kotlin
/**
* ✅ 命令结果(密封类)
*/
sealed class CommandResult {
/**
* ✅ 操作成功
*/
data class Success(
val message: String,
val data: Any? = null,
val skipped: Boolean = false, // 是否被跳过(已存在/不存在/时间戳更小)
val timestamp: Long = System.currentTimeMillis()
) : CommandResult()
/**
* ❌ 操作失败
*/
data class Failed(
val error: Throwable,
val message: String,
val timestamp: Long = System.currentTimeMillis()
) : CommandResult()
/**
* ⚠️ 操作被拒绝
*/
data class Rejected(
val reason: String,
val timestamp: Long = System.currentTimeMillis()
) : CommandResult()
}
/**
* ✅ 推送事件结果(封装 CommandResult,对外接口)
*/
sealed class PushEventResult {
data class Success(
val message: String,
val recordId: String,
val skipped: Boolean = false
) : PushEventResult()
data class Failed(
val message: String,
val recordId: String,
val error: Throwable? = null
) : PushEventResult()
data class Rejected(
val reason: String,
val recordId: String
) : PushEventResult()
}
```
#### 7. 完整集成示例
```kotlin
/**
* ✅ 云同步事件管理器(使用命令响应模式)
*/
class CloudSyncEventManager {
private val commandManager = PushEventCommandManager()
/**
* ✅ 处理 MQTT 消息(统一入口)
*/
fun handleMqttMessage(messageBean: MqttMessageBean) {
if (messageBean.protocol != MQTT_PROTOCOL_CLOUD_SYNC) {
Logger.w(TAG, "handleMqttMessage: unsupported protocol=${messageBean.protocol}")
return
}
// ✅ 发送到命令管理器处理
ioScope.launch {
val result = commandManager.handlePushEvent(messageBean)
// ✅ 根据结果处理
handlePushEventResult(result)
}
}
/**
* ✅ 处理推送事件结果
*/
private fun handlePushEventResult(result: PushEventResult) {
when (result) {
is PushEventResult.Success -> {
Logger.i(TAG, "Push event handled successfully: recordId=${result.recordId}, message=${result.message}, skipped=${result.skipped}")
// 通知监听器
if (!result.skipped) {
notifyRecordUpdated(result.recordId)
updateSyncTime(result.recordId)
}
}
is PushEventResult.Failed -> {
Logger.e(TAG, "Push event failed: recordId=${result.recordId}, message=${result.message}")
// 错误处理
handlePushEventError(result)
}
is PushEventResult.Rejected -> {
Logger.w(TAG, "Push event rejected: recordId=${result.recordId}, reason=${result.reason}")
// 被拒绝处理
}
}
}
/**
* ✅ 清理资源
*/
fun clear() {
commandManager.clear()
}
}
```
---
## 📝 使用示例
### 示例1:基本使用
```kotlin
/**
* ✅ 基本使用:处理 MQTT 推送事件
*/
class MqttPushHandler {
private val commandManager = PushEventCommandManager()
suspend fun handlePushMessage(message: MqttMessageBean) {
// ✅ 发送到命令管理器处理
val result = commandManager.handlePushEvent(message)
// ✅ 根据结果处理
when (result) {
is PushEventResult.Success -> {
if (!result.skipped) {
// 操作成功,通知 UI
notifyUI(result.recordId)
}
}
is PushEventResult.Failed -> {
// 操作失败,记录错误
logError(result)
}
is PushEventResult.Rejected -> {
// 操作被拒绝,记录原因
logRejection(result)
}
}
}
}
```
### 示例2:带回调的使用
```kotlin
/**
* ✅ 带回调的使用:处理完推送事件后执行回调
*/
class PushEventWithCallback {
private val commandManager = PushEventCommandManager()
suspend fun handlePushMessage(
message: MqttMessageBean,
callback: (PushEventResult) -> Unit
) {
val result = commandManager.handlePushEvent(message)
// ✅ 执行回调
callback(result)
// ✅ 根据结果处理
when (result) {
is PushEventResult.Success -> handleSuccess(result)
is PushEventResult.Failed -> handleFailed(result)
is PushEventResult.Rejected -> handleRejected(result)
}
}
}
```
### 示例3:批量处理
```kotlin
/**
* ✅ 批量处理:处理多个推送事件
*/
class BatchPushHandler {
private val commandManager = PushEventCommandManager()
suspend fun handleBatchMessages(
messages: List<MqttMessageBean>
): List<PushEventResult> {
// ✅ 并发处理多个消息
return messages.map { message ->
async {
commandManager.handlePushEvent(message)
}
}.awaitAll()
}
}
```
### 示例4:结果监听
```kotlin
/**
* ✅ 结果监听:监听推送事件处理结果
*/
class PushEventResultListener {
private val commandManager = PushEventCommandManager()
// ✅ 结果 Flow(用于监听所有处理结果)
private val resultFlow = MutableSharedFlow<PushEventResult>(
replay = 0,
extraBufferCapacity = 100
)
fun startListening() {
lifecycleScope.launch {
resultFlow
.filter { it is PushEventResult.Success && !it.skipped }
.collect { result ->
// ✅ 只处理成功的、未跳过的结果
handleSuccessfulResult(result as PushEventResult.Success)
}
}
}
}
```
---
## 🎯 最佳实践
### 1. 命令队列管理
```kotlin
// ✅ 推荐:按记录ID分组,保证顺序
private val commandChannels = mutableMapOf<String, Channel<PushEventCommand>>()
// ✅ 推荐:使用 UNLIMITED 容量(或根据实际需求设置)
val channel = Channel<PushEventCommand>(Channel.UNLIMITED)
// ✅ 推荐:每个记录ID一个处理器
actorScope.launch {
for (command in channel) {
processCommand(command) // 串行处理
}
}
```
### 2. 超时处理
```kotlin
// ✅ 推荐:设置超时时间
val result = withTimeoutOrNull(30000) { // 30秒超时
command.responseChannel.receive()
} ?: CommandResult.Failed(
error = TimeoutException("Command processing timeout"),
message = "Command processing timeout"
)
```
### 3. 错误处理
```kotlin
// ✅ 推荐:统一的错误处理
try {
val result = commandProcessor.processCommand(command)
command.responseChannel.send(result)
} catch (e: Exception) {
Logger.e(TAG, "Error processing command: ${e.message}")
command.responseChannel.send(
CommandResult.Failed(
error = e,
message = e.message ?: "Unknown error"
)
)
} finally {
command.responseChannel.close()
}
```
### 4. 资源清理
```kotlin
// ✅ 推荐:清理时关闭所有 Channel
fun clear() {
commandChannels.values.forEach { it.close() }
commandChannels.clear()
actorScope.cancel()
}
```
---
## 📐 设计原则
### 1. 单一职责原则
```kotlin
// ✅ CommandFactory:只负责创建命令
class CommandFactory {
fun createCommand(eventData: CloudSyncEventData): PushEventCommand
}
// ✅ CommandProcessor:只负责处理命令
class CommandProcessor {
suspend fun processCommand(command: PushEventCommand): CommandResult
}
// ✅ CommandManager:只负责命令队列管理
class PushEventCommandManager {
suspend fun handlePushEvent(message: MqttMessageBean): PushEventResult
}
```
### 2. 开闭原则
```kotlin
// ✅ 对扩展开放:新增命令类型只需添加新的命令类
sealed class PushEventCommand {
// 新增命令类型
data class RestoreRecordCommand(...) : PushEventCommand()
}
// ✅ 对修改封闭:CommandProcessor 使用 when 处理,不需要修改核心逻辑
```
### 3. 依赖倒置原则
```kotlin
// ✅ 依赖抽象接口,不依赖具体实现
interface RecordSyncService {
suspend fun getCloudRecordById(recordId: String): CloudRecord?
}
class CommandProcessor {
private val recordSyncService: RecordSyncService // ← 依赖接口
}
```
### 4. 接口隔离原则
```kotlin
// ✅ 每个接口职责单一
interface CommandFactory {
fun createCommand(eventData: CloudSyncEventData): PushEventCommand
}
interface CommandProcessor {
suspend fun processCommand(command: PushEventCommand): CommandResult
}
```
---
## ❓ 常见问题
### Q1: 如何处理大量并发推送?
**A:**
```kotlin
// ✅ 方案1:按记录ID分组,并行处理不同记录
private val commandChannels = mutableMapOf<String, Channel<PushEventCommand>>()
// ✅ 方案2:使用 SupervisorJob,单个命令失败不影响其他命令
private val actorScope = CoroutineScope(Dispatchers.IO + SupervisorJob())
```
### Q2: 如何处理命令处理超时?
**A:**
```kotlin
// ✅ 使用 withTimeoutOrNull
val result = withTimeoutOrNull(30000) {
command.responseChannel.receive()
} ?: CommandResult.Failed(
error = TimeoutException("Timeout"),
message = "Command processing timeout"
)
```
### Q3: 如何保证同一记录的操作顺序?
**A:**
```kotlin
// ✅ 按记录ID分组,每个记录ID一个 Channel,串行处理
private fun getOrCreateChannel(recordId: String): Channel<PushEventCommand> {
return commandChannels.getOrPut(recordId) {
val channel = Channel<PushEventCommand>(Channel.UNLIMITED)
// ✅ 启动串行处理器
actorScope.launch {
for (command in channel) {
processCommand(command) // 串行处理
}
}
channel
}
}
```
### Q4: 如何处理命令处理失败?
**A:**
```kotlin
// ✅ 统一的错误处理机制
try {
val result = commandProcessor.processCommand(command)
command.responseChannel.send(result)
} catch (e: Exception) {
// ✅ 发送错误结果
command.responseChannel.send(
CommandResult.Failed(
error = e,
message = e.message ?: "Unknown error"
)
)
} finally {
command.responseChannel.close()
}
```
### Q5: 如何扩展新的命令类型?
**A:**
```kotlin
// ✅ 1. 添加新的命令类
data class RestoreRecordCommand(...) : PushEventCommand()
// ✅ 2. 在 CommandFactory 中添加创建逻辑
when (eventData.operateType) {
RecordOperateDef.OPERATE_TYPE_RESTORE -> {
RestoreRecordCommand(...)
}
}
// ✅ 3. 在 CommandProcessor 中添加处理逻辑
when (command) {
is RestoreRecordCommand -> handleRestoreCommand(command)
}
```
---
## 🔄 与其他模式对比
### 命令响应模式 vs 观察者模式
| 特性 | 命令响应模式 | 观察者模式 |
|------|------------|-----------|
| **操作结果** | ✅ 明确的请求-响应 | ❌ 被动接收状态变化 |
| **顺序保证** | ✅ Channel 队列保证 | ❌ 无法保证顺序 |
| **操作追踪** | ✅ 每个命令有ID | ❌ 无法追踪操作 |
| **适用场景** | ✅ 需要操作结果的场景 | ✅ 只需要状态通知的场景 |
### 命令响应模式 vs Actor 模式
| 特性 | 命令响应模式 | Actor 模式 |
|------|------------|-----------|
| **实现方式** | 命令 + Channel + 处理器 | Channel + 单线程处理 |
| **结果反馈** | ✅ 明确的响应 Channel | ⚠️ 需要额外设计 |
| **命令封装** | ✅ 密封类命令 | ⚠️ 简单的数据结构 |
| **复杂度** | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| **推荐度** | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
---
## 📚 总结
### 核心要点
1. **命令模式**:每个推送事件封装为一个命令对象
2. **请求-响应**:每个命令有明确的处理结果
3. **Channel 队列**:保证命令按顺序处理
4. **分组处理**:按记录ID分组,保证同一记录的操作顺序
5. **错误处理**:完善的错误处理和反馈机制
### 设计优势
- ✅ **明确的请求-响应**:每个推送事件有明确的处理结果
- ✅ **顺序保证**:通过 Channel 队列保证操作顺序
- ✅ **操作追踪**:每个命令有唯一ID,可以追踪状态
- ✅ **易于扩展**:新增命令类型只需添加新的命令类
- ✅ **错误处理完善**:统一的错误处理和反馈机制
### 适用场景
- ✅ **MQ 推送事件处理**:MQTT、Kafka 等消息队列推送
- ✅ **需要操作结果**:更新/删除操作需要明确的成功/失败结果
- ✅ **需要顺序保证**:同一记录的操作需要按顺序处理
- ✅ **需要操作追踪**:需要追踪每个操作的状态和结果
### 不适用场景
- ❌ **只需要状态通知**:如果只需要通知状态变化,使用观察者模式更合适
- ❌ **不需要操作结果**:如果不需要知道操作是否成功,使用观察者模式更简单
---
## 🔄 后续探索方向
1. **命令优先级**:如何实现命令的优先级处理
2. **命令重试机制**:如何处理失败命令的重试
3. **命令持久化**:如何持久化命令,保证可靠性
4. **命令监控**:如何监控命令处理的性能指标
5. **分布式场景**:如何在分布式环境中使用命令模式
---
**文档版本**: v1.0
**最后更新**: 2024-01-XX
**作者**: AI Assistant
# MQ 推送事件处理:命令响应模式 + Channel 实践
最新推荐文章于 2025-12-18 21:03:42 发布
部署运行你感兴趣的模型镜像
您可能感兴趣的与本文相关的镜像
Llama Factory
模型微调
LLama-Factory
LLaMA Factory 是一个简单易用且高效的大型语言模型(Large Language Model)训练与微调平台。通过 LLaMA Factory,可以在无需编写任何代码的前提下,在本地完成上百种预训练模型的微调
254

被折叠的 条评论
为什么被折叠?



