VPS

Laravel queue trên VPS: chạy job nền không rớt

Thanh Nguyễn Đăng 9 phút đọc 6 khối lệnh

Laravel queue trên VPS: chạy job nền không rớt
Mục lục bài viết
  1. Chọn driver theo tải thật
  2. Worker chạy như dịch vụ hệ thống
  3. Job lỗi và cách không mất việc
  4. Timeout, bộ nhớ và những giới hạn nên đặt
  5. Quy trình deploy không làm hỏng hàng đợi
  6. Chọn nơi lưu hàng đợi theo tải thật
  7. Tiến trình xử lý chạy như một dịch vụ hệ thống
  8. Ba việc phải làm để không mất việc
  9. Câu hỏi thường gặp
  10. Bài viết liên quan

Trả lời nhanh: Muốn Laravel queue chạy bền trên máy chủ thật, cần bốn thứ: chọn driver hợp tải (database cho ứng dụng nhỏ, Redis khi lượng job lớn), để systemd trông worker thay vì chạy tay, bật bảng job lỗi và có cách chạy lại, và khởi động lại worker sau mỗi lần deploy vì tiến trình cũ vẫn giữ code cũ trong bộ nhớ. Bỏ điều cuối là nguyên nhân phổ biến nhất của cảnh code đã sửa mà job vẫn chạy sai.

Hàng đợi là thứ khiến ứng dụng có cảm giác nhanh: người dùng bấm nút là được phản hồi ngay, còn việc nặng như gửi email hay xử lý ảnh chạy phía sau. Nhưng chính vì chạy phía sau nên khi nó hỏng, không ai biết. Bài này đi vào phần vận hành thật của queue trên VPS, tiếp nối phần cài đặt cơ bản đã nói ở bài deploy Laravel.

Chọn driver theo tải thật

Database là lựa chọn mặc định hợp lý khi bắt đầu. Không cần cài thêm gì, job nằm trong một bảng nên xem được bằng câu truy vấn thông thường, tiện lúc gỡ lỗi. Nhược điểm lộ ra khi lượng job lớn: worker liên tục hỏi bảng đó, tạo tải đọc ghi đều đặn lên database vốn đang phục vụ người dùng.

Redis hợp khi job nhiều hoặc cần lấy ra thật nhanh. Nó giữ hàng đợi trong bộ nhớ nên nhẹ hơn hẳn cho database, đổi lại bạn phải cài và trông thêm một dịch vụ, đồng thời chú ý cấu hình lưu xuống đĩa để không mất job khi máy khởi động lại.

Mốc chuyển đổi thực tế: khi số job vượt vài nghìn mỗi ngày hoặc bạn thấy bảng job xuất hiện trong danh sách truy vấn chậm, đó là lúc chuyển. Trước đó thì database gọn hơn và ít thứ phải trông hơn.

Worker chạy như dịch vụ hệ thống

TerminalWorker chạy như dịch vụ hệ thốngbash
# /etc/systemd/system/[email protected][Unit]Description=Laravel queue worker %iAfter=network.target [Service]User=www-dataRestart=alwaysRestartSec=3ExecStart=/usr/bin/php /var/www/app/artisan queue:work redis   --queue=cao,mac-dinh --sleep=3 --tries=3 --max-time=3600 [Install]WantedBy=multi-user.target
TerminalWorker chạy như dịch vụ hệ thốngbash
sudo systemctl enable --now laravel-queue@1 laravel-queue@2

Dấu @ trong tên file cho phép bạn bật nhiều worker từ cùng một cấu hình, tiện khi cần tăng số tiến trình xử lý. Số worker hợp lý bắt đầu bằng số nhân CPU, rồi điều chỉnh theo thực tế: job chờ mạng nhiều thì tăng được, job ngốn CPU thì đừng vượt số nhân.

