🇬🇧 Read the English version of this article on tanhdev.com

GraphHopper Distance Matrix Là Gi?

Answer-first: GraphHopper distance matrix là API endpoint /matrix thuộc thư viện định tuyến mã nguồn mở (open-source routing engine) GraphHopper. Nó tiếp nhận thông tin tọa độ của N điểm trạm và trả về một ma trận kết quả kích cỡ N×N chứa toàn bộ dữ liệu thời gian di chuyển (tính theo giây) và độ dài khoảng cách (tính theo mét) dựa trên mạng lưới giao thông thực tế được cung cấp bởi bản đồ OpenStreetMap — hoàn toàn miễn phí khi bạn chọn cách tự triển khai (self-hosted). Với quy mô 100 điểm dừng giao hàng (delivery stops), cỗ máy này dễ dàng xử lý toàn bộ 10,000 cặp lộ trình chỉ trong khoảng thời gian dưới 50 mili-giây (50ms) ngay trên một máy chủ VPS thông thường.

Nội dung dưới đây trình bày chi tiết mọi kỹ thuật bạn cần để vận hành GraphHopper distance matrix hiệu quả trong môi trường production: triển khai Docker, sử dụng /matrix API, sử dụng Custom Models để thiết lập quy tắc cho xe tải/xe máy, dựng tầng cache H3-based cho Redis, và cuối cùng là đánh giá trực quan và so sánh cùng OSRM, Valhalla, và Google Maps (để hiểu sâu hơn về việc chọn lựa routing engine, hãy xem bài So sánh Kiến trúc OSRM và GraphHopper).


Tại Sao Nên Chọn GraphHopper Distance Matrix?

Khi lựa chọn các công cụ định tuyến mã nguồn mở nhằm tính toán khoảng cách ma trận (route distance matrix computation), người ta thường nhắc đến ba giải pháp chính là Haversine (tính toán khoảng cách theo đường chim bay), OSRM (viết bằng C++, tốc độ rất cao nhưng cấu hình cứng - rigid profiles), và GraphHopper (viết bằng Java, độ linh hoạt cao nhờ tính năng Custom Models). Bên cạnh đó là giải pháp dịch vụ API trả phí (Google Maps, HERE, Mapbox).

Yếu Tố (Criterion)GraphHopperOSRMHaversineGoogle Maps
Chi Phí (Cost)Miễn phí (self-hosted)Miễn phí (self-hosted)Miễn phí$0.005/element
Độ Chuẩn Xác (Accuracy)Theo đường nhựa ✅Theo đường nhựa ✅Đường chim bay ❌Đường nhựa + Kẹt xe ✅
Thời Gian Xử Lý 100×100~50ms~20ms<1msTừ 500ms+
Thay đổi quy tắc định tuyến khi Runtime✅ Hỗ trợ Custom Models❌ Không (Cần recompile + Lua)Miễn bàn (N/A)Cần cấu hình thêm theo gói
Hỗ trợ SDK Java/Python✅ Hỗ trợ SDK Java NativeGọi qua HTTP APIHàng tích sẵn NativeGọi qua HTTP API
Triển Khai Docker✅ Có ảnh chính thức (Official)✅ Có ảnh chính thức (Official)N/AN/A
Phù hợp nhất cho (Best for)Đội xe hỗn hợp nhiều quy tắcTối ưu tốc độ cho một loại phương tiệnLọc nhanh các vị trí ban đầuCần dữ liệu giao thông thực tế (ETA)

Kinh Nghiệm Rút Ra (Rule of thumb): Nên dùng GraphHopper trong trường hợp hệ thống cần xử lý nhiều loại phương tiện với các quy tắc định tuyến (routing rules) khác nhau (ví dụ: giới hạn tải trọng xe tải, quy tắc di chuyển cho xe máy, hoặc tránh trạm thu phí). Trong khi đó, OSRM là lựa chọn tối ưu khi hệ thống chỉ phục vụ duy nhất một loại phương tiện và yêu cầu tốc độ phản hồi tối đa.


Hướng Dẫn Khởi Động Nhanh (Quick Start): GraphHopper Distance Matrix Với Docker

Bước 1: Khởi Động GraphHopper Server

Để chạy, GraphHopper sử dụng dữ liệu bản đồ dạng .osm.pbf từ OpenStreetMap. Bạn có thể tải miễn phí các bản cắt phân khu (regional extracts) từ trang Geofabrik.

