websocket是什么以及它要怎么用

WebSocket 实时通知机制
在高并发、异步下单场景中,它是提升用户体验、降低轮询压力的关键组件。
接下来会从原理讲清楚,再结合“秒杀活动”落地展示完整的前后端交互与代码实现(Java Spring Boot + 前端示例)。


一、WebSocket 是什么?

WebSocket 是一种在浏览器和服务器之间建立「全双工、持久连接」的通信协议
与传统 HTTP 不同,HTTP 是短连接、请求响应式的;而 WebSocket 建立连接后,服务器可以主动推送消息给客户端,不需要客户端轮询。

🔁 对比传统 HTTP 轮询

特点HTTP 轮询WebSocket
连接模式 短连接,请求-响应 持久连接,双向通信
服务器能否主动发消息 ❌ 不能 ✅ 可以
性能 频繁请求,压力大 长连接,轻量、实时
适用场景 请求量小、实时性低 聊天、订单状态、秒杀、行情推送

二、在「秒杀活动」中为什么用 WebSocket?

在你的秒杀设计中:

  • 用户点击“立即抢购” → 系统用 Redis + MQ 异步下单

  • 请求立即返回 "QUEUED" 状态;

  • 后端在队列消费者落单后,才知道成功或失败。

如果不用 WebSocket:

  • 用户只能轮询 GET /seckill/status?messageId=...

  • 并发量高时,会造成 Web 层 + Redis + DB 压力。

如果用 WebSocket:

  • 用户请求秒杀后,保持 WebSocket 长连接;

  • 后端消费者落单完成时,通过 WebSocket 通道主动推送订单状态变更

  • 用户即时看到结果,不需要反复刷新。

👉 实时、节流、省资源。


三、WebSocket 在秒杀系统中的使用流程(图解)

        ┌──────────────────────┐
        │       浏览器端        │
        │   1. 建立 WebSocket  │
        │   2. 发送秒杀请求     │
        │   3. 等待推送结果     │
        └──────────┬───────────┘
                   │
          WebSocket 连接
                   │
        ┌──────────▼──────────┐
        │   秒杀服务 (Spring)  │
        │ - 维护连接映射表     │
        │ - 通过 MQ 消费结果   │
        │ - 推送状态到客户端   │
        └──────────┬───────────┘
                   │
                 MQ/Rabbit
                   │
        ┌──────────▼──────────┐
        │     消费者服务       │
        │ - 创建订单           │
        │ - 发送结果通知       │
        └──────────────────────┘

四、详细实现(Java + 前端)

🔹 前端实现(示例)

假设前端是一个简单的 Vue / HTML 页面。

<!DOCTYPE html>
<html>
<head>
  <title>秒杀活动</title>
</head>
<body>
  <h2>秒杀活动</h2>
  <button id="buyBtn">立即抢购</button>
  <div id="status"></div>

  <script>
    const userId = "user_123";
    const ws = new WebSocket(`ws://localhost:8080/ws/seckill/${userId}`);

    ws.onopen = () => {
      console.log("✅ WebSocket connected");
    };

    ws.onmessage = (event) => {
      const msg = JSON.parse(event.data);
      if (msg.type === "ORDER_STATUS") {
        document.getElementById("status").innerText = "订单状态: " + msg.status;
      }
    };

    document.getElementById("buyBtn").onclick = async () => {
      const res = await fetch("/api/seckill/buy?eventId=1001&userId=" + userId, {
        method: "POST"
      });
      const data = await res.json();
      document.getElementById("status").innerText = "请求结果: " + data.status;
    };
  </script>
</body>
</html>

说明:

  • 页面在加载时与服务器建立 WebSocket 长连接;

  • 当用户点击“立即抢购”后,发起 HTTP 下单请求;

  • 后端在订单异步完成后,通过 WebSocket 主动推送消息;

  • 前端即时更新显示。


🔹 后端实现(Spring Boot)

我们需要三个部分:

  1. WebSocket 服务端(负责连接与推送)

  2. 秒杀请求 Controller(发起秒杀逻辑)

  3. 订单消费者(异步落单后推送结果)


1️⃣ WebSocket 配置与服务端类

// WebSocketConfig.java
@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
        registry.addHandler(new SeckillWebSocketHandler(), "/ws/seckill/{userId}")
                .setAllowedOrigins("*"); // 生产环境记得做白名单
    }
}
// SeckillWebSocketHandler.java
@Component
public class SeckillWebSocketHandler extends TextWebSocketHandler {

