Appearance
STOMP 消息发送:让服务端主动与客户端对话 🚀
什么是 STOMP 消息发送?
在传统的 HTTP 请求-响应模式中,只有客户端可以主动发起请求,服务端只能被动响应。但在实时应用中,我们经常需要服务端主动向客户端推送消息,比如:
- 📢 系统通知推送
- 💬 聊天消息广播
- 📊 实时数据更新
- ⚠️ 告警信息推送
STOMP(Simple Text Oriented Messaging Protocol)消息发送就是解决这个问题的关键技术!
IMPORTANT
STOMP 消息发送的核心价值:让服务端能够在任何时候、从应用的任何地方主动向已连接的客户端发送消息,实现真正的双向实时通信。
核心组件:SimpMessagingTemplate
SimpMessagingTemplate
是 Spring WebSocket 提供的消息发送模板,它就像一个"消息快递员",负责将我们的消息准确投递到指定的目的地。
设计哲学
Spring 框架的设计者们遵循了一个重要原则:简化复杂操作。他们将复杂的 WebSocket 消息发送逻辑封装在 SimpMessagingTemplate
中,让开发者只需要关注业务逻辑,而不用担心底层的消息传输细节。
实战应用:Kotlin + SpringBoot 示例
基础消息发送
kotlin
@Controller
class GreetingController {
// 需要手动处理 WebSocket 连接和消息格式
@Autowired
private lateinit var webSocketHandler: WebSocketHandler
@PostMapping("/greetings")
fun greet(@RequestBody greeting: String) {
// 复杂的消息构建和发送逻辑
val sessions = webSocketHandler.getSessions()
val message = createWebSocketMessage(greeting)
sessions.forEach { session ->
try {
session.sendMessage(message)
} catch (e: Exception) {
// 需要手动处理异常
}
}
}
}
kotlin
@Controller
class GreetingController(
private val messagingTemplate: SimpMessagingTemplate
) {
@PostMapping("/greetings")
fun greet(@RequestBody greeting: String) {
val timestamp = LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_TIME)
val message = "[$timestamp]: $greeting"
// 一行代码搞定消息发送!
messagingTemplate.convertAndSend("/topic/greetings", message)
}
}
TIP
使用 SimpMessagingTemplate
后,复杂的 WebSocket 消息发送变成了一行代码!这就是 Spring 框架"约定优于配置"哲学的体现。
高级应用场景
1. 用户特定消息推送
kotlin
@Service
class NotificationService(
private val messagingTemplate: SimpMessagingTemplate
) {
/**
* 向特定用户发送私人消息
* @param userId 用户ID
* @param notification 通知内容
*/
fun sendPersonalNotification(userId: String, notification: String) {
// 发送到用户专属队列
messagingTemplate.convertAndSendToUser(
userId,
"/queue/notifications",
notification
)
}
/**
* 广播系统公告
* @param announcement 公告内容
*/
fun broadcastAnnouncement(announcement: String) {
val message = mapOf(
"type" to "SYSTEM_ANNOUNCEMENT",
"content" to announcement,
"timestamp" to System.currentTimeMillis()
)
// 广播给所有订阅者
messagingTemplate.convertAndSend("/topic/announcements", message)
}
}
2. 实时数据监控推送
kotlin
@Component
class SystemMonitorService(
private val messagingTemplate: SimpMessagingTemplate
) {
/**
* 定时推送系统状态
*/
@Scheduled(fixedRate = 5000) // 每5秒执行一次
fun pushSystemStatus() {
val systemStatus = SystemStatus(
cpuUsage = getCpuUsage(),
memoryUsage = getMemoryUsage(),
activeConnections = getActiveConnections(),
timestamp = LocalDateTime.now()
)
// 推送系统状态到监控面板
messagingTemplate.convertAndSend("/topic/system-status", systemStatus)
}
/**
* 异常告警推送
*/
fun sendAlert(alertLevel: AlertLevel, message: String) {
val alert = Alert(
level = alertLevel,
message = message,
timestamp = LocalDateTime.now()
)
when (alertLevel) {
AlertLevel.CRITICAL -> {
// 紧急告警发送给管理员
messagingTemplate.convertAndSend("/topic/alerts/critical", alert)
}
AlertLevel.WARNING -> {
// 警告信息发送给运维团队
messagingTemplate.convertAndSend("/topic/alerts/warning", alert)
}
else -> {
// 一般信息广播
messagingTemplate.convertAndSend("/topic/alerts/info", alert)
}
}
}
}
data class SystemStatus(
val cpuUsage: Double,
val memoryUsage: Double,
val activeConnections: Int,
val timestamp: LocalDateTime
)
data class Alert(
val level: AlertLevel,
val message: String,
val timestamp: LocalDateTime
)
enum class AlertLevel {
INFO, WARNING, CRITICAL
}
依赖注入的两种方式
方式一:按类型注入(推荐)
kotlin
@Controller
class MessageController(
private val messagingTemplate: SimpMessagingTemplate
) {
// Spring 会自动注入 SimpMessagingTemplate 实例
}
方式二:按名称注入
kotlin
@Controller
class MessageController {
@Autowired
@Qualifier("brokerMessagingTemplate")
private lateinit var messagingTemplate: SimpMessagingTemplate
// 当存在多个相同类型的 Bean 时使用
}
NOTE
当应用中只有一个 SimpMessagingTemplate
Bean 时,推荐使用构造函数注入(方式一)。当存在多个同类型 Bean 时,使用 @Qualifier
注解指定具体的 Bean 名称。
实际业务场景应用
聊天应用示例
完整的聊天室实现示例
kotlin
@RestController
@RequestMapping("/api/chat")
class ChatController(
private val messagingTemplate: SimpMessagingTemplate,
private val chatService: ChatService
) {
/**
* 发送聊天消息
*/
@PostMapping("/send")
fun sendMessage(@RequestBody chatMessage: ChatMessageRequest): ResponseEntity<String> {
try {
// 保存消息到数据库
val savedMessage = chatService.saveMessage(chatMessage)
// 构建广播消息
val broadcastMessage = ChatMessage(
id = savedMessage.id,
username = chatMessage.username,
content = chatMessage.content,
timestamp = savedMessage.createdAt,
type = MessageType.CHAT
)
// 广播给所有在线用户
messagingTemplate.convertAndSend("/topic/chat/room/${chatMessage.roomId}", broadcastMessage)
return ResponseEntity.ok("消息发送成功")
} catch (e: Exception) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("消息发送失败: ${e.message}")
}
}
/**
* 用户加入聊天室
*/
@PostMapping("/join")
fun joinRoom(@RequestBody joinRequest: JoinRoomRequest): ResponseEntity<String> {
// 广播用户加入消息
val joinMessage = ChatMessage(
username = "系统",
content = "${joinRequest.username} 加入了聊天室",
timestamp = LocalDateTime.now(),
type = MessageType.SYSTEM
)
messagingTemplate.convertAndSend("/topic/chat/room/${joinRequest.roomId}", joinMessage)
return ResponseEntity.ok("加入聊天室成功")
}
}
data class ChatMessage(
val id: Long? = null,
val username: String,
val content: String,
val timestamp: LocalDateTime,
val type: MessageType
)
data class ChatMessageRequest(
val roomId: String,
val username: String,
val content: String
)
data class JoinRoomRequest(
val roomId: String,
val username: String
)
enum class MessageType {
CHAT, SYSTEM, NOTIFICATION
}
关键优势总结
🎯 简化开发
- 一行代码发送消息:
convertAndSend()
方法封装了所有复杂逻辑 - 自动类型转换:支持对象自动序列化为 JSON
- 异常处理:内置错误处理机制
🔄 灵活的消息路由
- 主题广播:
/topic/*
一对多消息推送 - 队列私发:
/queue/*
一对一消息推送 - 用户专属:
convertAndSendToUser()
用户特定消息
⚡ 高性能
- 异步处理:消息发送不阻塞主线程
- 连接复用:复用已建立的 WebSocket 连接
- 批量发送:支持批量消息处理
IMPORTANT
SimpMessagingTemplate
让服务端消息推送变得像发送 HTTP 请求一样简单,这正是 Spring 框架"让复杂的事情变简单"设计理念的完美体现!
最佳实践建议
开发建议
- 消息格式统一:定义标准的消息格式,包含类型、内容、时间戳等字段
- 异常处理:在消息发送周围添加适当的异常处理逻辑
- 性能监控:监控消息发送频率,避免消息风暴
- 安全考虑:验证消息发送者权限,防止恶意消息推送
通过 SimpMessagingTemplate
,我们可以轻松实现服务端主动消息推送,让应用具备真正的实时交互能力! ✨