最近接手一个物流配送优化项目,需要计算仓库到各个配送点的最优路径。刚开始直接用经纬度计算直线距离,结果被老板骂得狗血淋头——实际道路距离比直线距离多了将近40%。更麻烦的是,这个系统最终要部署在政府内网,根本没法调用高德、百度这些在线地图API。
这就是典型的离线地图需求场景。比如:
经过一周的折腾,终于用Graphhopper搭建出稳定的离线路径规划服务。整个过程踩坑无数,光重装系统就搞了3次。下面就把这些血泪经验整理成保姆级教程,手把手带你避开所有雷区。
先说说我的翻车经历:第一次尝试用公司配的4G内存笔记本处理全国路网数据,直接卡到死机。后来换成16G内存的台式机才跑通。建议配置:
| 数据规模 | 最低内存 | 建议内存 | 硬盘空间 |
|---|---|---|---|
| 省级路网 | 8GB | 16GB | 50GB |
| 全国路网 | 16GB | 32GB | 200GB |
| 跨国路网 | 32GB+ | 64GB+ | 1TB+ |
实测发现:处理北京市路网数据(约1.2GB的pbf文件)需要约6GB内存,处理时间约25分钟
官方文档说只需要Java 8+和Maven,但实际部署时会发现一堆隐藏依赖:
bash复制# 必须安装的依赖
sudo apt-get install -y \
git \
openjdk-11-jdk \
maven \
wget \
unzip
# 容易被忽略的依赖
sudo apt-get install -y \
gdal-bin \ # 处理地理数据
osmctools \ # OSM数据转换
spatialite-bin # 空间数据库支持
Windows用户特别注意:
新手最容易栽在数据源选择上。推荐这两个稳定源:
bash复制wget https://download.geofabrik.de/asia/china-latest.osm.pbf
bash复制wget https://download.bbbike.org/osm/bbbike/Shanghai/Shanghai.osm.pbf
血泪教训:千万别用.osm格式的原始数据!我下载了个20GB的.osm文件,解析了8小时最后内存溢出。应该优先选择压缩过的.pbf格式。
拿到数据后别急着导入,先用这个命令检查下:
bash复制osmconvert china-latest.osm.pbf --out-statistics
输出示例:
code复制file size: 3.2GB
nodes: 253,432,123
ways: 28,765,432
relations: 1,234,567
如果发现数据量过大,可以用osmium工具裁剪:
bash复制osmium extract -b 116.2,39.8,116.6,40.2 china-latest.osm.pbf -o beijing.pbf
这个命令提取北京五环内的路网数据,文件大小从3.2GB降到180MB。
千万别直接clone master分支!我一开始没注意,结果发现缺少关键脚本:
bash复制git clone -b stable https://github.com/graphhopper/graphhopper.git
cd graphhopper
修改config.yml时这几个参数最关键:
yaml复制graph.dataaccess: RAM_STORE # 小数据用内存模式更快
graph.vehicles: car,bike,foot # 按需保留
graph.elevation.provider: srtm # 需要地形数据时开启
首次构建建议增加内存参数:
bash复制export JAVA_OPTS="-Xmx8g -Xms8g"
./graphhopper.sh build europe_germany_berlin.pbf
启动服务的正确姿势:
bash复制# 生产环境建议用nohup
nohup ./graphhopper.sh web europe_germany_berlin.pbf > log.txt 2>&1 &
# 测试环境可以用前台模式
./graphhopper.sh web europe_germany_berlin.pbf
常见启动报错解决方案:
server.portchmod -R 755 data在config.yml中添加这些参数可以显著提升性能:
yaml复制graph.encoded_values: speed,access
graph.flag_encoders: car|turn_costs=true
graph.bytes_for_flags: 4
graph.location_index_resolution: 500
默认只有英文,要添加中文提示需要:
bash复制wget https://raw.githubusercontent.com/graphhopper/graphhopper/master/web/src/main/resources/com/graphhopper/resources/zh_CN.properties
yaml复制i18n.locale: zh_CN
Python调用示例(带超时重试):
python复制import requests
from retrying import retry
@retry(stop_max_attempt_number=3, wait_fixed=2000)
def get_route(points):
url = f"http://localhost:8989/route?point={points[0]}&point={points[1]}&profile=car"
try:
res = requests.get(url, timeout=5)
return res.json()['paths'][0]
except Exception as e:
print(f"请求失败: {e}")
raise
# 调用示例
route_info = get_route(["39.915,116.404", "31.230,121.473"])
print(f"距离:{route_info['distance']/1000}公里")
print(f"耗时:{route_info['time']//60000}分钟")
现象:执行./graphhopper.sh web后立即退出
排查步骤:
logs/graphhopper.logOutOfMemoryError:增加JAVA_OPTS内存设置NoSuchFileException:检查pbf文件路径UnsupportedOperationException:可能是数据文件损坏现象:返回的路径明显绕远路
解决方案:
yaml复制graph.flag_encoders: car|speed_factor=1.5
yaml复制routing.ch.discovery: astar
routing.ch.landmarks: 16
现象:运行一段时间后响应变慢
优化方案:
yaml复制graph.dataaccess: MMAP
graph.mmap: read
bash复制./graphhopper.sh precompute bbox=116.3,39.8,116.5,40.0
修改config.yml支持多种出行方式:
yaml复制graph.vehicles: car,bike,foot
profiles:
- name: car
vehicle: car
weighting: fastest
- name: bike
vehicle: bike
weighting: shortest
调用时指定profile参数:
python复制# 骑车路线
requests.get("http://localhost:8989/route?point=...&profile=bike")
需要下载SRTM高程数据:
bash复制wget https://srtm.csi.cgiar.org/wp-content/uploads/files/srtm_5x5/TIFF/srtm_12_03.zip
unzip srtm_12_03.zip -d elevation/
配置高程数据处理:
yaml复制graph.elevation.provider: srtm
graph.elevation.cache_dir: elevation/
graph.elevation.srtm.resolution: 30m
虽然Graphhopper是离线服务,但可以接入实时交通数据:
csv复制timestamp,edge_id,speed
1634567890,12345,30
bash复制./graphhopper.sh web data.osm.pbf --traffic traffic.csv
关键日志位置:
logs/graphhopper.log:主服务日志logs/access.log:API访问日志用awk统计高频请求:
bash复制awk '{print $7}' logs/access.log | sort | uniq -c | sort -nr
编写监控脚本check_health.sh:
bash复制#!/bin/bash
response=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8989)
if [ "$response" -ne 200 ]; then
echo "服务异常!HTTP状态码: $response" | mail -s "Graphhopper告警" admin@example.com
systemctl restart graphhopper
fi
添加到crontab:
bash复制*/5 * * * * /path/to/check_health.sh
推荐增量更新方式:
bash复制osmupdate old.pbf new.pbf
bash复制./graphhopper.sh build new.pbf --update old.pbf
建议更新频率: