← กรณีการใช้งาน

จำนวนคนออนไลน์และผู้ชม

แสดงจำนวนคนที่ออนไลน์หรือกำลังดู นับผู้ใช้ไม่ซ้ำด้วย presence และอ่านจำนวนคนของทุกห้องจากเซิร์ฟเวอร์ด้วย HTTP API และ webhook

“ออนไลน์ 1,284 คน” “กำลังดู 38 คน” “ห้องเต็ม”: ตัวเลขคนแบบสดทำให้ผลิตภัณฑ์ดูคึกคัก และตัวเลขเดียวกันนี้ก็บอกทีมของคุณว่าห้องไหนกำลังร้อนแรง เมื่อไรควรเพิ่มผู้ดูแล และช่วงพีคตอนค่ำสูงแค่ไหน คุณไม่ต้องนับการเชื่อมต่อเอง Hibira รู้อยู่แล้วว่าใครสมัครรับข้อมูลในแต่ละช่อง และโปรโตคอล Pusher มีสี่วิธีให้คุณถาม

สิ่งที่ผู้ใช้ได้รับ

  • ตัวเลข “ออนไลน์” หรือ “กำลังดู” ที่ขยับตามคนที่เข้าและออก
  • แถวรูปโปรไฟล์พร้อม “และอีก 120 คน” โดยคนที่เปิดสองแท็บนับเป็นคนเดียว
  • ห้องที่บอกว่า “เต็ม” ก่อนจะมีใครพยายามเข้า
  • สำหรับทีมของคุณ: หน้าผู้ดูแลที่แสดงห้องที่คนเยอะที่สุด และประวัติช่วงพีค
หน้าผู้ดูแลตัวอย่าง: จำนวนคนออนไลน์ทั้งเว็บไซต์ จำนวนผู้ชมแบบสด และห้องที่คนเยอะที่สุด ทั้งหมดอ่านจาก Hibira
หน้าผู้ดูแลตัวอย่าง: จำนวนคนออนไลน์ทั้งเว็บไซต์ จำนวนผู้ชมแบบสด และห้องที่คนเยอะที่สุด ทั้งหมดอ่านจาก Hibira

ฟีเจอร์ที่ใช้

  • จำนวนผู้สมัครรับข้อมูล (pusher:subscription_count) สำหรับตัวเลขในเบราว์เซอร์บนช่องสาธารณะและช่องส่วนตัว
  • ช่อง presence (presence-) สำหรับนับผู้ใช้ไม่ซ้ำ และ ช่อง presence ขนาดใหญ่ (presence-large-) สูงสุด 100,000 คน
  • HTTP API (GET /channels) เพื่ออ่านตัวเลขจากเซิร์ฟเวอร์ของคุณได้ทุกเมื่อ
  • Webhook (member_added, member_removed, subscription_count) เพื่อรับแจ้งเมื่อจำนวนเปลี่ยน จะได้เก็บประวัติไว้

ในเบราว์เซอร์

Hibira
pusher:subscription_count
ผู้ชมทุกคน“กำลังดู 38 คน”

เซิร์ฟเวอร์ของคุณถาม

หน้าผู้ดูแล
fetch
เซิร์ฟเวอร์ของคุณ
GET /channels?info=user_count
Hibiraจำนวนคนต่อห้อง

เซิร์ฟเวอร์ของคุณได้รับแจ้ง

Hibira
webhook member_added
เซิร์ฟเวอร์ของคุณประวัติและการแจ้งเตือน
อ่านตัวเลขในเบราว์เซอร์ ถามจากเซิร์ฟเวอร์ หรือรับแจ้งเมื่อมันเปลี่ยน

นับการเชื่อมต่อหรือนับคน?

นี่คือตัวเลขสองแบบที่ต่างกัน ควรเลือกอย่างตั้งใจ:

  • subscription_count นับการเชื่อมต่อ คนที่เปิดสามแท็บถูกนับสามครั้ง ใช้ได้บนช่องสาธารณะและช่องส่วนตัวโดยไม่ต้องลงชื่อเข้าใช้ จึงเหมาะกับตัวนับ “กำลังดู” บนหน้าสาธารณะ
  • user_count นับผู้ใช้ไม่ซ้ำบนช่อง presence ตาม user_id ที่เซิร์ฟเวอร์ของคุณลงนาม สองแท็บ หรือโทรศัพท์กับแล็ปท็อป นับเป็นคนเดียว ใช้เมื่อ “คน” คือสิ่งสำคัญ เช่น จำนวนที่นั่งสูงสุดหรือรายชื่อคนออนไลน์

ขั้นที่ 1: ตัวเลขในเบราว์เซอร์

เปิด ส่งจำนวนผู้สมัครรับข้อมูล ในการตั้งค่าแอป จากนั้นผู้สมัครรับข้อมูลทุกคนของช่องสาธารณะหรือช่องส่วนตัวจะได้รับจำนวนปัจจุบัน ไม่เกินวินาทีละครั้งระหว่างที่มันเปลี่ยน:

const stream = pusher.subscribe('stream-42');
stream.bind('pusher:subscription_count', ({ subscription_count }) => {
  watching.textContent = subscription_count.toLocaleString();
});

ไม่ต้องเขียนโค้ดก็ได้ สคริปต์ฝังบรรทัดเดียว ทำสิ่งเดียวกัน: <span data-hibira-online="stream-42">0</span> คนกำลังดู

ขั้นที่ 2: นับคนด้วย presence

บนช่อง presence ทุกคนได้รับรายชื่อสมาชิก ดังนั้นตัวเลขอยู่ในเบราว์เซอร์แล้ว:

const room = pusher.subscribe('presence-room-' + roomId);
const show = () => online.render(room.members.count, room.members);

room.bind('pusher:subscription_succeeded', show);
room.bind('pusher:member_added', show);
room.bind('pusher:member_removed', show);

ช่อง presence รองรับสูงสุด 100 คน เพราะสมาชิกทุกคนได้รับการเข้าร่วมทุกครั้ง สำหรับทั้งเว็บไซต์หรืออีเวนต์ใหญ่ ให้ทุกหน้าเข้าร่วมช่อง presence ขนาดใหญ่ช่องเดียวด้วย @hibira-io/large-presence รองรับสูงสุด 100,000 คน และส่งจำนวนที่แม่นยำพร้อมตัวอย่างผู้ที่อยู่ วินาทีละครั้ง:

import { LargePresence } from '@hibira-io/large-presence';

const site = LargePresence.subscribe(pusher, 'presence-large-site', { maxMembers: 12 });
site.on('ready', ({ count }) => badge.render(count, site.members));
site.on('change', ({ count }) => badge.render(count, site.members));

ขั้นที่ 3: ถามจากเซิร์ฟเวอร์ของคุณ

เซิร์ฟเวอร์ของคุณอ่านตัวเลขเดียวกันได้ผ่าน HTTP API ด้วยไลบรารีฝั่งเซิร์ฟเวอร์ที่เข้ากันได้กับ Pusher ตัวใดก็ได้ หนึ่งช่อง:

const res = await pusher.get({
  path: '/channels/presence-room-42',
  params: { info: 'user_count,subscription_count' },
});
const { occupied, user_count, subscription_count } = await res.json();

ทุกห้องในครั้งเดียว เรียงจากคนเยอะที่สุด:

const res = await pusher.get({
  path: '/channels',
  params: { filter_by_prefix: 'presence-room-', info: 'user_count' },
});
const { channels } = await res.json();
// { "presence-room-7": { "user_count": 42 }, "presence-room-9": { "user_count": 17 } }
const busiest = Object.entries(channels)
  .map(([name, { user_count }]) => ({ name, users: user_count }))
  .sort((a, b) => b.users - a.users);

