← 使用场景

抢票与限量发售的排队等候室

在开售页前排一条公平、实时的队伍:访客看着自己的位置前进,按你设定的速度进场,你的网站验证签名 pass。任何主机都能用,包括 Laravel 和 Filament。

抢票、限量发售、预购开跑,一整天的流量会挤在开卖的第一分钟。自动扩展来不及反应,结账对每个人都变慢,最后拼的是谁的网速快。等候室在最忙的页面前排一条平静、公平的队伍:访客拿到号码、实时看着队伍前进,按你的网站撑得住的速度进场。自己注册、自己设置,不用找销售,也不用换主机或 CDN。

访客会看到什么

  • 排队号码与「前面还有 4,812 人」,等待期间实时更新。
  • 刷新或多开标签页,位置不会掉,也不会往前插队。
  • 托管等候页支持英文、繁体中文、简体中文、日文、韩文、泰文与越南文。
  • 开卖前有清楚的倒数,卖完时清楚显示「已售完」。
托管等候页:访客的排队位置、前面人数、预估等待时间与商家公告,以访客的语言显示。
托管等候页:访客的排队位置、前面人数、预估等待时间与商家公告,以访客的语言显示。

你会得到什么

  • 按你设置的速度前进的队伍,例如每分钟 300 人。
  • 关键时刻用得到的按钮:暂停队伍、立刻放 500 人进来、库存卖完就关闭。
  • 每位放行的访客都会拿到签名 pass,你的网站验证后才提供受保护的页面,猜网址也插不了队。
  • 需要的话,用 pass 里的 ticket ID 做到一个号码只能结账一次。
  • 排队中的访客不占用 app 的连接数,抢购期间网站其他实时功能照样有余量。
  • 对所有排队的人发公告,例如「A 区已售完」,等候页立即显示。
控制台里的等候室:实时人数、放行速度、预估清空时间,以及暂停、放行、售罄与公告按钮。
控制台里的等候室:实时人数、放行速度、预估清空时间,以及暂停、放行、售罄与公告按钮。

等候室是任何 Hibira 方案都能加购的附加功能。一个房间、每月 5,000 位排队访客免费;付费方案每月 $29 起,单场活动票 $49 可用 72 小时。超过方案用量时,多出的访客从点数扣抵,不会有人被挡在门外。详见价格。

用到的功能

  • Hibira app 里的等候室,在控制台或用签名的 HTTP API 管理。
  • 托管等候页 ws.hibira.io/wait/APP_KEY/ROOM,或用 @hibira-io/waiting-room 自建等候页。
  • 每个房间一个 cache 频道(cache-waitroom-ROOM):一则消息告诉所有等候中的访客队伍走到哪里,每个浏览器自己算出位置。
  • 用你的 app secret 签名(HMAC-SHA256)的 pass,你的网站自己验证,不必调用 Hibira。
访客打开 /drop
没有 pass:导向
Hibira托管等候页
轮到了:?hibira_pass=…
你的网站验证 pass、设置 cookie

让队伍前进

你的团队控制台、Filament 或 API
打开、暂停、放行、关闭
Hibiracache-waitroom-presale
一则更新
所有等候中的访客各自算出位置
没有 pass 的访客先排队,轮到时带着签名 pass 回来

选择接入方式

按网站跑在哪里来选。越上面的方式越难被绕过。

步骤 1:创建房间

在控制台创建,或用签名的 HTTP API 创建,签名方式和发布事件相同:

POST /apps/APP_ID/waiting_rooms/presale
{
  "admit_per_minute": 300,
  "state": "paused",
  "starts_at": "2026-12-04T12:00:00+09:00",
  "title": "Winter tour presale",
  "return_urls": ["https://shop.example.com/"]
}

open 按速度放行、paused 先把队伍停住、closed 在卖完时停止发号码。在 starts_at 之前到的访客会拿到号码并看到倒数,时间一到队伍才开始前进。

步骤 2:在 edge 保护页面

在 Cloudflare Workers、Next.js middleware、Deno 或 Bun 上,一个调用就搞定:

// middleware.js (Next.js)
import { NextResponse } from 'next/server';
import { protect } from '@hibira-io/waiting-room/server';

export async function middleware(request) {
  const blocked = await protect(request, {
    appKey: process.env.HIBIRA_KEY,
    secret: process.env.HIBIRA_SECRET,
    room: 'presale',
  });
  return blocked ?? NextResponse.next();
}

