Search K
Appearance
🍵 欢迎来到技术茶馆 🍵
这里是一个分享技术、交流学习的地方
技术札记 | 茶馆周刊 | 工具书签 | 作品展示
让我们一起品茗技术,共同成长
Appearance
在线拍卖的竞价窗口只有几十秒,一旦延迟超过 300 ms,用户就会怀疑自己被“插队”。我们最早在传统 HTTP 轮询上临时起盘,结果出现以下事故:
这篇文章把整个踩坑过程拆成四步:先看技术底座,再聊通讯原理,然后用真实 demo 复盘,最后给出选型和总结。
Upgrade: websocket 的 HTTP GET,服务端回 101 Switching Protocols,二者约定后复用同一条 TCP。代理兼容性好,但必须校验 Origin 防止跨站低价抢购。EventSource 发起 GET,Accept: text/event-stream 表明希望拿到 SSE。服务端响应 Content-Type: text/event-stream、Cache-Control: no-cache、Connection: keep-alive。event、id、data 组成,\n\n 结束。我们把“当前领先价”“倒计时”“围观人数”拆成多个事件;心跳只发 :\n\n 保持链路活跃。Last-Event-ID,服务端据此补发缺失的事件。缺点是只能传 UTF-8 文本,二进制图片要额外 Base64。EventSource 只支持 GET,无法顺便塞鉴权头,我们只能在 URL Query 中带 token,注意及时刷新;IE 全军覆没,微信内置浏览器也有版本差异,需要 fallback。RTCPeerConnection,数据通道走 RTCDataChannel。我们把“拍品高清直播”放在媒体流里,“竞价按钮状态”通过 DataChannel 做可靠传输,保证指令有序。const socket = new WebSocket('wss://auction.example/ws?lot_id=20241127');
socket.onopen = () => {
socket.send(JSON.stringify({ type: 'JOIN', lotId: '20241127', token }));
};
socket.onmessage = ({ data }) => {
const payload = JSON.parse(data);
if (payload.type === 'BID_PUSH') {
renderBid(payload.amount, payload.bidder);
}
};
// 心跳 + 断线重连
setInterval(() => socket.readyState === 1 && socket.send('ping'), 5000);后端基于 ws 或 Socket.IO,记得把 Sec-WebSocket-Key 写入日志排查代理转发问题。
const stream = new EventSource(`/auction/stream?lot=20241127&token=${token}`);
stream.onmessage = (evt) => {
const lines = JSON.parse(evt.data);
updateTicker(lines.price, lines.countdown);
};
stream.addEventListener('orderStatus', (evt) => {
showToast(evt.data);
});服务端用 Node.js Readable 或 Go http.Flusher 连续写入:
event: ticker
id: 1098
data: {"price":120900,"countdown":8}const pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] });
const local = await navigator.mediaDevices.getUserMedia({ audio: true, video: true });
local.getTracks().forEach((track) => pc.addTrack(track, local));
const dataChannel = pc.createDataChannel('control', { ordered: true });
dataChannel.onmessage = (evt) => syncBidButton(JSON.parse(evt.data));
const offer = await pc.createOffer();
await pc.setLocalDescription(offer);
signalServer.send({ type: 'offer', sdp: offer.sdp });TURN 账号一定要限制 IP 和流量,否则拍卖高峰期费用可以直接打穿预算。
navigator.connection.effectiveType)、基于消息 ID 的补发接口。Access-Control-Allow-Origin 白名单;信令通道要做鉴权签名;WebRTC 强制开启 SRTP,录制需征得用户授权。proxy_set_header Upgrade $http_upgrade; 与 Connection "Upgrade",否则 502。oniceconnectionstatechange 都要走同一套状态机,方便监控。EventSource 无法加自定义头,token 只能放 URL 或 Cookie;WebSocket 要校验 Origin,TURN 则需要独立域名。参考资料: