Scale PubNub applications for high-volume real-time events
PubNub is designed for massive scale:
| Channels Needed | Strategy | Max Channels |
|---|---|---|
| 1-30 | Multiplexing | 30 recommended |
| 30-2,000 | Single Channel Group | 2,000 |
| 2,000-20,000 | Multiple Channel Groups | 10 groups × 2,000 |
| Hierarchical | Wildcard Subscribe | Unlimited matching |
Subscribe to multiple named channels over single connection.
// Subscribe to multiple channels (up to 30 recommended)
pubnub.subscribe({
channels: ['chat-room-1', 'chat-room-2', 'notifications']
});Best for: Small, known set of channels per client.
Requires: Stream Controller enabled in Admin Portal
// Add channels to a group
await pubnub.channelGroups.addChannels({
channelGroup: 'user-alice-feeds',
channels: [
'feed-news',
'feed-sports',
'feed-tech',
'user-notifications-alice'
]
});
// Subscribe to the group
pubnub.subscribe({
channelGroups: ['user-alice-feeds']
});// List channels in group
const result = await pubnub.channelGroups.listChannels({
channelGroup: 'user-alice-feeds'
});
console.log('Channels:', result.channels);
// Remove channels from group
await pubnub.channelGroups.removeChannels({
channelGroup: 'user-alice-feeds',
channels: ['feed-tech']
});
// Delete entire group
await pubnub.channelGroups.deleteChannelGroup({
channelGroup: 'user-alice-feeds'
});| Resource | Limit |
|---|---|
| Channels per group | 2,000 (configurable) |
| Groups per client | 10 |
| Total channels | 20,000 per client |
// Server-side: Create personalized feed group
async function createUserFeedGroup(userId, subscriptions) {
const groupName = `user-${userId}-feeds`;
// Add user's subscribed topics
await pubnub.channelGroups.addChannels({
channelGroup: groupName,
channels: subscriptions.map(topic => `feed-${topic}`)
});
// Add user's personal channels
await pubnub.channelGroups.addChannels({
channelGroup: groupName,
channels: [
`user-notifications-${userId}`,
`user-dm-${userId}`
]
});
return groupName;
}
// Client-side: Subscribe to personal feed
const feedGroup = `user-${currentUserId}-feeds`;
pubnub.subscribe({
channelGroups: [feedGroup]
});Requires: Stream Controller enabled in Admin Portal
*) must be at the end.) is the hierarchy delimiter// One level
pubnub.subscribe({ channels: ['sports.*'] });
// Matches: sports.football, sports.basketball, sports.baseball
// Two levels
pubnub.subscribe({ channels: ['iot.building1.*'] });
// Matches: iot.building1.temp, iot.building1.humidity
// Specific namespace
pubnub.subscribe({ channels: ['stocks.nasdaq.*'] });
// Matches: stocks.nasdaq.aapl, stocks.nasdaq.goog// Wildcard not at end - INVALID
'sports.*.scores'
// Wildcard at start - INVALID
'*.notifications'
// Too many levels - INVALID
'a.b.c.d.*'// Publish sensor data to specific channel
await pubnub.publish({
channel: 'sensors.floor1.room101.temperature',
message: { value: 72.5, unit: 'F', timestamp: Date.now() }
});
// Subscribe to all sensors on floor 1
pubnub.subscribe({
channels: ['sensors.floor1.*']
});
// Listener receives from all matching channels
pubnub.addListener({
message: (event) => {
console.log('Channel:', event.channel); // sensors.floor1.room101.temperature
console.log('Data:', event.message);
}
});// For < 10,000 users in a room: Single channel works well
'chat-room-main'
// For > 10,000 users: Consider sharding
function getShardedChannel(roomId, userId) {
const shardCount = 10;
const shardId = hashCode(userId) % shardCount;
return `chat-room-${roomId}-shard-${shardId}`;
}
// Users subscribe to their shard
const myChannel = getShardedChannel('main', currentUserId);
pubnub.subscribe({ channels: [myChannel] });
// Broadcast messages to all shards
async function broadcastToRoom(roomId, message) {
const shardCount = 10;
const publishes = [];
for (let i = 0; i < shardCount; i++) {
publishes.push(pubnub.publish({
channel: `chat-room-${roomId}-shard-${i}`,
message
}));
}
await Promise.all(publishes);
}// For high-occupancy channels, use interval events
// Configure "Announce Max" in Admin Portal
pubnub.addListener({
presence: (event) => {
if (event.action === 'interval') {
// Batch updates for high occupancy
console.log('Occupancy:', event.occupancy);
if (event.join) event.join.forEach(handleJoin);
if (event.leave) event.leave.forEach(handleLeave);
} else {
// Individual events for normal occupancy
handlePresenceEvent(event);
}
}
});Browsers limit connections per hostname (Chrome: 6).
// PubNub uses efficient connection pooling
// One subscribe connection + one for publishes/API calls
// For multiple PubNub instances (rare), consider custom origin
// Contact PubNub support for custom origin: yoursubdomain.pubnubapi.com// PubNub SDKs maintain long-lived connections
// Default: 1-hour keep-alive with 5-minute pings
// For battery-sensitive mobile apps
const pubnub = new PubNub({
subscribeKey: 'sub-c-...',
userId: 'user-123',
heartbeatInterval: 120, // Less frequent heartbeats
presenceTimeout: 600 // Longer timeout
});pubnub.addListener({
status: (statusEvent) => {
if (statusEvent.operation === 'PNPublishOperation') {
console.log('Publish latency info:', statusEvent);
}
}
});tessl i pubnub/pubnub-scale@0.1.4