Tham số --queue=cao,mac-dinh đặt thứ tự ưu tiên: worker luôn lấy hết hàng đợi cao trước rồi mới tới hàng đợi thường. Đây là cách đơn giản để email đặt lại mật khẩu không phải xếp sau hai nghìn job xuất báo cáo.

Job lỗi và cách không mất việc

Tạo bảng lưu job thất bại ngay từ đầu:

TerminalJob lỗi và cách không mất việcbash
php artisan make:queue-failed-tablephp artisan migrate

Từ đó, mọi job thử đủ số lần vẫn hỏng sẽ nằm lại trong bảng kèm thông báo lỗi, thay vì biến mất. Ba lệnh dùng hằng ngày:

TerminalJob lỗi và cách không mất việcbash
php artisan queue:failed              # xem danh sáchphp artisan queue:retry <id>          # chạy lại một jobphp artisan queue:retry all           # chạy lại tất cả

Quan trọng hơn cả ba lệnh trên: hãy có cảnh báo khi bảng này có bản ghi mới. Một job hỏng lặng lẽ có thể nghĩa là khách hàng không nhận được email xác nhận đơn suốt cả tuần mà không ai hay. Một tác vụ định kỳ đếm số dòng trong bảng rồi gửi thông báo là đủ, không cần hệ thống giám sát phức tạp.

Timeout, bộ nhớ và những giới hạn nên đặt

Ba tham số quyết định độ bền của worker:

  • timeout, thời gian tối đa cho một job. Đặt quá ngắn thì job nặng bị cắt giữa chừng, quá dài thì một job treo giữ chỗ mãi. Nên đặt riêng cho loại job nặng thay vì nâng mức chung cho tất cả.
  • tries, số lần thử lại. Job gọi API bên ngoài nên thử lại vài lần vì lỗi mạng là tạm thời. Job ghi dữ liệu thì cẩn thận, thử lại có thể tạo bản ghi trùng nếu code chưa xử lý chuyện lặp.
  • max-time hoặc max-jobs, buộc tiến trình tự kết thúc theo chu kỳ để systemd dựng lại tiến trình mới. Đây là cách đơn giản nhất để sống chung với rò rỉ bộ nhớ, vốn tích tụ dần trong tiến trình PHP chạy dài.

Nếu job của bạn xử lý ảnh hoặc xuất file lớn, hãy nhớ chúng đọc ghi đĩa rất nhiều. Trên máy chủ ổ chậm, thời gian chạy job có thể gấp nhiều lần so với máy ổ NVMe, và nghẽn nằm ở đĩa chứ không phải ở CPU.

Quy trình deploy không làm hỏng hàng đợi

Thứ tự đúng khi đưa code mới lên:

TerminalQuy trình deploy không làm hỏng hàng đợibash
php artisan down                # tạm dừng nhận yêu cầu mới, tùy chọngit pull && composer install --no-dev -ophp artisan migrate --forcephp artisan config:cache && php artisan route:cachephp artisan queue:restart       # bao worker ket thuc job hien tai roi thoatphp artisan up

Lệnh queue:restart không giết tiến trình ngay mà báo cho worker kết thúc job đang làm rồi tự thoát, sau đó systemd dựng lại tiến trình mới với code mới. Đây là cách dừng an toàn, khác hẳn với việc giết tiến trình vốn có thể làm job đang chạy dở nằm ở trạng thái nửa vời.

Với thay đổi database, thêm một nguyên tắc: đừng đổi cấu trúc tham số của job cũ trong cùng một lần deploy. Trong hàng đợi có thể còn job đã xếp hàng theo định dạng cũ, và chúng sẽ hỏng hàng loạt khi worker mới đọc không hiểu. Cách an toàn là giữ tương thích một vòng phát hành rồi mới dọn.

Chọn nơi lưu hàng đợi theo tải thật