export const config = { matcher: ['/drop/:path*'] };

没有 pass 的访客会被导到等候页。轮到时带着 pass 回来,protect() 验证后存进 HttpOnly cookie。Express 请用同一个套件里的 expressWaitingRoom()。

步骤 3:Laravel

使用 hibira-io/laravel 0.4 以上版本(你可能已经用它做广播),用命令创建房间,再用一个middleware 保护路由:

composer require hibira-io/laravel:^0.4
php artisan hibira:waiting-room presale create --rate=300 --title="Winter presale"
Route::middleware('hibira.waiting:presale')->group(function () {
    Route::get('/drop', [DropController::class, 'show']);
    Route::post('/drop/checkout', [DropController::class, 'checkout']);
});

没有 pass 的访客会被导到等候页,语言跟你的 app 一致。访客带回来的 pass用你的 app secret 验证,不需要调用 API,并存在 HttpOnly cookie 里直到过期。没有 pass 的 POST 会得到 403。在 controller 里可以从 request 取得 pass :

$pass = $request->attributes->get('hibira_pass');
$pass->number;   // queue number
$pass->ticketId; // record it at checkout: one order per number

在代码、队列任务或命令行都能操作房间:

use Hibira\Laravel\Facades\Hibira;

Hibira::waitingRoom('presale')->pause();
Hibira::waitingRoom('presale')->admit(500);
Hibira::waitingRoom('presale')->notice('Zone A is sold out');
Hibira::waitingRoom('presale')->close();

本地开发时设置 HIBIRA_WAITING_ROOMS=false,就不用排队。

步骤 4:在 Filament 里操作队伍

开卖当天,团队不需要再多开一个后台。使用 hibira-io/filament 0.5 以上版本,一行就能在后台加上「等候室」页面:

HibiraPlugin::make()->waitingRooms(
    authorize: fn ($user) => $user->is_admin,
)

这个页面实时显示每个房间的排队人数、放行进度和速度。团队可以在这里暂停、开放、立即放行、发布公告、售完时关闭房间,也能创建或修改房间。不写 authorize 的话,所有后台用户都能进入。

步骤 5:自建等候页

托管等候页适合大多数网站。想完全符合自家品牌,可以自己做:

import { joinWaitingRoom } from '@hibira-io/waiting-room';

joinWaitingRoom({
  key: 'APP_KEY',
  room: 'presale',
  onUpdate: ({ ahead, etaMinutes }) => render(ahead, etaMinutes),
  onTurn: (turn) => turn.redirect('https://shop.example.com/drop'),
  onClosed: () => showSoldOut(),
});

客户端会把访客的 ticket 存在浏览器里,刷新也不会掉位置。如果连不上,它会改成每隔几秒查一次状态,不会有人被挡在外面。

步骤 6:在 App 里排队

手机或游戏客户端用的是和托管等候页相同的公开端点:加入队伍、订阅房间的 cache 频道、轮到时换 pass,之后在 API 调用中带上 pass。你的 API 用 app secret 验证 pass,不需要调用 Hibira;在 Laravel 上用 WaitingRoom::verifyPass($pass, 'presale') 即可。

POST /waitroom/APP_KEY/presale/join         -> {"ticket": "…", "number": 5121}
GET  /waitroom/APP_KEY/presale              -> {"admitted": 4980, "state": "open", …}
POST /waitroom/APP_KEY/presale/pass         {"ticket": "…"}  -> {"pass": "…"}

与 Queue-it、Cloudflare Waiting Room 比较

根据 2026 年 11 月各家公开的价格与方案页面。方案会变动,做决定前请再向各家确认。

完整对比(含价格与各自适合的情况):Queue-it、Cloudflare Waiting Room、CrowdHandler、Queue-Fair。

小提醒

  • 保护真正会卖完的页面:商品页、购物车与结账。首页和图片放在等候室外面。
  • 在关键的请求上验证 pass,不要只在浏览器检查。访客可以改自己页面上的任何脚本,但伪造不了签名。
  • 如果一个号码只能买一次,结账时记下 pass 的 ticket ID,拒绝用同一个 ID 下第二笔订单。
  • 开卖前 15 到 30 分钟就打开房间并设置 starts_at。早到的访客会在倒数页等,而不是在你的网站不停刷新。
  • 一开始先用结账撑得住的速度,一边看错误率一边往上调。暂停永远只差一个按钮。