    // 存储所有用户连接(简单实现,可换为 Redis + Channel 分布式映射)
    private static final Map<String, WebSocketSession> userSessions = new ConcurrentHashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) {
        String userId = getUserId(session);
        userSessions.put(userId, session);
        System.out.println("用户 " + userId + " 已连接");
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
        String userId = getUserId(session);
        userSessions.remove(userId);
        System.out.println("用户 " + userId + " 已断开");
    }

    private String getUserId(WebSocketSession session) {
        return session.getUri().getPath().split("/ws/seckill/")[1];
    }

    public void sendMessageToUser(String userId, String message) {
        WebSocketSession session = userSessions.get(userId);
        if (session != null && session.isOpen()) {
            try {
                session.sendMessage(new TextMessage(message));
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

2️⃣ 秒杀 Controller(异步下单 + 即时返回) 

@RestController
@RequestMapping("/api/seckill")
public class SeckillController {

    @Autowired
    private MessageQueueProducer mqProducer;
    @Autowired
    private SeckillWebSocketHandler wsHandler;

    @PostMapping("/buy")
    public ResponseEntity<?> buy(@RequestParam Long eventId, @RequestParam String userId) {
        // 假设 Redis 校验逻辑已经通过
        String messageId = UUID.randomUUID().toString();
        SeckillMessage msg = new SeckillMessage(messageId, eventId, userId);

        // 发送异步消息给 MQ
        mqProducer.send("seckill-topic", msg);

        // 立即通知前端:排队中
        wsHandler.sendMessageToUser(userId, "{\"type\":\"ORDER_STATUS\", \"status\":\"QUEUEING\"}");

        return ResponseEntity.ok(Map.of("status", "QUEUED", "messageId", messageId));
    }
}

3️⃣ 消费者处理(模拟异步下单完成后推送结果)

@Component
public class SeckillConsumer {

    @Autowired
    private SeckillWebSocketHandler wsHandler;

    // 模拟从 MQ 消费到消息
    @RabbitListener(queues = "seckill-queue")
    public void handleSeckill(SeckillMessage msg) throws Exception {
        System.out.println("消费到秒杀消息:" + msg);
        Thread.sleep(2000); // 模拟落单耗时

        // 模拟结果(成功/失败)
        boolean success = Math.random() > 0.2;
        String status = success ? "SUCCESS" : "FAILED";

        // 通知前端(WebSocket 推送)
        wsHandler.sendMessageToUser(
            msg.getUserId(),
            String.format("{\"type\":\"ORDER_STATUS\", \"status\":\"%s\", \"orderNo\":\"%s\"}",
                    status, msg.getMessageId())
        );
    }
}

五、WebSocket 在秒杀系统中的优势

优势描述
✅ 实时性强 MQ 消费完成立刻推送结果给前端
✅ 降低轮询压力 不需要前端频繁 GET /status
✅ 提升体验 用户感觉“抢购完立刻知道结果”
✅ 可扩展 支持多用户连接、广播通知等
⚠️ 注意 要做断线重连与分布式会话管理

六、生产实践中要考虑的问题

问题解决方案
WebSocket 连接量大(百万用户) Netty / Gateway 分发层 维护长连接(例如 Spring WebFlux 或 IM 服务)
多实例部署时如何路由用户连接 用 Redis Pub/Sub 或 Kafka 通知其他实例转发消息
用户断线怎么办 前端定时重连 + 状态轮询兜底
安全性 WebSocket 握手时带 Token 鉴权,不允许匿名连接

七、总结

WebSocket 在秒杀系统中的角色:

“让用户在异步下单模型下实时获知订单状态,替代高频轮询。”

典型实现路径:

  1. 用户进入活动页 → 建立 WebSocket;

  2. 点击抢购 → HTTP 调用 /buy;

  3. 后端 Redis + MQ 异步落单;

  4. 消费端完成后 → 通过 WebSocket 主动推送结果;

  5. 前端即时显示成功/失败。

典型技术栈:

  • 后端: Spring Boot + WebSocket + MQ (Rabbit/Kafka)

  • 前端: WebSocket API + 状态 UI 更新

  • 连接管理: Redis Pub/Sub + Session 映射

 

posted @ 2025-11-02 23:22  Boblim  阅读(3)  评论(0)    收藏  举报