Skip to main content
POST
/
route
/
batch
curl -X POST "http://localhost:5001/route/batch?intranet=0" \
  -H "Content-Type: application/json" \
  -d '["user1", "user2", "user3"]'
[
  {
    "uids": ["user1", "user2"],
    "tcp_addr": "127.0.0.1:5100",
    "ws_addr": "ws://127.0.0.1:5200",
    "wss_addr": "wss://127.0.0.1:5300"
  },
  {
    "uids": ["user3"],
    "tcp_addr": "127.0.0.1:5101",
    "ws_addr": "ws://127.0.0.1:5201",
    "wss_addr": "wss://127.0.0.1:5301"
  }
]

Overview

Batch get IM connection addresses for multiple users, used to assign different connection nodes for different users.

Query Parameters

intranet
integer
default:0
Whether to return intranet addresses
  • 0 - Return external network addresses
  • 1 - Return internal network addresses

Request Body

uids
array
required
Array of user IDs
uids[]
string
User ID
curl -X POST "http://localhost:5001/route/batch?intranet=0" \
  -H "Content-Type: application/json" \
  -d '["user1", "user2", "user3"]'
[
  {
    "uids": ["user1", "user2"],
    "tcp_addr": "127.0.0.1:5100",
    "ws_addr": "ws://127.0.0.1:5200",
    "wss_addr": "wss://127.0.0.1:5300"
  },
  {
    "uids": ["user3"],
    "tcp_addr": "127.0.0.1:5101",
    "ws_addr": "ws://127.0.0.1:5201",
    "wss_addr": "wss://127.0.0.1:5301"
  }
]

Response Fields

The response is an array, each element contains the following fields:
uids
array
required
List of user IDs assigned to this address
tcp_addr
string
required
TCP connection address, format: host:port
ws_addr
string
required
WebSocket connection address, format: ws://host:port
wss_addr
string
required
WebSocket Secure connection address, format: wss://host:port

Status Codes

Status CodeDescription
200Successfully retrieved batch IM connection addresses
400Request parameter error
500Internal server error

Use Cases

Multi-User Connection Setup

Batch Connect Multiple Users:
// Connect multiple users to their assigned nodes
async function batchConnectUsers(userIds) {
    try {
        // Get connection addresses for all users
        const response = await fetch('/route/batch?intranet=0', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(userIds)
        });
        
        const addressGroups = await response.json();
        const connections = [];
        
        // Connect users to their assigned nodes
        for (const group of addressGroups) {
            const connectionUrl = window.location.protocol === 'https:' 
                ? group.wss_addr 
                : group.ws_addr;
            
            for (const uid of group.uids) {
                try {
                    const connection = await connectUser(uid, connectionUrl);
                    connections.push({
                        uid: uid,
                        connection: connection,
                        node_address: connectionUrl,
                        success: true
                    });
                } catch (error) {
                    connections.push({
                        uid: uid,
                        connection: null,
                        node_address: connectionUrl,
                        success: false,
                        error: error.message
                    });
                }
            }
        }
        
        return connections;
    } catch (error) {
        console.error('Batch user connection failed:', error);
        throw error;
    }
}

// Usage
const userIds = ['user1', 'user2', 'user3', 'user4', 'user5'];
const connections = await batchConnectUsers(userIds);

const successful = connections.filter(c => c.success);
const failed = connections.filter(c => !c.success);

console.log(`Connected ${successful.length} users, ${failed.length} failed`);

Load Distribution

Distribute Users Across Nodes:
// Distribute users across available nodes for load balancing
class UserLoadDistributor {
    constructor() {
        this.nodeUserMap = new Map();
    }
    
    async distributeUsers(userIds, batchSize = 50) {
        const batches = this.chunkArray(userIds, batchSize);
        const allDistributions = [];
        
        for (const batch of batches) {
            try {
                const distributions = await this.getBatchDistribution(batch);
                allDistributions.push(...distributions);
                
                // Track node assignments
                this.trackNodeAssignments(distributions);
                
            } catch (error) {
                console.error('Failed to distribute batch:', error);
            }
        }
        
        return allDistributions;
    }
    
    async getBatchDistribution(userIds) {
        const response = await fetch('/route/batch?intranet=0', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(userIds)
        });
        
        return await response.json();
    }
    
    trackNodeAssignments(distributions) {
        for (const dist of distributions) {
            const nodeKey = dist.ws_addr;
            if (!this.nodeUserMap.has(nodeKey)) {
                this.nodeUserMap.set(nodeKey, []);
            }
            this.nodeUserMap.get(nodeKey).push(...dist.uids);
        }
    }
    
    getNodeStatistics() {
        const stats = [];
        for (const [nodeAddr, users] of this.nodeUserMap) {
            stats.push({
                node_address: nodeAddr,
                user_count: users.length,
                users: users
            });
        }
        return stats;
    }
    
    chunkArray(array, size) {
        const chunks = [];
        for (let i = 0; i < array.length; i += size) {
            chunks.push(array.slice(i, i + size));
        }
        return chunks;
    }
}

// Usage
const distributor = new UserLoadDistributor();
const userIds = Array.from({length: 1000}, (_, i) => `user${i + 1}`);

const distributions = await distributor.distributeUsers(userIds);
const stats = distributor.getNodeStatistics();

console.log('Node distribution statistics:', stats);

Connection Pool Management

Manage Connection Pools by Node:
// Manage connection pools for different nodes
class NodeConnectionPoolManager {
    constructor() {
        this.pools = new Map();
        this.userNodeMap = new Map();
    }
    
