寻找最短 RTT 的 IP 地址

2024年7月10日

问题

现有 http://ip/ping 接口, 有一堆 ip 地址列表,并发数是 10,请用最快的时间找出 rtt 最少的 ip 地址。

思路

  1. 并发请求 10个 为 一组,取最快的一个, 然后就取消其他请求,记录下这个ip地址和 rtt
  2. 如果组内的所有请求比之前记录的 rtt 还慢,则直接取消 fetch
  3. 如果组内的所有请求比之前记录的 rtt 还快,则记录下这个 ip 地址和 rtt

解决方案

/**
 * 找到最快rtt的ip地址 
 * @param {Array<string>} ips 
 * @param {number} parellel 
 */

async function findShortestRttIp(ips, parellel = 10) {
    const chunks = splitChunks(ips);

    const result = {
        rtt: Infinity,
        ip: '',
    };

    for(const chunk of chunks) {
        const pingResult = await race(chunk, result.rtt);
        if (pingResult) {
            result.rtt = pingResult.rtt;
            result.ip = pingResult.ip;
        }
    }

    return result.ip;
}

function splitChunks(ips, parellel = 10) {
    const chunks = [];
    for(let i = 0; i < ips.length; i += parellel) {
        chunks.push(ips.slice(i, i + parellel));
    }
    return chunks;
}

function race(ips, mininmalRtt) {
    return new Promise((resolve) => {
        const controller = new AbortController();
        const signal = controller.signal;
        // 超过 mininmalRtt 的请求,直接取消
        setTimeout(() => {
            controller.abort();
            resolve(null);
        }, mininmalRtt);

        for(let ip of ips) {
            const startTime = Date.now();
            fetch(`http://${ip}/ping`, { signal }).finally(() => {
                const rtt = Date.now() - startTime;
                if (rtt < mininmalRtt) {
                    resolve({
                        ip,
                        rtt,
                    })
                }
                // 取消其他请求
                controller.abort();
            })
        }

    });   
}