# Tạo thư mục chứa dữ liệu (Create a data directory)
mkdir -p ./graphhopper-data

# Khởi chạy container GraphHopper — tự động tải bản đồ OSM Việt Nam
docker run -d \
  --name graphhopper \
  -p 8989:8989 \
  -v $(pwd)/graphhopper-data:/data \
  israelhikingmap/graphhopper:latest \
  --url https://download.geofabrik.de/asia/vietnam-latest.osm.pbf \
  --host 0.0.0.0

# Theo dõi log — quá trình xử lý đồ thị lần đầu mất khoảng 5-15 phút
docker logs -f graphhopper

Chờ cho đến khi xuất hiện dòng log: Started server at HTTP 0.0.0.0:8989

Bước 2: Thử Nghiệm Gọi Matrix API

# Gửi yêu cầu tính toán ma trận 3×3 cho 3 địa điểm tại TP.HCM
curl -X POST http://localhost:8989/matrix \
  -H "Content-Type: application/json" \
  -d '{
    "points": [
      [106.7011, 10.7712],
      [106.7100, 10.7780],
      [106.6980, 10.7650]
    ],
    "profile": "car",
    "out_arrays": ["times", "distances"],
    "fail_fast": false
  }'

⚠️ Lưu ý về định dạng tọa độ GeoJSON: GraphHopper sử dụng thứ tự tọa độ [kinh độ longitude, vĩ độ latitude] (thay vì [lat, lng]). Đây là lỗi phổ biến khi chuyển từ các thư viện khác sang.

Kết quả phản hồi (Response):

{
  "times": [[0, 320, 185], [315, 0, 410], [180, 405, 0]],
  "distances": [[0, 2100, 1350], [2050, 0, 2900], [1300, 2880, 0]],
  "info": {"took": 12, "copyrights": ["OpenStreetMap contributors"]}
}
  • Nhãn times: ma trận N×N chứa thời gian đo bằng giây (seconds)
  • Nhãn distances: ma trận N×N chỉ khoảng cách tính bằng mét (meters)
  • Đường chéo chính (Diagonal) có giá trị 0 (khoảng cách và thời gian từ một điểm đến chính nó).

Xây Dựng Python Client Dành Cho Production

import requests
from dataclasses import dataclass
from typing import Optional

@dataclass
class Location:
    lat: float
    lng: float
    label: Optional[str] = None

@dataclass
class DistanceMatrix:
    durations: list[list[int]]   # tính theo giây (seconds), ma trận N×N
    distances: list[list[int]]   # tính theo mét (meters), ma trận N×N

class GraphHopperClient:
    """
    Python client cho GraphHopper Distance Matrix API đạt chuẩn Production-ready.
    Tự động xử lý định dạng [lng, lat], quản lý lỗi và hỗ trợ cơ chế thử lại (retries).
    """

    def __init__(self, base_url: str = "http://localhost:8989", profile: str = "car"):
        self.base_url = base_url.rstrip("/")
        self.profile = profile
        self.session = requests.Session()
        self.session.headers.update({"Content-Type": "application/json"})

    def get_matrix(self, locations: list[Location], timeout: int = 30) -> DistanceMatrix:
        """
        Tính toán ma trận khoảng cách N×N cho danh sách các địa điểm truyền vào.

        Tham số (Args):
            locations: Danh sách đối tượng Location (chứa lat/lng).
            timeout:   Thời gian chờ tối đa cho HTTP request (tính theo giây).

        Kết quả (Returns):
            Đối tượng DistanceMatrix chứa 'durations' (giây) và 'distances' (mét).

        Ngoại lệ (Raises):
            requests.HTTPError: Xảy ra khi GraphHopper trả về lỗi HTTP.
            ValueError: Xảy ra nếu danh sách truyền vào có ít hơn 2 địa điểm.
        """
        if len(locations) < 2:
            raise ValueError("Cần tối thiểu 2 địa điểm để tính toán ma trận khoảng cách.")

        # Cẩn thận: GraphHopper ăn theo chuẩn [lng, lat] — định dạng GeoJSON
        points = [[loc.lng, loc.lat] for loc in locations]

        payload = {
            "points": points,
            "profile": self.profile,
            "out_arrays": ["times", "distances"],
            "fail_fast": False,   # Trả về kết quả cho các cặp hợp lệ ngay cả khi có cặp không tìm được đường đi
        }

        response = self.session.post(
            f"{self.base_url}/matrix",
            json=payload,
            timeout=timeout
        )
        response.raise_for_status()
        data = response.json()

        return DistanceMatrix(
            durations=data["times"],
            distances=data["distances"],
        )


