mapbox 中的请求队列

370 阅读2分钟

前提:

CHROME浏览器(http1.1)

①同一域名下,同一GET请求的并发数是1,也就是说上一个请求结束,才会执行下一个请求,否则置入队列等待发送;

②同一域名下,不同GET/POST请求的并发数量是6。当发送的请求数量达到6个,并且都没有得到响应时,后面的请求会置入队列等待发送。

问题

多个 raster 瓦片(256*256)图层,不同域名,在加载时会阻塞

代码实现(核心)

let queue, numFetch;

export const resetFetchQueue = () => {
    queue = [];
    numFetch = 0;
};

resetFetchQueue();

function fetch (p, callback) {
    let complete = false;
    let aborted = false;

    const controller = new window.AbortController();

    setTimeout(() => {
        if (!aborted) {
            callback();
        }
    }, Math.random() * 10);

    return {
        cancel: () => {
            aborted = true;
            if (!complete) controller.abort();
        },
    };
}

function run (params, callback) {
    if (numFetch >= config.MAX_PARALLEL_IMAGE_REQUESTS) {
        const queued = {
            params,
            callback,
            cancelled: false,
            cancel() { this.cancelled = true; }
        };
        queue.push(queued);
        return queued;
    }
    numFetch++;

    let advanced = false;
    const advanceFetchQueue = () => {
        if (advanced) return;
        advanced = true;
        numFetch--;
        while (queue.length && numFetch < config.MAX_PARALLEL_IMAGE_REQUESTS) { // eslint-disable-line
            const request = queue.shift();
            const { params, callback, cancelled } = request;
            if (!cancelled) {
                request.cancel = fetch(params, callback).cancel;
            }
        }
    };

    const request = fetch(params, (err: any, data: any) => {
        advanceFetchQueue();

        if (err) {
            callback(err);
        } else if (data) {
            callback(null, data);
        }
    });

    return {
        cancel: () => {
            request.cancel();
            advanceFetchQueue();
        }
    };
}

当我们调用 run 的时候(对应 mapbox 代码中的getImage 函数)发生了什么:

  1. 当正在请求的数量大于等于设置的最大并发数时,我们不会直接调用真实的请求,而是将当前所需的请求 push 到队列中,返回当前队列的标识queued
  2. 如果正在请求的数量小于设置的最大并发数时,首先调用的 numFetch 计数自增,说明当前并发未满。
  3. 发出真实请求。
  4. 真实请求不论成功和失败,执行请求队列;
  5. 当队列不为空并且当前请求数量小于设置的最大并发数时,循环取出队列的第一个请求执行。

这样的实现一般是基于一下几种原因:

  1. 控制并发量,可以避免同时加载大量瓦片,从而减轻服务器和客户端的负担,并提高网站的性能。
  2. 未完成的无用请求可以取消(因为地图我们每次缩放和平移都会请求大量的瓦片,当我们快速的从 0 级放大到 10 级,那么中间层级的大部分瓦片的请求队列都是不必要的,那么可以丢弃或者取消请求)

在以上伪代码实现中MAX_PARALLEL_IMAGE_REQUESTS的默认值是 16,也就是说它允许的并行的图片请求最大为 16,我们也可以通过 map.maxParallelImageRequests=number来更改默认值。

还需要注意的是我们文前提到同一域名下(http1.1)的浏览器最大并发限制为 6,如果我们使用了多个不同域名的影像瓦片图层(256*256)mapbox-gl内部还是限制了最大并发数,这种情况下无法充分利用浏览器的资源并发。

其他

在文章未发出之前,群里 hu 佬 已经基于此开发了一个相关的网络请求调度库 有需要的可以看看。