Кэширование данных представляет собой набор методов, позволяющих сократить количество повторных запросов к серверу, уменьшить объем передаваемого трафика и ускорить отображение трехмерных сцен. В приложениях на базе CesiumJS кэширование играет критически важную роль, поскольку геопространственные данные часто обладают значительным объемом и загружаются из множества источников:
При отсутствии кэширования каждая навигация по карте способна приводить к повторной загрузке одинаковых ресурсов, что увеличивает задержки и создает дополнительную нагрузку на серверную инфраструктуру.
Перед изучением механизмов кэширования необходимо понимать жизненный цикл данных.
Типичная схема работы выглядит следующим образом:
Во время работы приложения могут существовать одновременно несколько уровней кэша:
| Уровень | Назначение |
|---|---|
| Browser Cache | Кэширование HTTP-ответов |
| Memory Cache | Хранение данных в оперативной памяти |
| GPU Cache | Размещение ресурсов в видеопамяти |
| Application Cache | Пользовательские механизмы хранения |
| Service Worker Cache | Offline-кэширование ресурсов |
Каждый уровень решает собственную задачу и влияет на производительность по-разному.
Наиболее распространенный механизм основан на стандартных возможностях браузера.
Когда сервер возвращает тайл, в ответе могут присутствовать специальные заголовки:
Cache-Control: public, max-age=86400
ETag: "a8d32bc"
Last-Modified: Tue, 21 Jan 2025 12:00:00 GMT
После получения такого ответа браузер сохраняет данные локально.
При повторном обращении:
const imageryLayer = new Cesium.ImageryLayer(
new Cesium.UrlTemplateImageryProvider({
url: '/tiles/{z}/{x}/{y}.png'
})
);
загрузка может происходить непосредственно из браузерного кэша без обращения к сети.
Преимущества:
Недостаток заключается в том, что эффективность полностью зависит от корректной настройки серверных заголовков.
Карты и спутниковые снимки обычно поставляются в виде растровых тайлов.
Например:
const provider = new Cesium.UrlTemplateImageryProvider({
url: '/satellite/{z}/{x}/{y}.jpg'
});
viewer.imageryLayers.addImageryProvider(provider);
После загрузки изображения Cesium сохраняет текстуры во внутренних структурах.
Когда пользователь возвращается к ранее просмотренной области, движок может повторно использовать уже загруженные данные без повторного получения файлов.
Особенно эффективно это работает при:
Наборы 3D Tiles часто содержат тысячи или миллионы объектов.
Пример подключения:
const tileset = await Cesium.Cesium3DTileset.fromUrl(
'/tileset/tileset.json'
);
viewer.scene.primitives.add(tileset);
После загрузки тайлы помещаются во внутренний кэш набора.
Cesium автоматически:
Внутренний алгоритм работает по принципу ограничения объема памяти.
Для управления потреблением памяти используется свойство:
tileset.cacheBytes = 512 * 1024 * 1024;
В данном случае устанавливается лимит:
512 МБ
При превышении ограничения движок начинает выгружать неиспользуемые тайлы.
Дополнительно существует параметр:
tileset.maximumCacheOverflowBytes =
256 * 1024 * 1024;
Он позволяет временно превышать установленный лимит.
Пример:
tileset.cacheBytes = 1024 * 1024 * 1024;
tileset.maximumCacheOverflowBytes =
512 * 1024 * 1024;
Такой подход уменьшает количество повторных загрузок во время активной навигации.
Cesium применяет стратегии удаления данных из памяти.
Основная идея:
Концептуально процесс напоминает алгоритм LRU (Least Recently Used).
Последовательность:
Загрузка тайла
↓
Использование
↓
Помещение в кэш
↓
Превышение лимита
↓
Удаление старых тайлов
Благодаря этому память расходуется более эффективно.
Terrain является одним из самых ресурсоемких типов данных.
Подключение:
const terrain = await Cesium.CesiumTerrainProvider.fromUrl(
'/terrain'
);
viewer.terrainProvider = terrain;
Высотные тайлы загружаются по мере необходимости и остаются в памяти до момента вытеснения.
Кэширование Terrain обеспечивает:
Особенно заметен эффект при полетах над одной и той же территорией.
При загрузке моделей:
const model = await Cesium.Model.fromGltfAsync({
url: '/models/aircraft.glb'
});
viewer.scene.primitives.add(model);
Cesium сохраняет:
Если одна и та же модель используется несколько раз:
const model1 = await Cesium.Model.fromGltfAsync({
url: 'tank.glb'
});
const model2 = await Cesium.Model.fromGltfAsync({
url: 'tank.glb'
});
часть ресурсов может переиспользоваться, что уменьшает потребление памяти и сетевой трафик.
Иногда данные поступают из внешнего API.
Например:
async function loadAircraft(id) {
const response = await fetch(
`/api/aircraft/${id}`
);
return response.json();
}
Без кэширования каждый вызов приводит к новому запросу.
Простейший вариант:
const cache = new Map();
async function loadAircraft(id) {
if (cache.has(id)) {
return cache.get(id);
}
const response = await fetch(
`/api/aircraft/${id}`
);
const data = await response.json();
cache.set(id, data);
return data;
}
Теперь данные загружаются только один раз.
Часто данные устаревают.
Для этого вводится TTL (Time To Live).
const cache = new Map();
async function getData(id) {
const item = cache.get(id);
if (
item &&
Date.now() - item.time < 60000
) {
return item.data;
}
const response =
await fetch(`/api/data/${id}`);
const data =
await response.json();
cache.set(id, {
data,
time: Date.now()
});
return data;
}
В данном примере информация считается актуальной одну минуту.
GeoJSON-файлы могут содержать большое количество объектов.
Пример загрузки:
const dataSource =
await Cesium.GeoJsonDataSource.load(
'/data/roads.geojson'
);
viewer.dataSources.add(dataSource);
Если набор используется повторно, разумно сохранять ссылку:
const geoJsonCache = {};
async function getRoads() {
if (geoJsonCache.roads) {
return geoJsonCache.roads;
}
geoJsonCache.roads =
await Cesium.GeoJsonDataSource.load(
'/data/roads.geojson'
);
return geoJsonCache.roads;
}
Такой подход избавляет от повторного парсинга файла.
Service Worker позволяет организовать полноценный локальный слой хранения.
Регистрация:
navigator.serviceWorker.register(
'/service-worker.js'
);
Пример обработки запросов:
self.addEventListener(
'fetch',
event => {
event.respondWith(
caches.match(event.request)
.then(response => {
return response ||
fetch(event.request);
})
);
}
);
В результате:
Иногда требуется работа без подключения к интернету.
В этом случае тайлы заранее сохраняются:
caches.open('tiles-cache')
.then(cache => {
return cache.addAll([
'/tiles/0/0/0.png',
'/tiles/1/0/0.png',
'/tiles/1/0/1.png'
]);
});
После этого данные могут использоваться локально.
Подобный подход применяется в:
Для предотвращения утечек памяти необходимо периодически удалять устаревшие данные.
Пример:
for (const [key, value] of cache) {
if (
Date.now() - value.time >
300000
) {
cache.delete(key);
}
}
Также можно полностью очистить структуру:
cache.clear();
Особенно важно делать это при длительной работе приложения.
При работе с большими сценами рекомендуется контролировать объем загруженных данных.
Для 3D Tiles доступна диагностика:
console.log(
tileset.totalMemoryUsageInBytes
);
Получение значения:
284563219
означает фактический объем памяти, занимаемый тайлами.
На основе этих данных можно динамически изменять настройки кэширования.
Слишком маленький кэш приводит к постоянным повторным загрузкам:
tileset.cacheBytes =
64 * 1024 * 1024;
Слишком большой кэш способен вызвать нехватку памяти:
tileset.cacheBytes =
4 * 1024 * 1024 * 1024;
Практика показывает следующие ориентиры:
| Тип приложения | Размер кэша |
|---|---|
| Простая карта | 128–256 МБ |
| Городская сцена | 512 МБ |
| Большой цифровой двойник | 1–2 ГБ |
| Инженерные модели | 2–4 ГБ |
Конкретные значения зависят от объема данных и характеристик оборудования.
Для уменьшения задержек можно заранее загружать данные.
Пример:
viewer.camera.flyTo({
destination: destination,
complete: async () => {
await tileset.readyPromise;
tileset.preloadFlightDestinations = true;
}
});
Предварительная загрузка позволяет получать необходимые тайлы до момента их фактического отображения.
Преимущества:
Для Imagery Layers
Для Terrain
Для 3D Tiles
cacheBytes;Для пользовательских API
Для офлайн-режима
Грамотно реализованное кэширование позволяет значительно сократить сетевые задержки, повысить плавность навигации, уменьшить нагрузку на серверы и обеспечить стабильную работу сложных геоинформационных приложений на базе CesiumJS даже при обработке больших объемов пространственных данных.