Spring Boot电商项目实时通知实现方案咨询(含WebSocket及替代方案)
基于Spring Boot的电商下单通知实现方案
一、WebSocket 实现步骤
1. 引入依赖
在pom.xml中添加Spring WebSocket依赖:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
2. 配置WebSocket
创建配置类注册端点和消息代理:
@Configuration @EnableWebSocketMessageBroker public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void configureMessageBroker(MessageBrokerRegistry config) { config.enableSimpleBroker("/topic", "/queue"); config.setApplicationDestinationPrefixes("/app"); } @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/ws-notify") .setAllowedOriginPatterns("*") .withSockJS(); } }
3. 定义消息实体
创建区分买家/商家通知的消息类:
@Data public class OrderNotifyMessage { private String userId; private String type; // ORDER_SUCCESS / MERCHANT_DELIVER private OrderContent content; @Data public static class OrderContent { private String orderId; private List<OrderItem> items; private BigDecimal totalAmount; } @Data public static class OrderItem { private String productId; private String merchantId; private Integer quantity; private BigDecimal price; } }
4. 客户端连接逻辑
- 买家端:登录后连接
/ws-notify,订阅/queue/order-success-{buyerId}队列接收一对一通知。 - 商家端:登录后订阅
/queue/merchant-deliver-{merchantId}队列,仅接收自身订单通知。
前端JS示例:
const stompClient = new StompJs.Client({ brokerURL: 'ws://localhost:8080/ws-notify', connectHeaders: { 'userId': 'buyer_123' } }); stompClient.onConnect = (frame) => { stompClient.subscribe(`/queue/order-success-buyer_123`, (message) => { const notify = JSON.parse(message.body); console.log('下单成功:', notify); // 前端展示逻辑:弹窗、消息中心等 }); }; stompClient.activate();
5. 下单时发送通知
在订单服务中完成订单保存后,通过SimpMessagingTemplate推送消息:
@Service public class OrderService { @Autowired private SimpMessagingTemplate messagingTemplate; public void createOrder(OrderCreateRequest request) { // 1. 保存订单到数据库 Order order = saveOrderToDB(request); String buyerId = request.getBuyerId(); Set<String> merchantIds = order.getItems().stream() .map(OrderItem::getMerchantId) .collect(Collectors.toSet()); // 2. 发送买家下单成功通知 OrderNotifyMessage buyerNotify = buildBuyerNotify(order); messagingTemplate.convertAndSendToUser(buyerId, "/queue/order-success", buyerNotify); // 3. 批量发送商家待配送通知 for (String merchantId : merchantIds) { OrderNotifyMessage merchantNotify = buildMerchantNotify(order, merchantId); messagingTemplate.convertAndSendToUser(merchantId, "/queue/merchant-deliver", merchantNotify); } } // 封装买家/商家通知的工具方法 private OrderNotifyMessage buildBuyerNotify(Order order) { /* ... */ } private OrderNotifyMessage buildMerchantNotify(Order order, String merchantId) { /* ... */ } }
二、更简便的替代方案:Redis发布订阅 + 长轮询
如果不想维护长连接,可采用该方案:
1. 引入Redis依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2. 下单时发布消息
@Service public class OrderService { @Autowired private StringRedisTemplate redisTemplate; public void createOrder(OrderCreateRequest request) { Order order = saveOrderToDB(request); String buyerId = request.getBuyerId(); Set<String> merchantIds = order.getItems().stream() .map(OrderItem::getMerchantId) .collect(Collectors.toSet()); // 发布买家通知到专属频道 String buyerChannel = String.format("order:success:%s", buyerId); redisTemplate.convertAndSend(buyerChannel, JSON.toJSONString(buildBuyerNotify(order))); // 发布商家通知到各自专属频道 for (String merchantId : merchantIds) { String merchantChannel = String.format("order:deliver:%s", merchantId); redisTemplate.convertAndSend(merchantChannel, JSON.toJSONString(buildMerchantNotify(order, merchantId))); } } }
3. 长轮询获取通知
后端提供接口阻塞等待Redis消息,前端定时请求:
@RestController @RequestMapping("/notify") public class NotifyController { @Autowired private StringRedisTemplate redisTemplate; @GetMapping("/buyer/{buyerId}") public ResponseEntity<OrderNotifyMessage> getBuyerNotify(@PathVariable String buyerId) { String channel = String.format("order:success:%s", buyerId); CountDownLatch latch = new CountDownLatch(1); OrderNotifyMessage[] result = new OrderNotifyMessage[1]; RedisConnection connection = redisTemplate.getConnectionFactory().getConnection(); connection.subscribe((message, pattern) -> { result[0] = JSON.parseObject(new String(message.getBody()), OrderNotifyMessage.class); latch.countDown(); }, channel.getBytes()); try { // 阻塞10秒,超时返回空 latch.await(10, TimeUnit.SECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } finally { connection.close(); } return result[0] != null ? ResponseEntity.ok(result[0]) : ResponseEntity.noContent().build(); } }
三、方案选择建议
- WebSocket:适合实时性要求高、用户在线时长稳定的场景(如商家后台),需处理重连和离线消息存储(可结合数据库存未读消息)。
- Redis发布订阅+长轮询:实现简单,无需维护长连接,适合移动端等用户在线不稳定的场景,实时性略低于WebSocket(取决于轮询间隔)。
内容的提问来源于stack exchange,提问作者Ukeme Elijah
相关产品推荐
相关产品推荐