หากต้องการตรวจจำนวนที่นั่งก่อนลงนามการสมัครรับข้อมูล ให้อ่านจำนวนคนในห้องที่เส้นทางยืนยันสิทธิ์ช่อง และปฏิเสธเมื่อห้องเต็ม:

app.post('/pusher/auth', requireLogin, async (req, res) => {
  const { socket_id, channel_name } = req.body;
  const room = await pusher.get({ path: `/channels/${channel_name}`, params: { info: 'user_count' } });
  const { user_count } = await room.json();
  if (user_count >= 8) return res.status(403).json({ error: 'room_full' });
  res.json(pusher.authorizeChannel(socket_id, channel_name, { user_id: req.user.id, user_info: { name: req.user.name } }));
});

และเมื่อเผยแพร่ คุณถามได้ว่าตอนนั้นมีกี่การเชื่อมต่อที่สมัครรับข้อมูลอยู่ ซึ่งบอกว่าอีเวนต์ไปถึงกี่คน:

const sent = await pusher.trigger('stream-42', 'goal', { minute: 81 }, { info: 'subscription_count' });
const { channels } = await sent.json();
console.log(`reached ${channels['stream-42'].subscription_count} viewers`);

user_count ขอได้เฉพาะบนช่อง presence และในรายการช่องต้องใช้ filter_by_prefix=presence- การรวมจำนวนคนของหลายห้องจะนับซ้ำคนที่อยู่สองห้องพร้อมกัน หากต้องการจำนวนคนไม่ซ้ำทั้งเว็บไซต์ ให้อ่าน GET /channels/presence-large-site?info=user_count แทน

ขั้นที่ 4: เก็บประวัติด้วย webhook

หากต้องการวาดกราฟช่วงพีคตอนค่ำ หรือส่งการแจ้งเตือนเมื่อห้องว่าง ให้ Hibira แจ้งเซิร์ฟเวอร์ของคุณ ในพอร์ทัล เพิ่ม webhook สำหรับ member_added และ member_removed (ช่อง presence), subscription_count (ช่องสาธารณะและส่วนตัว เมื่อเปิดการนับ) หรือ channel_occupied และ channel_vacated:

app.post('/hibira/webhooks', express.text({ type: '*/*' }), async (req, res) => {
  const webhook = pusher.webhook({ headers: req.headers, rawBody: req.body });
  if (!webhook.isValid()) return res.sendStatus(401);
  for (const e of webhook.getEvents()) {
    if (e.name === 'member_added') await db.online.add(e.channel, e.user_id);
    if (e.name === 'member_removed') await db.online.remove(e.channel, e.user_id);
    if (e.name === 'subscription_count') await db.viewers.record(e.channel, e.subscription_count);
  }
  res.sendStatus(200);
});

เคล็ดลับ

  • การอ่านตัวเลขไม่ใช้โควตาข้อความ มีเพียงอีเวนต์ที่เผยแพร่เท่านั้นที่นับเป็นข้อความ ถึงอย่างนั้น ควรแคชคำตอบของ HTTP API ไว้บนเซิร์ฟเวอร์สักสองสามวินาที แทนที่จะถามทุกครั้งที่มีการเปิดหน้า
  • Webhook อาจมาช้าหรือมาซ้ำ ใช้มันสำหรับประวัติและการแจ้งเตือน และอ่าน HTTP API เมื่อต้องการตัวเลขที่แม่นยำตอนนี้
  • บนหน้าสาธารณะที่คนเยอะ ให้แสดงตัวเลขแบบปัดเศษ (“ออนไลน์ 1.2k”) มันขยับน้อยกว่าและยังรู้สึกมีชีวิตชีวา
  • ตัวเลขแยกตามแอป หากคุณมีแอปทดสอบและแอปจริง แต่ละแอปเห็นเฉพาะการเชื่อมต่อของตัวเอง
  • แพ็กเกจจำกัดการเชื่อมต่อพร้อมกัน ไม่ใช่จำนวนคน: ดูพีคที่แต่ละแพ็กเกจรองรับได้ที่ราคา