Nơi lưuPhù hợpƯuNhược
Cơ sở dữ liệu quan hệDưới vài nghìn việc mỗi ngàyKhông cần cài thêm gìThêm tải cho cơ sở dữ liệu chính
Bộ nhớ đệm dạng khóa giá trịVài nghìn tới hàng chục nghìn việcNhanh, nhẹPhải bật ghi xuống ổ, nếu không mất việc khi khởi động lại
Dịch vụ hàng đợi chuyên biệtKhối lượng lớn, nhiều loại việcBền và nhiều tính năngThêm một dịch vụ phải vận hành

Lời khuyên: bắt đầu bằng cơ sở dữ liệu quan hệ vì không phải cài gì thêm. Chuyển sang bộ nhớ đệm khi đo thấy hàng đợi làm chậm cơ sở dữ liệu chính. Đừng dựng dịch vụ chuyên biệt khi chưa cần.

Tiến trình xử lý chạy như một dịch vụ hệ thống

TerminalTiến trình xử lý chạy như một dịch vụ hệ thốngbash
# Chay bang tay la chet khi ban dong phien dang nhap.# Phai chay nhu mot dich vu, tu khoi dong lai khi chet. # Vi du dat trong /etc/systemd/system/queue-worker.service# [Unit]# Description=Worker xu ly hang doi# After=network.target## [Service]# User=www-data# Restart=always# RestartSec=5# ExecStart=/usr/bin/php /var/www/ungdung/artisan queue:work --sleep=3 --tries=3 --max-time=3600## [Install]# WantedBy=multi-user.target systemctl daemon-reloadsystemctl enable --now queue-workersystemctl status queue-worker # Xem nhat ky khi co su cojournalctl -u queue-worker -n 100 --no-pager

Hai tham số đáng chú ý: giới hạn số lần thử lại để việc lỗi không lặp vô hạn, và giới hạn thời gian sống của tiến trình để nó tự khởi động lại định kỳ, tránh rò rỉ bộ nhớ tích tụ.

Ba việc phải làm để không mất việc

  • Bật bảng lưu việc thất bại và có người xem nó. Việc lỗi rơi vào hư không là mất đơn hàng, mất thư xác nhận.
  • Đặt cảnh báo khi số việc thất bại vượt ngưỡng, thay vì phát hiện khi khách phàn nàn.
  • Khởi động lại tiến trình xử lý sau mỗi lần triển khai mã mới. Bước này hay bị quên, và hậu quả là tiến trình cũ chạy mã cũ, gây lỗi rất khó hiểu.

Câu hỏi thường gặp

Nên chạy bao nhiêu worker?

Bắt đầu bằng số bằng số nhân CPU rồi quan sát. Job chủ yếu chờ mạng thì tăng thêm vẫn có lợi vì tiến trình rảnh trong lúc chờ. Job nặng tính toán hoặc nặng đọc ghi đĩa thì vượt số nhân chỉ làm mọi thứ chậm đều.

Queue database có đủ cho ứng dụng thật không?

Đủ cho phần lớn ứng dụng vừa và nhỏ, nhất là khi lượng job dưới vài nghìn mỗi ngày. Dấu hiệu cần chuyển sang Redis là truy vấn vào bảng job bắt đầu xuất hiện trong danh sách truy vấn chậm hoặc worker phải chờ lâu mới lấy được việc.

Vì sao job vẫn chạy code cũ sau khi deploy?

Vì tiến trình worker nạp mã nguồn vào bộ nhớ lúc khởi động và giữ nguyên cho tới khi thoát. Phải chạy queue:restart sau mỗi lần deploy. Đưa lệnh này vào script deploy để không bao giờ quên.

Có nên dùng Horizon không?

Horizon cho giao diện theo dõi hàng đợi và số liệu thông lượng, rất tiện khi bạn có nhiều loại job và cần nhìn tổng thể. Nó yêu cầu Redis. Với một hai worker đơn giản thì systemd cộng cảnh báo job lỗi là đủ.

Chia sẻ:Facebook

Thanh Nguyễn

TND

Tư vấn bản quyền, VPS và máy chủ kèm hóa đơn VAT, hợp đồng đầy đủ cho doanh nghiệp.