【免费下载链接】deflock
Crowdsourced tool for locating and reporting ALPRs
DeFlock 是一个众包收集 ALPR(车牌识别摄像头)位置与举报的开源工具,它配套的地理编码 API(Geocoding API)基于 Fastify 封装了 OpenStreetMap 的 Nominatim 服务。为了扛住高频的地理编码查询并绕开 Nominatim 的访问频率限制,DeFlock 用磁盘缓存 + 本地 ZIP 邮编数据集两套加速手段,让邮政编码等常见查询在本地毫秒级返回,无需再发网络请求。
为什么地理编码 API 需要加速
DeFlock 地图支持用户在搜索框中输入地址、城市或 ZIP 邮编来定位 ALPR 摄像头。这类请求最终都会打到 API 的/geocode接口,再转发给 Nominatim 公共地理编码服务。直接用会有两个痛点:
- 慢且不稳定:Nominatim 是公共服务,响应时间与可用性无法保证;
- 有使用频率限制:官方明确要求客户端控制请求量,且要求携带自定义
User-Agent。
所以 DeFlock 在 server.ts 中实现的地理编码链路做了一层"漏斗"式优化:先分类查询、再查本地数据、最后才走网络请求并落盘缓存。
第一层:查询分类,能本地答的绝不发请求
入口在 GeoQueryClassifier.ts 中的classifyGeoQuery()函数,它用一组正则把用户输入归类为 7 种地理查询类型:
| 类型 | 示例 | 说明 |
|---|---|---|
zip_code | 90210 | 5 位美国 ZIP 邮编 |
coordinates | 34.0522, -118.2437 | 经纬度坐标对 |
address | 123 Main St, Springfield | 街道地址 |
city_state | Portland, OR | 城市 + 两字母州缩写 |
state | Oregon/OR | 州名或缩写 |
city | Portland | 纯城市名 |
other | 其他 | 兜底分类 |
分类结果还有第二个用途:server.ts 会把geocode.source(local_zip或nominatim)写入 OpenTelemetry 指标,方便统计"本地数据集到底拦截了多少请求"。
第二层:本地 ZIP 数据集,邮编查询零网络请求
美国 ZIP 邮编查询是地理编码中最高频的场景之一,但让 Nominatim 每次都跑一遍网络请求既浪费又慢。DeFlock 的做法是内置一份美国邮编数据集 zipcodes-us.json(仅 1.8MB,未压缩格式),结构非常精简:
{"90210": [34.0959, -118.4369, "Beverly Hills", "CA"], "10001": [40.7484, -73.9967, "New York", "NY"]}ZipCodeService.ts 在模块加载时一次性同步读入内存,lookupZipCode()命中后直接构造出与 Nominatim 返回结构完全兼容的结果对象(addresstype、lat、lon、address等字段),前端代码无需区分数据来自哪里。
关键短路逻辑在 NominatimClient.ts 的geocodePhrase()开头:
if (classifyGeoQuery(query) === 'zip_code') { const zipResult = lookupZipCode(query.trim()); return zipResult ? [zipResult] : []; }也就是说:只要是 ZIP 查询,请求根本不会离开服务器本地,Nominatim 完全不被打扰。
第三层:Nominatim 磁盘缓存,城市/地址查询也提速
对于城市、地址类查询,DeFlock 用cache-manager+cache-manager-fs-hash搭建了一个磁盘文件缓存,配置见 NominatimClient.ts:
const cache: Cache = createCache({ stores: [new DiskStore({ path: '/tmp/nominatim-cache', ttl: 3600 * 24, // 缓存 24 小时 maxsize: 1000 * 1000 * 100, // 上限 100MB subdirs: true, zip: false, })] });几个值得注意的细节:
- 缓存键:
geocode:${query},同一个查询词 24 小时内只打一次 Nominatim; - 磁盘而非内存:服务重启后缓存不丢失,多实例部署也可共享;
- 结果过滤:geocodeSingleResult() 还会按查询类型做二次筛选——"城市, 州"形式的查询优先取
city/town/village级别结果,含邮编的查询优先取postcode结果,避免返回不精确的地点; - 响应级缓存:server.ts 还设置了
Cache-Control: public, max-age=86400, s-maxage=86400响应头,浏览器和 CDN 可以再缓存 1 天,把请求进一步挡在 API 之前。
本地跑起来:两步启动地理编码 API
API 使用 Bun。在api/目录下执行:
bun install bun dev开发模式下服务默认监听3420端口(生产为3000)。然后就可以直接体验加速效果:
curl "http://localhost:3420/geocode?query=90210" # 本地 ZIP 数据集,毫秒级 curl "http://localhost:3420/geocode?query=Portland" # 首次走 Nominatim,之后走磁盘缓存其他端点还包括/sponsors/github(需要GITHUB_TOKEN环境变量)和/healthcheck,完整说明见 api/README.md。
可观测性:用指标验证加速效果
DeFlock API 接入了完整的 OpenTelemetry 链路:telemetry.ts 定义日志与指标导出,otelcol/config.yaml 配置 OpenTelemetry Collector 将数据转发到 Grafana Cloud(并自动丢弃 404 日志降噪)。
结合前文提到的geocode.source属性(local_zip/nominatim)与http.server.requests.total请求计数器,你可以在监控面板里直接回答两个问题:
- 本地 ZIP 数据集拦截了多大比例的地理编码请求?
- 磁盘缓存命中后,API 上游 Nominatim 的错误率(
upstream_error:nominatim)是否下降?
总结:三层漏斗的设计精髓
| 层级 | 手段 | 命中场景 | 数据来源 |
|---|---|---|---|
| 1 | 查询分类 | 所有请求 | GeoQueryClassifier.ts |
| 2 | 本地 ZIP 数据集 | 5 位邮编 | zipcodes-us.json |
| 3 | 磁盘缓存 + 响应缓存 | 重复城市/地址查询 | NominatimClient.ts |
这套"分类 → 本地数据集 → 磁盘缓存"的漏斗模式并不复杂,但组合效果显著:最高频的邮编查询完全本地化,其余查询 24 小时内只消耗一次 Nominatim 配额。对于任何需要包装 Nominatim 等公共地理编码服务的自建项目,这都是一个低成本、易复制的加速方案。🚀
【免费下载链接】deflock
Crowdsourced tool for locating and reporting ALPRs
相关推荐
Windows Defender故障诊断与修复:深度技术解决方案
Windows Defender故障诊断与修复:深度技术解决方案 当Windows安全中心出现异常状态时,系统安全防护机制面临严重威胁。本文提供一套完整的"问题
memorycache 使用指南:仓颉(Cangjie)语言 LRU 本地磁盘缓存与内存缓存实战
memorycache 使用指南:仓颉(Cangjie)语言 LRU 本地磁盘缓存与内存缓存实战 memorycache 是一个使用仓颉(Cangjie)语言编
缓存抽象缓存DeFlock地图秒开揭秘:ALPR数据经纬度分片加载与缓存设计
DeFlock地图秒开揭秘:ALPR数据经纬度分片加载与缓存设计 DeFlock 是一个开源的 ALPR(自动车牌识别) 众包地图工具,帮助用户定位和上报遍布美
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考