# --- Khúc rải múa trình diễn cách xài (Usage example) ---

client = GraphHopperClient(base_url="http://localhost:8989", profile="car")

locations = [
    Location(lat=10.7712, lng=106.7011, label="Kho hàng (Warehouse)"),
    Location(lat=10.7780, lng=106.7100, label="Khách C (Customer A)"),
    Location(lat=10.7650, lng=106.6980, label="Khách B (Customer B)"),
]

matrix = client.get_matrix(locations)

# In ma trận thời gian di chuyển (Print duration matrix)
for i, from_loc in enumerate(locations):
    for j, to_loc in enumerate(locations):
        if i != j:
            duration_min = matrix.durations[i][j] // 60
            distance_km = matrix.distances[i][j] / 1000
            print(f"{from_loc.label}{to_loc.label}: tốn {duration_min}min, khoảng {distance_km:.1f}km")

Quản Lý Cấu Hình Phương Tiện (Vehicle Profiles) Và Custom Models

Ưu điểm vượt trội của GraphHopper so với OSRM là tính năng Custom Models, cho phép thay đổi quy tắc định tuyến (routing rules) trực tiếp tại thời điểm chạy (runtime) mà không cần phải xử lý hay biên dịch lại dữ liệu bản đồ.

Cấu Hình Có Sẵn (Built-in Profiles)

# Cấu hình mặc định cho xe ô tô (car)
curl -X POST http://localhost:8989/matrix \
  -d '{"points": [[106.7011, 10.7712], [106.7100, 10.7780]], "profile": "car", "out_arrays": ["times", "distances"]}'

# Cấu hình cho xe máy (motorcycle)
curl -X POST http://localhost:8989/matrix \
  -d '{"points": [[106.7011, 10.7712], [106.7100, 10.7780]], "profile": "motorcycle", "out_arrays": ["times", "distances"]}'

# Cấu hình cho xe đạp (bike)
curl -X POST http://localhost:8989/matrix \
  -d '{"points": [[106.7011, 10.7712], [106.7100, 10.7780]], "profile": "bike", "out_arrays": ["times", "distances"]}'

# Cấu hình cho người đi bộ (foot)
curl -X POST http://localhost:8989/matrix \
  -d '{"points": [[106.7011, 10.7712], [106.7100, 10.7780]], "profile": "foot", "out_arrays": ["times", "distances"]}'

Tùy Biến Với Custom Models — Thay Đổi Quy Tắc Tại Thời Điểm Chạy (Runtime)

Tính năng Custom Models cho phép điều chỉnh hành vi định tuyến (routing behavior) linh hoạt mà không cần khởi động lại máy chủ hay biên dịch lại bản đồ. Đây là yếu tố then chốt cho các hệ thống logistics vận hành đội xe hỗn hợp (mixed fleets).

Ví dụ: Quy định định tuyến cho xe tải nặng (tránh đường cao tốc và hạn chế vào đường dân cư)

{
  "points": [[106.7011, 10.7712], [106.7100, 10.7780]],
  "profile": "car",
  "out_arrays": ["times", "distances"],
  "custom_model": {
    "speed": [
      {
        "if": "road_class == MOTORWAY",
        "limit_to": 90
      },
      {
        "if": "road_environment == TUNNEL",
        "multiply_by": 0
      }
    ],
    "priority": [
      {
        "if": "max_weight < 10",
        "multiply_by": 0
      },
      {
        "if": "road_class == RESIDENTIAL",
        "multiply_by": 0.3
      }
    ]
  }
}

Ví dụ: Định tuyến né trạm thu phí (Toll-avoidance routing)

{
  "custom_model": {
    "priority": [
      {
        "if": "toll == ALL",
        "multiply_by": 0
      }
    ]
  }
}

Trường hợp sử dụng thực tế (Real-world use case): Một doanh nghiệp logistics quản lý đồng thời cả xe máy giao hàng chặng cuối (last-mile) và xe tải 10 tấn có thể dùng chung một instance GraphHopper duy nhất. Bằng cách gửi kèm Custom Model tương ứng cho từng loại phương tiện trong mỗi request, hệ thống xử lý linh hoạt mà không cần duy trì nhiều hạ tầng khác nhau.