    async initializePools(userIds) {
        // Get node assignments for users
        const distributions = await this.getBatchAddresses(userIds);
        
        // Create connection pools for each node
        for (const dist of distributions) {
            const nodeKey = dist.ws_addr;
            
            if (!this.pools.has(nodeKey)) {
                this.pools.set(nodeKey, {
                    address: dist.ws_addr,
                    tcp_addr: dist.tcp_addr,
                    wss_addr: dist.wss_addr,
                    connections: new Map(),
                    userCount: 0
                });
            }
            
            const pool = this.pools.get(nodeKey);
            
            // Track user assignments
            for (const uid of dist.uids) {
                this.userNodeMap.set(uid, nodeKey);
                pool.userCount++;
            }
        }
        
        console.log(`Initialized ${this.pools.size} connection pools`);
    }
    
    async getBatchAddresses(userIds) {
        const response = await fetch('/route/batch?intranet=0', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(userIds)
        });
        
        return await response.json();
    }
    
    async connectUser(userId) {
        const nodeKey = this.userNodeMap.get(userId);
        if (!nodeKey) {
            throw new Error(`No node assignment found for user ${userId}`);
        }
        
        const pool = this.pools.get(nodeKey);
        if (!pool) {
            throw new Error(`No connection pool found for node ${nodeKey}`);
        }
        
        // Check if user already has a connection
        if (pool.connections.has(userId)) {
            return pool.connections.get(userId);
        }
        
        // Create new connection
        try {
            const connection = new WebSocket(pool.address);
            
            await new Promise((resolve, reject) => {
                connection.onopen = resolve;
                connection.onerror = reject;
                setTimeout(reject, 5000); // 5 second timeout
            });
            
            pool.connections.set(userId, connection);
            console.log(`User ${userId} connected to node ${nodeKey}`);
            
            return connection;
        } catch (error) {
            console.error(`Failed to connect user ${userId} to node ${nodeKey}:`, error);
            throw error;
        }
    }
    
    disconnectUser(userId) {
        const nodeKey = this.userNodeMap.get(userId);
        if (!nodeKey) return;
        
        const pool = this.pools.get(nodeKey);
        if (!pool) return;
        
        const connection = pool.connections.get(userId);
        if (connection) {
            connection.close();
            pool.connections.delete(userId);
            console.log(`User ${userId} disconnected from node ${nodeKey}`);
        }
    }
    
    getPoolStatistics() {
        const stats = [];
        for (const [nodeKey, pool] of this.pools) {
            stats.push({
                node_address: nodeKey,
                assigned_users: pool.userCount,
                active_connections: pool.connections.size,
                connection_rate: (pool.connections.size / pool.userCount * 100).toFixed(2) + '%'
            });
        }
        return stats;
    }
}

// Usage
const poolManager = new NodeConnectionPoolManager();

// Initialize with user list
const userIds = ['user1', 'user2', 'user3', 'user4', 'user5'];
await poolManager.initializePools(userIds);

// Connect users
for (const userId of userIds) {
    try {
        await poolManager.connectUser(userId);
    } catch (error) {
        console.error(`Failed to connect ${userId}:`, error);
    }
}

// Get statistics
const stats = poolManager.getPoolStatistics();
console.log('Pool statistics:', stats);

Geographic Distribution

Route Users by Geographic Location:
// Route users to geographically optimal nodes
async function routeUsersByLocation(userLocations) {
    const userIds = userLocations.map(ul => ul.userId);
    
    try {
        // Get node assignments
        const distributions = await fetch('/route/batch?intranet=0', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify(userIds)
        }).then(r => r.json());
        
        // Analyze geographic distribution
        const geoAnalysis = [];
        
        for (const dist of distributions) {
            const nodeUsers = dist.uids.map(uid => 
                userLocations.find(ul => ul.userId === uid)
            );
            
            const avgLatitude = nodeUsers.reduce((sum, u) => sum + u.latitude, 0) / nodeUsers.length;
            const avgLongitude = nodeUsers.reduce((sum, u) => sum + u.longitude, 0) / nodeUsers.length;
            
            geoAnalysis.push({
                node_address: dist.ws_addr,
                users: dist.uids,
                user_count: dist.uids.length,
                avg_location: {
                    latitude: avgLatitude,
                    longitude: avgLongitude
                },
                user_locations: nodeUsers.map(u => ({
                    userId: u.userId,
                    latitude: u.latitude,
                    longitude: u.longitude,
                    country: u.country
                }))
            });
        }
        
        return geoAnalysis;
    } catch (error) {
        console.error('Geographic routing failed:', error);
        throw error;
    }
}

// Usage
const userLocations = [
    { userId: 'user1', latitude: 40.7128, longitude: -74.0060, country: 'US' },
    { userId: 'user2', latitude: 51.5074, longitude: -0.1278, country: 'UK' },
    { userId: 'user3', latitude: 35.6762, longitude: 139.6503, country: 'JP' },
    { userId: 'user4', latitude: 39.9042, longitude: 116.4074, country: 'CN' },
    { userId: 'user5', latitude: 52.5200, longitude: 13.4050, country: 'DE' }
];

const geoDistribution = await routeUsersByLocation(userLocations);
console.log('Geographic distribution:', geoDistribution);

Best Practices

  1. Batch Size: Use appropriate batch sizes to balance performance and resource usage
  2. Error Handling: Handle partial failures gracefully when some users can’t be routed
  3. Caching: Cache node assignments to reduce API calls for frequently accessed users
  4. Load Monitoring: Monitor node load distribution and adjust routing as needed
  5. Failover: Implement failover mechanisms when assigned nodes become unavailable
  6. Geographic Optimization: Consider user geographic location for optimal routing
  7. Connection Pooling: Use connection pools to efficiently manage multiple user connections