Ping URL: báo động khi việc định kỳ im lặng
Mỗi monitor có một URL bí mật; job gọi nó khi chạy xong, thêm /fail khi lỗi. Belvador báo khi hết tín hiệu — kể cả khi app, database hay node của bạn đã sập.
Trong bài này
Monitor ping URL (heartbeat, hay "công tắc người chết") canh những việc phải xảy ra đều đặn — cron job, bộ hẹn giờ gọi vào app mỗi phút, script backup, lịch CI. Mỗi lần chạy, job gọi một URL HTTPS bí mật. Khi các lần gọi dừng lại, Belvador báo "không nhận được tín hiệu".
Không cần SDK, không cần header: thứ gì gửi được request HTTPS đều dùng được — curl, crontab, Python, Node.js, PHP, Go, GitHub Actions.
Vì sao vẫn chạy khi hệ thống của bạn sập#
App không thể tự báo rằng nó đã ngừng. Vì vậy hạn chờ của từng monitor ping URL do Belvador giữ ở edge — không phải app của bạn, cũng không phải node của bạn:
Nếu app, database hay bộ hẹn giờ chết, ping dừng và Belvador báo đúng hạn.
Nếu node Belvador của bạn mất kết nối, monitor ping URL vẫn được canh và vẫn báo động. Chỉ danh sách chi tiết các lần ping gần đây phải chờ node quay lại.
Để làm được vậy, Belvador giữ một ít metadata về tín hiệu sống bên ngoài node: id ẩn danh của monitor, tín hiệu nào tới (ok, start, fail, log, mã thoát) và lúc nào, các hạn chờ và trạng thái sự cố. Đây là ngoại lệ duy nhất của nguyên tắc "dữ liệu event nằm trên node của bạn" và được ghi ở trang quyền riêng tư. Nó không bao giờ chứa body request, header, địa chỉ IP máy chủ của bạn hay bất cứ gì job in ra — output bạn chủ động gửi kèm chỉ đi về node của bạn (xem bên dưới).
Tạo monitor#
Trong một project, mở Monitor → Tạo monitor và chọn Ping URL (canh định kỳ).
| Trường | Ý nghĩa |
|---|---|
| Tên và slug | Cách monitor hiện trong Belvador và trong cảnh báo |
| Lịch | Mỗi N phút/giờ (ví dụ mỗi 1 phút), hoặc lịch cron kèm múi giờ (0 2 * * * = 02:00 mỗi ngày) |
| Cho phép trễ | Sau thời điểm đáng lẽ có ping, Belvador chờ bao lâu rồi mới báo — mặc định 5 phút, tối đa 1440 |
| Thời gian chạy tối đa | Chỉ dùng khi job gọi thêm /start: một lượt mở quá lâu tính là quá giờ |
| Môi trường | Một ping URL báo cho một môi trường (mặc định production). Tạo monitor thứ hai cho staging |
Sau khi lưu, Belvador hiện ping URL một lần duy nhất. Hãy chép vào secret của máy chủ hoặc CI. Belvador chỉ giữ dấu vân tay của URL nên không hiện lại được — nếu làm mất, hãy xoay URL (xem bên dưới).
Monitor hiện Đang chờ ping đầu tiên cho tới khi có lần gọi đầu; trước đó không đánh giá gì.
Gọi URL#
https://ping.belvador.dev/p/bvp_<your-ping-token> chạy xong, ổnhttps://ping.belvador.dev/p/bvp_<your-ping-token>/start bắt đầu (tuỳ chọn; bật kiểm tra thời gian chạy tối đa)https://ping.belvador.dev/p/bvp_<your-ping-token>/fail lỗi — báo ngayhttps://ping.belvador.dev/p/bvp_<your-ping-token>/<0-255> mã thoát: 0 = ổn, 1–255 = lỗihttps://ping.belvador.dev/p/bvp_<your-ping-token>/log gửi kèm output hoặc ghi chú; không đổi trạng tháiGọi GET hay POST đều được. Có / ở cuối cũng được.
curl#
# Alive / finished OKcurl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token> || true# Started (optional: arms the max-runtime check)curl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token>/start || true# Failed — alerts at oncecurl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token>/fail || true# Or send the job's exit status right after it: 0 = success, 1–255 = failure./nightly-job.sh; curl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token>/$? || truecrontab#
# Runs at 02:00; Belvador gets "started", then the job's exit status.0 2 * * * curl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token>/start || true ; /usr/local/bin/nightly-job.sh ; curl -fsS -m 10 --retry 5 -o /dev/null https://ping.belvador.dev/p/bvp_<your-ping-token>/$? || trueNode.js, Bun hoặc Deno#
const PING_URL = process.env.BELVADOR_PING_URL!; // https://ping.belvador.dev/p/bvp_<your-ping-token>/** Never lets monitoring break the job: 10 s timeout, errors ignored. */async function ping(suffix = "") { await fetch(PING_URL + suffix, { method: "POST", signal: AbortSignal.timeout(10_000) }).catch(() => {});}await ping("/start");try { await runJob(); await ping();} catch (error) { await ping("/fail"); throw error;}Python#
import os, urllib.requestPING_URL = os.environ["BELVADOR_PING_URL"] # https://ping.belvador.dev/p/bvp_<your-ping-token>def ping(suffix=""): try: urllib.request.urlopen(PING_URL + suffix, data=b"", timeout=10) except OSError: pass # monitoring never breaks the jobping("/start")try: run_job() ping()except Exception: ping("/fail") raiseGitHub Actions#
# .github/workflows/nightly.yml — store the URL as the secret BELVADOR_PING_URLjobs: nightly: runs-on: ubuntu-latest steps: - run: curl -fsS -m 10 --retry 5 -o /dev/null "${{ secrets.BELVADOR_PING_URL }}/start" || true - run: ./scripts/nightly.sh - if: success() run: curl -fsS -m 10 --retry 5 -o /dev/null "${{ secrets.BELVADOR_PING_URL }}" || true - if: failure() run: curl -fsS -m 10 --retry 5 -o /dev/null "${{ secrets.BELVADOR_PING_URL }}/fail" || trueGửi kèm output của job#
Tín hiệu nào cũng có thể mang output của job trong body POST — vài dòng cuối của log, thông báo lỗi, số dòng đã xử lý. Nội dung hiện ở mục Nội dung gửi kèm trên trang monitor và trong lượt chạy tương ứng.
# Run the job, then send its exit status with the end of its output (60 KB; the cap is 64 KB).out=$(./nightly-job.sh 2>&1); code=$?printf '%s\n' "$out" | tail -c 60000 | curl -fsS -m 10 --retry 5 -o /dev/null --data-binary @- https://ping.belvador.dev/p/bvp_<your-ping-token>/$code || true# A progress note at any time — shown with the run, never changes the statusecho "exported 1204 rows" | curl -fsS -m 10 --retry 5 -o /dev/null --data-binary @- https://ping.belvador.dev/p/bvp_<your-ping-token>/log || trueTối đa 64 KB văn bản mỗi lần ping. Body lớn hơn bị từ chối với
413và lần ping đó không được ghi nhận, nên hãy cắt bớt (tail -c 60000).Output chỉ được lưu trên node của bạn, giữ 100 bản mới nhất mỗi môi trường. Trên đường đi nó được xử lý như event lỗi của bạn: chuyển thẳng về node và niêm phong bằng khoá của node mỗi khi phải chờ ở Cloudflare. Cơ sở dữ liệu và tin cảnh báo của Belvador không bao giờ chứa nó.
Quy tắc lọc dữ liệu của node chạy trên nội dung này, và mọi ping URL trong văn bản được thay thế trước khi rời dịch vụ ping.
Output không bao giờ quyết định trạng thái: chỉ URL mới quyết định. Body ghi "error" mà gọi URL gốc thì vẫn là thành công.
Việc chuyển về node là cố gắng tối đa và không bao giờ làm chậm mã
200. Khi node không liên lạc được, ping vẫn được xét và cảnh báo vẫn gửi; output chờ node trong cùng giới hạn như event của bạn.
Mọi công thức đều đặt giới hạn 10 giây và bỏ qua lỗi (|| true sau mỗi lệnh curl), nên Belvador có gián đoạn cũng không bao giờ làm job của bạn thất bại — kể cả trong script set -e hay một step GitHub Actions, vốn dừng ngay ở lệnh lỗi đầu tiên. Nếu ping URL không bao giờ trả lời, mỗi lần ping bỏ cuộc sau khoảng 90 giây; thêm --retry-max-time 30 nếu job của bạn không chờ được lâu như vậy.
Các trạng thái#
| Trạng thái | Ý nghĩa |
|---|---|
| Đang chờ ping đầu tiên | Mới tạo, chưa có lần gọi nào |
| Khoẻ | Lần gọi gần nhất là thành công và chưa tới hạn lần sau |
| Trễ | Đã quá giờ ping kế tiếp nhưng còn trong khoảng cho phép trễ — chưa báo |
| Thất bại | Không có ping trong khoảng cho phép trễ (Bị lỡ), job gọi /fail hoặc mã thoát khác 0, hoặc một lượt đã gọi /start chạy quá thời gian tối đa |
| Tạm dừng | Không đánh giá cho tới khi bạn bật lại hoặc hết thời gian tạm dừng |
Lần ping thành công kế tiếp đóng sự cố và gửi tin đã hồi phục.
Trang monitor hiện lúc nào đang chờ ping kế tiếp, lúc nào Belvador sẽ báo, và 7 ngày gần nhất theo từng giờ (ổn, lỗi, bị lỡ).
Cảnh báo#
Monitor ping URL dùng chung quy tắc cảnh báo với các monitor khác: một lượt bị lỡ hoặc lỗi mở sự cố và các nơi nhận của quy tắc nhận tin; khi hồi phục có tin đóng. Không lặp lại trong (mặc định 30 phút) giữ cho cùng một monitor không báo hai lần liền. Chọn nơi nhận ở Cảnh báo, kể cả webhook của chính bạn.
Tin báo ghi "không nhận được tín hiệu", không bao giờ ghi "máy chủ đã chết": Belvador chỉ biết là ping đã dừng.
Tạm dừng để bảo trì#
Trên trang monitor chọn Tạm dừng — 30 phút, 1, 2, 8 hoặc 24 giờ, tới một thời điểm bạn chọn, hoặc cho tới khi bật lại. Trong lúc tạm dừng, thiếu ping không báo động. Ping vẫn được đếm nên lịch sử vẫn đầy đủ.
Xoay hoặc đặt lại URL#
Hãy giữ ping URL như mật khẩu: ai có nó đều báo được cho monitor này (nhưng không đọc được gì). Owner và admin quản lý ở mục Ping URL trên trang monitor; mỗi thao tác hỏi mã xác thực của bạn.
Xoay URL cấp URL mới và giữ URL cũ hoạt động thêm một khoảng bạn chọn — 1 giờ, 24 giờ (mặc định), 3 hoặc 7 ngày — để bạn cập nhật job mà không lỡ ping nào. Cùng lúc chỉ tối đa 2 URL hoạt động.
Ngừng URL cũ ngay kết thúc khoảng chồng lấn sớm.
Đặt lại khi bị lộ dừng mọi URL ngay lập tức và cấp URL mới.
Thay đổi có hiệu lực ở địa chỉ nhận ping trong vòng 60 giây.
Mã trả về#
| Mã | Ý nghĩa |
|---|---|
200 OK | Tín hiệu đã được lưu — Belvador chỉ trả lời sau khi đã ghi an toàn |
404 | URL không tồn tại, đã thu hồi hoặc hết hạn (cùng một câu trả lời để không đoán được URL) |
400 | Hậu tố khác start, fail, log hoặc 0–255 |
403 | Request đến từ trang web (có header Origin), hoặc workspace đang không nhận dữ liệu |
405 | Phương thức khác GET hoặc POST |
413 | Body lớn hơn 64 KB; không ghi nhận gì |
429 | Quá 60 lần gọi mỗi phút cho monitor này (dồn tối đa 20), hoặc vượt giới hạn tháng của gói. Hãy tôn trọng Retry-After |
503 | Thử lại sau vài giây (Retry-After) |
Gọi lặp lại một lần gọi là an toàn: gửi header Idempotency-Key (hoặc ?rid=<id>) thì các lần lặp chỉ tính một lần.
Cần biết#
Đừng gọi URL từ trang web. Trình duyệt bị từ chối, và URL sẽ lộ cho mọi người xem trang.
Ping tới sớm tối đa một phút trước giờ cron vẫn tính cho giờ đó, để bù lệch đồng hồ của máy chạy job.
Nếu chính Belvador tạm thời không đánh giá được, nó chờ các ping còn đang đệm rồi mới quyết định, và đánh dấu quyết định muộn là phát hiện muộn thay vì báo hai lần.
Bài viết về chủ đề này
Tất cả bài viết- Observability cho team nhỏ: năm tín hiệu, theo đúng thứ tự cần dùngTeam nhỏ nên theo dõi gì trước: lỗi, job định kỳ, uptime từ bên ngoài, một SLO, rồi mới tới trace. Dựa trên bốn thí nghiệm, kèm chỗ mỗi tín hiệu bỏ sót.
- Giám sát AI agent chạy trên serverless functionAgent trên serverless là chuỗi function ngắn có giới hạn thời gian cứng. Cần theo dõi gì ở từng bước, kèm ba thí nghiệm và bảng giới hạn trong tài liệu.
- Cron job chạy mãi không xong: heartbeat thật ra nhìn thấy gì?Thí nghiệm dừng cron job bằng SIGTERM và SIGKILL, mỗi kiểu ba lần. Gửi heartbeat từ đâu quyết định bạn nhận báo lỗi, báo OK giả hay im lặng.
Thấy chỗ sai hoặc thiếu? Gửi cho chúng tôi: hello@belvador.dev