Triển Khai Dạng Nhúng Với Java SDK (Embedded Mode)

Đối với các hệ thống backend phát triển bằng Java, bạn có thể nhúng trực tiếp thư viện GraphHopper vào tiến trình ứng dụng (in-process). Cách làm này loại bỏ hoàn toàn chi phí truyền tải qua HTTP (zero HTTP overhead) khi tính toán ma trận khoảng cách.

import com.graphhopper.GraphHopper;
import com.graphhopper.GraphHopperConfig;
import com.graphhopper.config.CHProfile;
import com.graphhopper.config.Profile;
import com.graphhopper.routing.matrix.MatrixResult;

public class EmbeddedGraphHopperMatrix {

    private final GraphHopper hopper;

    public EmbeddedGraphHopperMatrix(String osmFile, String graphLocation) {
        GraphHopperConfig config = new GraphHopperConfig();
        config.putObject("graph.location", graphLocation);

        this.hopper = new GraphHopper();
        this.hopper.setOSMFile(osmFile);
        this.hopper.setGraphHopperLocation(graphLocation);
        this.hopper.setProfiles(
            new Profile("car").setVehicle("car").setWeighting("fastest"),
            new Profile("truck").setVehicle("car").setWeighting("custom")
        );
        this.hopper.getCHPreparationHandler()
            .setCHProfiles(new CHProfile("car"));
        this.hopper.importOrLoad();
    }

    // Phương thức tiện ích sử dụng GHMatrixAPI cho các tính toán N×N
    // Tham khảo chi tiết tại: https://github.com/graphhopper/graphhopper/tree/master/web-api
    public void shutdown() {
        hopper.close();
    }
}

So sánh giữa Embedded Mode và HTTP API:

  • Embedded Mode: Độ trễ cực thấp (~1ms mỗi request) nhờ loại bỏ hoàn toàn network overhead. Phù hợp cho các Java microservices cần thực hiện hàng nghìn truy vấn ma trận mỗi giây.
  • HTTP API: Không phụ thuộc ngôn ngữ lập trình (Language-agnostic), dễ dàng mở rộng hàng ngang (scale horizontally). Lựa chọn tối ưu cho các hệ thống đa ngôn ngữ hoặc ứng dụng viết bằng Python, Go, Node.js.

Chiến Lược Caching Ma Trận Với Redis Và Chỉ Mục Không Gian H3

Mạng lưới giao thông rất ít khi thay đổi. Việc lưu cache kết quả ma trận khoảng cách dựa trên cặp ô chỉ mục không gian H3 (H3 cell pairs) giúp giảm hơn 90% số lượng request đến GraphHopper khi hệ thống đi vào vận hành ổn định.

import h3
import json
import redis
from graphhopper_client import GraphHopperClient, Location, DistanceMatrix

