Отрисовка глобусов, 3D-моделей, тайлов местности, спутниковых снимков и векторных данных требует постоянной загрузки большого количества ресурсов по сети. Одновременное выполнение сотен HTTP-запросов приводит к перегрузке браузера, росту задержек и ухудшению производительности приложения.
Для решения этой проблемы в CesiumJS реализован механизм Request Scheduling — централизованная система управления сетевыми запросами. Она контролирует:
Практически каждая подсистема движка использует планировщик запросов:
В основе механизма находится класс:
Cesium.RequestScheduler
Он представляет собой глобальный диспетчер, через который проходят запросы к удалённым ресурсам.
Упрощённая схема работы выглядит следующим образом:
Источник данных
↓
Request
↓
RequestScheduler
↓
Очередь запросов
↓
Проверка лимитов
↓
Выполнение
↓
Получение данных
Когда компонент Cesium хочет загрузить ресурс, он не обращается
напрямую к fetch() или XMLHttpRequest.
Вместо этого создаётся объект:
Cesium.Request
который передаётся в планировщик.
Каждый сетевой запрос описывается экземпляром класса:
const request = new Cesium.Request({
url: url
});
На практике используется гораздо больше параметров.
Пример:
const request = new Cesium.Request({
url: "tiles/15/1203/987.b3dm",
throttle: true,
throttleByServer: true,
type: Cesium.RequestType.TILES3D
});
Основные свойства:
| Свойство | Назначение |
|---|---|
| url | адрес ресурса |
| throttle | участвует в ограничении количества запросов |
| throttleByServer | учитывает лимиты сервера |
| priority | числовой приоритет |
| priorityFunction | функция вычисления приоритета |
| type | тип запроса |
| serverKey | ключ сервера |
Современные браузеры ограничивают число одновременных соединений с одним сервером.
Cesium дополнительно вводит собственные лимиты.
Глобальное ограничение:
Cesium.RequestScheduler.maximumRequests
По умолчанию:
console.log(
Cesium.RequestScheduler.maximumRequests
);
Типичное значение:
50
Если количество активных запросов достигло лимита:
50 активных запросов
+
новый запрос
↓
помещается в очередь
Выполнение начнётся только после освобождения слота.
Даже если глобальный лимит не исчерпан, один сервер может быть перегружен.
Для этого существует параметр:
Cesium.RequestScheduler.maximumRequestsPerServer
Пример:
console.log(
Cesium.RequestScheduler.maximumRequestsPerServer
);
Обычно:
18
Сценарий:
server-a.com
18 активных запросов
server-b.com
3 активных запроса
Новый запрос к:
server-a.com
будет ожидать освобождения соединения, несмотря на наличие свободных глобальных слотов.
Не все данные одинаково важны.
Например:
Для этого используется система приоритетов.
Чем меньше значение:
priority
тем выше приоритет.
Пример:
const request = new Cesium.Request({
priority: 1
});
Более низкий приоритет:
const request = new Cesium.Request({
priority: 100
});
Очередь будет обслуживаться следующим образом:
priority 1
priority 2
priority 5
priority 10
priority 100
Статический приоритет подходит не всегда.
Положение камеры постоянно меняется:
Поэтому Cesium позволяет вычислять приоритет динамически.
Пример:
const request = new Cesium.Request({
priorityFunction: function () {
return distanceToCamera;
}
});
При каждом обновлении очередь получает актуальное значение.
Чем ближе объект к камере:
меньше расстояние
↓
выше приоритет
↓
раньше загрузка
Для оптимизации движок классифицирует запросы.
Используется перечисление:
Cesium.RequestType
Основные значения:
Cesium.RequestType.TERRAIN
Cesium.RequestType.IMAGERY
Cesium.RequestType.TILES3D
Cesium.RequestType.OTHER
Пример:
const request = new Cesium.Request({
type: Cesium.RequestType.IMAGERY
});
Такая классификация помогает подсистемам корректно управлять загрузкой данных.
Троттлинг предотвращает неконтролируемое создание запросов.
Свойство:
throttle
указывает, должен ли запрос участвовать в системе ограничений.
Пример:
const request = new Cesium.Request({
throttle: true
});
Если все слоты заняты:
запрос
↓
очередь
↓
ожидание
Если указать:
throttle: false
запрос попытается выполниться немедленно.
Пример:
const request = new Cesium.Request({
throttle: false
});
Такой режим применяется редко и требует осторожности.
Свойство:
throttleByServer
активирует учёт лимитов конкретного хоста.
Пример:
const request = new Cesium.Request({
throttleByServer: true
});
Логика работы:
server.com
↓
проверка числа соединений
↓
если лимит достигнут
↓
ожидание
Для контроля нагрузки Cesium определяет уникальный идентификатор сервера.
Пример ключа:
assets.example.com:443
или
localhost:8080
Получить ключ можно следующим образом:
const key =
Cesium.RequestScheduler.getServerKey(
"https://assets.example.com"
);
console.log(key);
Результат:
assets.example.com:443
Этот ключ используется внутренними структурами планировщика.
Когда лимиты исчерпаны, запрос помещается в очередь.
Упрощённая схема:
Активные запросы
─────────────────
1
2
3
...
50
Очередь
─────────────────
51
52
53
54
...
После завершения активного запроса:
освобождение слота
↓
извлечение запроса
↓
запуск выполнения
Очередь постоянно пересортировывается согласно приоритетам.
Во время навигации камера может быстро перемещаться.
Например:
континент
↓
страна
↓
город
↓
улица
Пока загружаются тайлы континента, пользователь уже рассматривает улицу.
Загрузка старых тайлов становится бессмысленной.
Cesium автоматически отменяет подобные запросы:
запрос больше не нужен
↓
удаление из очереди
↓
освобождение ресурсов
Это значительно уменьшает сетевую нагрузку.
Низкоуровневый интерфейс выглядит следующим образом:
Cesium.RequestScheduler.request(
request
);
Пример:
const request = new Cesium.Request({
url: "data.json",
throttle: true
});
const promise =
Cesium.RequestScheduler.request(
request
);
Метод возвращает Promise.
promise.then(function(data) {
console.log(data);
});
На практике большинство сетевых операций выполняется через класс:
Cesium.Resource
Он автоматически интегрирован с планировщиком запросов.
Пример:
const resource =
new Cesium.Resource({
url: "tileset.json"
});
Загрузка:
resource.fetchJson();
Внутри будет использован механизм Request Scheduling.
Подсистема 3D Tiles наиболее активно использует планировщик.
При движении камеры движок постоянно определяет:
Для каждого тайла вычисляется приоритет.
Упрощённая последовательность:
Камера
↓
Видимые тайлы
↓
Расчёт SSE
↓
Приоритет
↓
Очередь запросов
↓
Загрузка
Ближайшие тайлы получают преимущество перед удалёнными.
Поставщики рельефа также используют очередь запросов.
Типичный сценарий:
камера движется
↓
нужны новые terrain tiles
↓
создание запросов
↓
планировщик
↓
получение данных высот
Благодаря этому отсутствует лавинообразная загрузка при быстром перемещении по карте.
Спутниковые снимки могут состоять из миллионов тайлов.
При масштабировании:
zoom in
↓
старые тайлы устаревают
↓
новые получают высокий приоритет
↓
загрузка только нужных изображений
Без планировщика браузер легко мог бы сформировать сотни лишних запросов.
Количество активных запросов доступно через внутренние структуры движка.
Во время отладки часто используются инструменты браузера:
Chrome DevTools
→ Network
или
Firefox Developer Tools
→ Network
Здесь можно наблюдать:
При необходимости лимиты можно изменить.
Увеличение общего числа запросов:
Cesium.RequestScheduler.maximumRequests =
100;
Изменение лимита на сервер:
Cesium.RequestScheduler.maximumRequestsPerServer =
30;
Подобные настройки требуют тестирования.
Слишком высокие значения способны привести к:
Не отключать троттлинг без необходимости
throttle: true
остается оптимальным вариантом практически для всех сценариев.
Использовать динамические приоритеты
Особенно для больших наборов пространственных данных.
priorityFunction
позволяет концентрировать ресурсы на области текущего обзора.
Следить за количеством серверов
Распределение данных между несколькими доменами помогает избежать локальных ограничений соединений.
Минимизировать объём ресурсов
Даже идеально настроенный планировщик не компенсирует чрезмерно большие тайлы и модели.
Использовать встроенные механизмы Cesium
Классы:
Cesium.Resource
Cesium.Cesium3DTileset
Cesium.ImageryLayer
Cesium.TerrainProvider
уже интегрированы с системой Request Scheduling и автоматически используют оптимальные алгоритмы управления сетевыми запросами.