Leaflet通过Express代理加载第三方瓦片集时出现随机瓦片缺失问题
问题根因分析
- 缺失错误和超时处理:当前代码完全没有对上游请求的异常、响应状态做判断,也没有设置超时时间。一旦Planet瓦片服务器返回错误、超时无响应,回调函数不会给客户端返回任何内容,请求会一直挂起,就是日志里
- - ms的来源。 - 依赖库已废弃:你使用的
request库2020年就已经停止维护,内置的连接池默认并发上限很低,Leaflet批量加载瓦片时很容易触发连接排队,导致后续请求超时。 - 触发上游限流:你固定请求
tiles1.planet.com单个节点,且没有做请求限流,瓦片加载时的高并发很容易触发Planet服务的速率限制,导致部分请求被拒绝或挂起。 - 无参数校验:当前代码没有对路径参数做校验,存在路径注入风险,也可能导致构造出非法的瓦片请求链接,上游无响应。
修复及优化方案
1. 紧急修复现有代码
建议先替换废弃的request为维护中的axios,补全异常处理、超时配置和参数校验,示例代码如下:
const axios = require('axios'); require('dotenv').config(); // 允许的瓦片节点列表,轮询使用分散压力 const TILE_HOSTS = ['tiles1', 'tiles2', 'tiles3']; // 允许的itemtype枚举,做参数校验避免非法请求 const ALLOWED_ITEM_TYPES = ['PSScene4Band', 'PSScene3Band']; exports.get_tileset = async function(req, res, next) { try { const { itemtype, id, z, x, y } = req.params; // 基础参数校验 if (!ALLOWED_ITEM_TYPES.includes(itemtype) || isNaN(z) || isNaN(x) || isNaN(y)) { return res.status(400).send('非法请求参数'); } // 随机选择瓦片节点,降低单节点限流概率 const randomHost = TILE_HOSTS[Math.floor(Math.random() * TILE_HOSTS.length)]; const tileUrl = `https://${randomHost}.planet.com/data/v1/${itemtype}/${id}/${z}/${x}/${y}.png`; const response = await axios.get(tileUrl, { params: { api_key: process.env.PLANET_API_KEY }, responseType: 'arraybuffer', timeout: 10000 // 设置10秒超时,避免请求长期挂起占用连接 }); res.set('Content-Type', 'image/png'); // 增加缓存头,让客户端缓存瓦片减少重复请求 res.set('Cache-Control', 'public, max-age=86400'); res.send(response.data); } catch (err) { // 上游请求失败时返回明确错误,可触发Leaflet自动重试 console.error('瓦片请求失败:', err.message); res.status(404).send('瓦片加载失败'); } }
2. 长期优化方案
- 增加代理层缓存:可以用
lru-cache做内存缓存,把已经请求过的瓦片存在本地,重复请求直接返回,不用再调用上游接口,大幅降低请求量和响应耗时。 - 接入Nginx反向代理:如果访问量较大,建议直接用Nginx做瓦片代理,在Nginx层完成API密钥注入、静态资源缓存、并发限流,性能远高于Node.js服务。
- Leaflet端优化:配置瓦片层的并发请求数限制(
maxConcurrentRequests: 6),同时开启加载失败自动重试,进一步降低瓦片加载失败概率。
内容的提问来源于stack exchange,提问作者Andrew Plowright
相关产品推荐
相关产品推荐