class CachedDistanceMatrix:
    """
    Tầng Caching cho GraphHopper Distance Matrix dựa trên chỉ mục H3 và Redis.
    Tỷ lệ cache hit có thể đạt trên 90% khi hệ thống vận hành ổn định.
    """

    H3_RESOLUTION = 9        # Mỗi ô H3 có bán kính ~174m (tương đương một khối nhà phố)
    CACHE_TTL_DAYS = 30      # Dữ liệu đường xá ít thay đổi nên lưu cache 30 ngày

    def __init__(self, gh_client: GraphHopperClient, redis_client: redis.Redis):
        self.gh = gh_client
        self.redis = redis_client

    def _h3_key(self, loc_a: Location, loc_b: Location) -> str:
        """Tạo cache key chuẩn hóa từ hai H3 cell ID."""
        cell_a = h3.latlng_to_cell(loc_a.lat, loc_a.lng, self.H3_RESOLUTION)
        cell_b = h3.latlng_to_cell(loc_b.lat, loc_b.lng, self.H3_RESOLUTION)
        # Sắp xếp hai cell ID để đảm bảo tính đối xứng (cặp A->B và B->A dùng chung một cache key)
        return f"gh:matrix:{min(cell_a, cell_b)}:{max(cell_a, cell_b)}"

    def get_pair(self, origin: Location, dest: Location) -> dict:
        """
        Lấy thông tin khoảng cách và thời gian cho một cặp địa điểm từ Redis cache.
        Kết quả (Returns): {"duration_s": int, "distance_m": int}
        """
        cache_key = self._h3_key(origin, dest)
        cached = self.redis.get(cache_key)

        if cached:
            return json.loads(cached)

        # Khi cache miss: gọi GraphHopper API để tính toán
        matrix = self.gh.get_matrix([origin, dest])
        result = {
            "duration_s": matrix.durations[0][1],
            "distance_m": matrix.distances[0][1],
        }

        self.redis.setex(
            cache_key,
            self.CACHE_TTL_DAYS * 86400,
            json.dumps(result)
        )
        return result

    def get_matrix_cached(self, locations: list[Location]) -> DistanceMatrix:
        """
        Lấy ma trận N×N bằng cách kết hợp dữ liệu từ Redis cache và gọi GraphHopper cho các cặp bị miss.
        """
        n = len(locations)
        durations = [[0] * n for _ in range(n)]
        distances = [[0] * n for _ in range(n)]
        missing_pairs = []

        # Kiểm tra cache cho tất cả các cặp địa điểm
        cache_keys = {}
        for i in range(n):
            for j in range(n):
                if i == j:
                    continue
                key = self._h3_key(locations[i], locations[j])
                cached = self.redis.get(key)
                if cached:
                    data = json.loads(cached)
                    durations[i][j] = data["duration_s"]
                    distances[i][j] = data["distance_m"]
                else:
                    missing_pairs.append((i, j))

        if missing_pairs:
            # Gửi request tính toán các cặp bị thiếu qua GraphHopper
            missing_locs = list({idx for pair in missing_pairs for idx in pair})
            loc_subset = [locations[i] for i in missing_locs]
            sub_matrix = self.gh.get_matrix(loc_subset)

            idx_map = {orig_idx: sub_idx for sub_idx, orig_idx in enumerate(missing_locs)}

            for i, j in missing_pairs:
                si, sj = idx_map[i], idx_map[j]
                dur = sub_matrix.durations[si][sj]
                dist = sub_matrix.distances[si][sj]
                durations[i][j] = dur
                distances[i][j] = dist

                # Lưu thông tin tính được vào Redis cache
                key = self._h3_key(locations[i], locations[j])
                self.redis.setex(
                    key,
                    self.CACHE_TTL_DAYS * 86400,
                    json.dumps({"duration_s": dur, "distance_m": dist})
                )

        return DistanceMatrix(durations=durations, distances=distances)

Dành cho các hệ thống vi dịch vụ (microservices) bằng Go, đây là tầng caching tương đương sử dụng gói thư viện github.com/uber/h3-go/v4github.com/redis/go-redis/v9:

package matrix

import (
	"context"
	"encoding/json"
	"fmt"
	"time"

	"github.com/redis/go-redis/v9"
	"github.com/uber/h3-go/v4"
)

type Location struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}

type CachedMatrix struct {
	gh    *GraphHopperClient
	redis *redis.Client
}

func (c *CachedMatrix) h3Key(a, b Location) string {
	cellA := h3.LatLngToCell(h3.NewLatLng(a.Lat, a.Lng), 9)
	cellB := h3.LatLngToCell(h3.NewLatLng(b.Lat, b.Lng), 9)

	minCell, maxCell := cellA, cellB
	if cellB < cellA {
		minCell, maxCell = cellB, cellA
	}
	return fmt.Sprintf("gh:matrix:%x:%x", minCell, maxCell)
}

func (c *CachedMatrix) GetPair(ctx context.Context, origin, dest Location) (map[string]int, error) {
	key := c.h3Key(origin, dest)
	cached, err := c.redis.Get(ctx, key).Result()
	if err == nil {
		var result map[string]int
		json.Unmarshal([]byte(cached), &result)
		return result, nil
	}

	// Xử lý khi miss cache, gọi fallback tới API HTTP của GraphHopper
	matrix, err := c.gh.GetMatrix([]Location{origin, dest})
	if err != nil {
		return nil, err
	}
	
	result := map[string]int{
		"duration_s": matrix.Durations[0][1],
		"distance_m": matrix.Distances[0][1],
	}

	data, _ := json.Marshal(result)
	c.redis.SetEx(ctx, key, data, 30*24*time.Hour)
	return result, nil
}

Hiệu quả thực tế của tầng Caching (Cache hit ratio in practice):

  • Ngày đầu tiên (Cold cache): Tỷ lệ hit ~0%, hệ thống tính toán và lấp đầy dữ liệu vào Redis cache.
  • Sau 7 ngày: Tỷ lệ hit đạt ~70%.
  • Sau 30 ngày (Trạng thái ổn định): Tỷ lệ hit đạt >90% do hầu hết các khu vực giao hàng lặp lại đã được lưu cache.

Đánh Giá Hiệu Năng (Production Benchmark): GraphHopper vs. OSRM vs. Google Maps

Thử nghiệm tính toán ma trận 100×100 (10,000 cặp điểm) trên cấu hình máy chủ VPS DigitalOcean 4-vCPU / 8GB RAM sử dụng dữ liệu bản đồ Việt Nam:

Routing Engine10×10 matrix50×50 matrix100×100 matrixChi phí hàng tháng (Monthly cost)
GraphHopper (Self-hosted)8ms28ms52msKhoảng ~$20 cho VPS
OSRM (Self-hosted)4ms14ms21msKhoảng ~$20 cho VPS
Valhalla (Self-hosted)15ms60ms120msKhoảng ~$20 cho VPS
Google Maps Distance Matrix API300ms1,200msTừ 2,500ms+Chi phí cao ($510/ngày ở quy mô lớn)
HERE Matrix Routing v8150ms600ms1,200ms$0.70/đoạn tuyến

Phân tích kết quả benchmark:

  • OSRM đạt tốc độ tính toán ma trận thuần túy nhanh hơn GraphHopper khoảng 2.5 lần.
  • Bù lại, GraphHopper vượt trội khi hệ thống cần áp dụng các quy tắc định tuyến động (Custom Models) linh hoạt cho từng loại phương tiện tại runtime.
  • Google Maps API có chi phí cao hơn đáng kể và độ trễ lớn hơn. Dịch vụ này chỉ cần thiết khi ứng dụng đòi hỏi dữ liệu giao thông thời gian thực (real-time traffic), trong khi các bài toán định tuyến giao hàng cố định (static delivery routing) nên sử dụng giải pháp Self-hosted như GraphHopper để tối ưu chi phí.

Yêu Cầu Phần Cứng Và Bộ Nhớ RAM (Memory & Hardware Requirements)

GraphHopper nạp toàn bộ đồ thị mạng lưới giao thông vào bộ nhớ RAM. Dung lượng RAM sử dụng phụ thuộc trực tiếp vào dung lượng và phạm vi của tệp bản đồ OSM:

Khu Vực (Region)Dung Lượng Tệp OSMDung Lượng RAM Yêu Cầu
TP.HCM mở rộng (Ho Chi Minh City metro)~180MB~2GB
Toàn bộ Việt Nam (Vietnam - entire country)~880MB~6GB
Đông Nam Á (Southeast Asia)~4.5GB~24GB+
Nước Đức (Germany)~3.8GB~20GB+

Cấu hình đề xuất cho môi trường Production (dữ liệu bản đồ Việt Nam):

  • Máy chủ VPS 4 vCPU / 8GB RAM
  • Ổ cứng SSD NVMe cho thư mục chứa đồ thị graph-cache
  • Bật cấu hình Contraction Hierarchies (CH) để tối ưu thời gian truy vấn
  • Khởi chạy tối thiểu 2 instances đằng sau Load Balancer để đảm bảo tính sẵn sàng cao (High Availability - HA)

Chuyên Mục Vấn Đáp (Frequently Asked Questions)

GraphHopper distance matrix là gì?
GraphHopper Distance Matrix là API endpoint /matrix thuộc công cụ định tuyến mã nguồn mở GraphHopper. API này tiếp nhận danh sách N tọa độ (latitude/longitude) và trả về ma trận N×N chứa thời gian di chuyển (tính bằng giây) cùng khoảng cách (tính bằng mét) dựa trên mạng lưới đường xá từ OpenStreetMap. Giải pháp hoàn toàn miễn phí khi tự triển khai qua Docker và có thể xử lý ma trận 100×100 (10,000 cặp điểm) dưới 50ms trên máy chủ VPS 4-vCPU.
GraphHopper có hoàn toàn miễn phí khi tự triển khai (self-hosted) hay không?
GraphHopper là một dự án mã nguồn mở (open-source) theo giấy phép Apache 2.0, do đó bạn hoàn toàn có thể tự triển khai (self-host) mà không tốn phí bản quyền. Dữ liệu bản đồ được lấy miễn phí từ OpenStreetMap (OSM) thông qua các nhà cung cấp như Geofabrik. Chi phí duy nhất là hạ tầng máy chủ (ví dụ: VPS khoảng $20/tháng trên DigitalOcean đủ để phục vụ bản đồ Việt Nam). Ngoài ra, GraphHopper GmbH cũng cung cấp dịch vụ Cloud API trả phí nếu bạn không muốn tự quản lý hạ tầng.
So sánh hiệu năng tính toán ma trận khoảng cách (Distance Matrix) giữa OSRM và GraphHopper?
OSRM đạt tốc độ tính toán nhanh hơn (khoảng 21ms cho ma trận 100x100 so với 52ms của GraphHopper) do được viết hoàn toàn bằng C++ và tối ưu hóa cực tốt cho thuật toán Contraction Hierarchies (CH). Tuy nhiên, GraphHopper vượt trội về tính linh hoạt nhờ tính năng Custom Models, cho phép điều chỉnh các quy tắc định tuyến (routing rules) như giới hạn tải trọng xe, tránh đường phí, hoặc hạn chế loại đường ngay tại thời điểm runtime mà không cần phải tiền xử lý lại đồ thị (recompiling the graph). Do đó, OSRM thích hợp cho các ứng dụng một loại xe (single vehicle type) cần tốc độ tối đa, trong khi GraphHopper là lựa chọn tối ưu cho đội xe hỗn hợp (mixed fleet) với nhiều ràng buộc tùy chỉnh.
Khi nào nên sử dụng GraphHopper Self-Hosted thay vì Google Maps Distance Matrix API?
GraphHopper Self-Hosted là lựa chọn hàng đầu cho các bài toán định tuyến giao hàng cố định (static delivery routing) với số lượng điểm giao lớn từ kho cố định. Việc tính toán ma trận 10,000 cặp điểm (100x100) trên GraphHopper chỉ mất dưới 50ms và chi phí $0. Trong khi đó, với Google Maps API, 10,000 request ma trận khoảng cách sẽ tốn khoản chi phí đáng kể ($510/ngày nếu gọi liên tục ở quy mô lớn). Google Maps API chỉ thực sự cần thiết khi ứng dụng đòi hỏi dữ liệu giao thông thời gian thực (real-time traffic) chính xác đến từng phút cho bài toán gọi xe hoặc giao hàng chặng cuối (last-mile delivery).
GraphHopper hỗ trợ định dạng dữ liệu bản đồ OSM nào?
GraphHopper sử dụng định dạng dữ liệu nén nhị phân chuẩn .osm.pbf của OpenStreetMap. Bạn có thể tải miễn phí các tệp bản đồ theo khu vực hoặc quốc gia từ Geofabrik (geofabrik.de). Ví dụ, dữ liệu bản đồ Việt Nam có thể tải từ: https://download.geofabrik.de/asia/vietnam-latest.osm.pbf. GraphHopper cũng hỗ trợ tự động tải tệp PBF từ URL được cấu hình thông qua tham số --url khi khởi chạy lần đầu.
Yêu cầu về bộ nhớ RAM của GraphHopper cho dữ liệu bản đồ như thế nào?
GraphHopper nạp toàn bộ dữ liệu đồ thị giao thông vào RAM để đảm bảo tốc độ truy vấn tức thì. Kích thước RAM phụ thuộc vào vùng bản đồ: dữ liệu toàn Việt Nam (tệp OSM ~880MB) cần khoảng 6GB RAM; dữ liệu khu vực TP.HCM (tệp OSM ~180MB) cần khoảng 2GB RAM. Một máy chủ VPS 4-vCPU / 8GB RAM trên DigitalOcean là đủ để vận hành trơn tru toàn bộ bản đồ Việt Nam cho hầu hết ứng dụng logistics.


🤝 Kết nối với tôi

Bạn đang gặp phải những thách thức tương tự về kiến trúc hệ thống, mở rộng quy mô (scaling) hay dịch chuyển (migration)? Hãy kết nối với tôi trên LinkedIn, theo dõi GitHub của tôi, hoặc gửi một email để trao đổi nhé.