NỘI DUNG

Hosting tốc độ cao Vietnix - tốc độ tải trang trung bình dưới 1 giây
VPS siêu tốc Vietnix - trải nghiệm mượt mà, ổn định
16/06/2023
Lượt xem

Hướng dẫn chạy job PHP nhiều lần một phút với Crontab trên Ubuntu 20.04

16/06/2023
20 phút đọc
Lượt xem

Đánh giá

5/5 - (59 bình chọn)

Khi xử lý logic nghiệp vụ, đôi khi ứng dụng cần phải thực thi lặp đi lặp lại một số tác vụ liên tục trong khoảng thời gian ngắn. Việc này không chỉ đảm bảo tính nhất quán của hệ thống mà còn có tác dụng nâng cao trải nghiệm người dùng. Trong bài viết sau đây, Vietnix sẽ hướng dẫn các bạn cách chạy job PHP nhiều lần một phút với Crontab trên Ubuntu 20.04.

Giới thiệu về Crontab

Crontab là một công cụ hỗ trợ các tác vụ nền theo thời gian của Linux. Dù thực hiện tốt việc liên tục lặp lại các tác vụ, tuy nhiên hạn chế của nó là bạn chỉ có thể thực hiện các tác vụ trong khoảng thời gian tối thiểu là 1 phút.

Trong nhiều trường hợp, việc này có thể ảnh hưởng đến trải nghiệm người dùng của ứng dụng. Chẳng hạn như một website cần lập lịch một tác vụ xử lý file bằng job-queue model (mô hình hàng đợi job), thời gian đợi lâu sẽ ảnh hưởng tiêu cực đến người dùng cuối.

Việc này có thể được khắc phục bằng cách sử dụng một vòng lặp với PHP script. Vòng lặp này sẽ liên tục xử lý các tác vụ cần thiết trong khoảng thời gian 60 giây đợi daemon gọi thực thi lần tiếp theo. Nhờ đó các tác vụ của ứng dụng có thể thực thi theo đúng thời gian và logic nghiệp vụ mà không khiến người dùng chờ đợi.

Bài viết này sẽ hướng dẫn các bạn cách tạo mẫu một cơ sở dữ liệu cron_jobs với bảng task trên server Ubuntu 20.04, sau đó tạo script thực thi vòng lặp PHP while(…){…} và hàm sleep() để xử lý các tác vụ trong bảng mỗi 5 giây.

Chuẩn bị để chạy job PHP nhiều lần một phút với Crontab trên Ubuntu 20.04

Các bạn cần chuẩn bị một số việc sau trước khi bắt đầu thực hiện:

  • Một server Ubuntu 20.04 và một tài khoản người dùng non-root.
  • Cài đặt LAMP stack (Linux, Apache, MySQL, PHP) trên server.

Bước 1 – Cấu hình cơ sở dữ liệu

Đầu tiên, bạn đăng nhập vào server và kết nối MySQL bằng tài khoản người dùng root để tạo mẫu một cơ sở dữ liệu.

$ sudo mysql -u root -p

Nhập mật khẩu người dùng MySQL server, ENTER và tạo cơ sở dữ liệu cron_jobs bằng lệnh dưới đây.

mysql> CREATE DATABASE cron_jobs;

Tiếp tục tạo một người dùng non-root. Người dùng này sẽ cung cấp các thông tin xác thực để PHP có thể kết nối cron_jobs. Bạn nên chọn một mật khẩu mạnh cho EXAMPLE_PASSWORD.

mysql> CREATE USER 'cron_jobs_user'@'localhost' IDENTIFIED WITH mysql_native_password BY 'EXAMPLE_PASSWORD';
mysql> GRANT ALL PRIVILEGES ON cron_jobs.* TO 'cron_jobs_user'@'localhost';
mysql> FLUSH PRIVILEGES;

Truy cập cơ sở dữ liệu cron_jobs bằng lệnh sau.

mysql> USE cron_jobs;
Output
Database changed

Các bạn tạo bảng task và thêm một số tác vụ mà cron job thực thi tự động. Những tác vụ này sẽ được thực thi cách 5 giây thay vì 1 phút sau khi bạn sử dụng một PHP script để ghi đè các thiết lập mặc định.

Tạo bảng task bằng lệnh truy vấn sau.

mysql> CREATE TABLE tasks (
mysql>     task_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
mysql>     task_name VARCHAR(50),          
mysql>     queued_at DATETIME,     
mysql>     completed_at DATETIME,
mysql>     is_processed CHAR(1)    
mysql> ) ENGINE = InnoDB;

Các bạn thêm 3 record (bản ghi) mới vào bảng. Hàm NOW() ở cột queued_at sẽ ghi lại thời gian tác vụ được thực thi. Hàm CURDATE() ở cột completed_at có giá trị mặc định là 00:00:00 và sẽ được script cập nhật sau khi tác vụ thực thi hoàn tất.

mysql> INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 1', NOW(), CURDATE(), 'N');
mysql> INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 2', NOW(), CURDATE(), 'N');
mysql> INSERT INTO tasks (task_name, queued_at, completed_at, is_processed) VALUES ('TASK 3', NOW(), CURDATE(), 'N');

Kiểm tra output sau mỗi lệnh INSERT.

Output
Query OK, 1 row affected (0.00 sec)
...

Để chắc chắn dữ liệu đã thêm thành công vào tasks, các bạn gọi lệnh truy vấn SELECT sau.

mysql> SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;

Danh sách các tác vụ sẽ được hiển thị.

Output
+-----------+---------------+-----------------------------+----------------------------+------------------+
 | task_id | task_name |        queued_at           |     completed_at        | is_processed |
+-----------+---------------+-----------------------------+----------------------------+------------------+
 |       1     |   TASK 1    | 2021-03-06 06:27:19 | 2021-03-06 00:00:00 |        N            |
 |       2     |   TASK 2    | 2021-03-06 06:27:28 | 2021-03-06 00:00:00 |        N            |
 |       3     |   TASK 3    | 2021-03-06 06:27:36 | 2021-03-06 00:00:00 |        N            |
+-----------+---------------+-----------------------------+----------------------------+------------------+
3 rows in set (0.00 sec)

Thoát MySQL CLI (command-line interface).

mysql> QUIT;
Output
Bye

Sau khi tạo bảng tasks và cơ sở dữ liệu cron_jobs, bước tiếp theo các bạn sẽ tạo một script PHP để xử lý tác vụ.

Nếu bạn đang tìm kiếm một dịch vụ VPS chất lượng để chạy job PHP nhiều lần một phút với Crontab, bạn có thể tham khảo lựa chọn VPS của Vietnix. Với các gói VPS đa dạng và phù hợp với nhu cầu của bạn như VPS Giá Rẻ, Cloud Server, VPS Cao Cấp, VPS NVMe, VPS GPU, Vietnix đảm bảo cung cấp cho bạn một môi trường ổn định và hiệu suất cao để thực hiện các công việc PHP của bạn một cách hiệu quả.

Bước 2 – Tạo và cấu hình script PHP chạy mỗi 5 giây

Các bạn sẽ kết hợp vòng lặp while(…){…} và hàm sleep của PHP để xử lý tác vụ sau một khoảng thời gian nhất định.

Dùng nano để tạo mới file /var/www/html/tasks.php trong thư mục root của web server.

$ sudo nano /var/www/html/tasks.php

Mở thẻ <?php và thêm block try { để khai báo các biến được tạo trong cơ sở dữ liệu ở bước 1. Đừng quên thay EXAMPLE_PASSWORD bằng mật khẩu cơ sở dữ liệu của người dùng.

<?php
try {
    $db_name     = 'cron_jobs';
    $db_user     = 'cron_jobs_user';
    $db_password = 'EXAMPLE_PASSWORD';
    $db_host     = 'localhost';

Tạo đối tượng PDO (PHP Data Object) và thiết lập thuộc tính ERRMODE_EXCEPTION để bắt các lỗi phát sinh của PDO, đồng thời gán giá trị false cho thuộc tính ATTR_EMULATE_PREPARES để engine của cơ sở dữ liệu MySQL xử lý việc giả lập. Các lệnh truy vấn SQL và dữ liệu nên được tách biệt để tăng cường bảo mật cũng như giảm thiểu nguy cơ bị lỗ hổng SQL injection.

$pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);

Biến $loop_expiry_time được tạo tiếp theo có giá trị là một phút sau khi khởi tạo. Biến được sử dụng để dừng vòng lặp PHP while(time() < $loop_expiry_time) { khi time() khớp với giá trị của $loop_expiry_time.

    $loop_expiry_time = time() + 60;

    while (time() < $loop_expiry_time) {

Khai báo một truy vấn SQL để lấy các job chưa chạy trong bảng tasks.

        $data = [];
        $sql  = "select 
                 task_id
                 from tasks
                 where is_processed = :is_processed
                 ";

Thực thi truy vấn và lấy về danh sách các dòng có giá trị cột is_processed là N. Đây là những dòng tác vụ chưa được chạy.

        $data['is_processed'] = 'N';  

        $stmt = $pdo->prepare($sql);
        $stmt->execute($data);

Duyệt qua danh sách trên bằng vòng lặp PHP while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {…}. Tiếp tục gọi một truy vấn SQL để cập nhật giá trị cột is_processedcomplete_at sau khi tác vụ được chạy. Việc này sẽ đảm bảo mỗi tác vụ chỉ thực thi một lần.

        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { 
            $data_update   = [];         
            $sql_update    = "update tasks set 
                              is_processed  = :is_processed,
                              completed_at  = :completed_at
                              where task_id = :task_id                                 
                              ";

            $data_update   = [		          
                             'is_processed' => 'Y',                          
                             'completed_at' => date("Y-m-d H:i:s"),
                             'task_id'      => $row['task_id']                         
                             ];
            $stmt = $pdo->prepare($sql_update);
            $stmt->execute($data_update);
        }

Lưu ý: Nếu hàng đợi quá lớn (giả sử bạn cần thực thi 100,000 tác vụ một giây), các bạn có thể xem xét sử dụng Redis Server có tốc độ nhanh hơn MySQL để xử lý tác vụ. Bộ dữ liệu trong bài viết này có kích thước nhỏ hơn trường hợp nêu trên rất nhiều.

Bổ sung lời gọi hàm sleep(5) trước khi kết thúc vòng lặp PHP while (time() < $loop_expiry_time) { đầu tiên để tạm ngưng thực thi tác vụ trong 5 giây và giải phóng tài nguyên server.

Tùy theo logic nghiệp vụ cũng như tốc độ thực thi tác vụ nhanh đến đâu mà bạn có thể điều chỉnh khoảng thời gian 5 giây tạm ngưng. Giả như các tác vụ cần thực thi 3 lần một phút thì bạn có thể chọn 20 giây là khoảng thời gian nghỉ.

Thêm block } catch (PDOException $ex) { echo $ex->getMessage(); } để bắt các thông điệp lỗi phát sinh của PDO trong quá trình chạy.

        sleep(5); 

        }       
		
} catch (PDOException $ex) {
    echo $ex->getMessage(); 
}

Dưới đây là file tasks.php hoàn chỉnh.

<?php
try {
    $db_name     = 'cron_jobs';
    $db_user     = 'cron_jobs_user';
    $db_password = 'EXAMPLE_PASSWORD';
    $db_host     = 'localhost';

    $pdo = new PDO('mysql:host=' . $db_host . '; dbname=' . $db_name, $db_user, $db_password);
    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  
    $pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);               

    $loop_expiry_time = time() + 60;

    while (time() < $loop_expiry_time) { 
        $data = [];
        $sql  = "select 
                 task_id
                 from tasks
                 where is_processed = :is_processed
                 ";

        $data['is_processed'] = 'N';             

        $stmt = $pdo->prepare($sql);
        $stmt->execute($data);

        while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) { 
            $data_update   = [];         
            $sql_update    = "update tasks set 
                              is_processed  = :is_processed,
                              completed_at  = :completed_at
                              where task_id = :task_id                                 
                              ";

            $data_update   = [		          
                             'is_processed' => 'Y',                          
                             'completed_at' => date("Y-m-d H:i:s"),
                             'task_id'      => $row['task_id']                         
                             ];
            $stmt = $pdo->prepare($sql_update);
            $stmt->execute($data_update);
        }   
       
        sleep(5); 

        }       
		
} catch (PDOException $ex) {
    echo $ex->getMessage(); 
}

Lưu file với tổ hợp phím CTRL + X, Y rồi ENTER.

Đến đây, bạn đã hoàn tất việc thiết lập logic trong file /var/www/html/tasks.php. Bước tiếp theo, các bạn sẽ lập lịch để file được gọi mỗi 1 phút bởi daemon crontab.

Bước 3 – Lên lịch chạy script PHP mỗi phút

Chỉ cần thêm một dòng lệnh vào file crontab, Linux có thể lập lịch chạy tự động các job sau một khoảng thời gian nhất định. Ở bước này, các bạn sẽ được hướng dẫn cách thiết lập crontab daemon chạy tự động script /var/www/html/tasks.php mỗi 60 giây. Mở file /etc/crontab bằng trình biên tập văn bản nano.

$ sudo nano /etc/crontab

Thêm vào cuối file dòng lệnh dưới đây để daemon gọi thực thi http://localhost/tasks.php sau mỗi phút.

...
* * * * * root /usr/bin/wget -O - http://localhost/tasks.php

Đóng và lưu file.

Như đã đề cập, dù script task.php chỉ được gọi một lần bởi daemon, các vòng lặp trong script sẽ tiếp tục thực thi các tác vụ chưa được xử lý trong vòng 60 giây kế tiếp. Một khi vòng lặp ngừng, daemon sẽ gọi lại script và tiếp tục lặp lại quy trình trên.

Sau khi lưu thiết lập, crontab sẽ lập tức thực thi các tác vụ trong bảng tasks trong cơ sở dữ liệu. Để đảm bảo mọi thứ hoạt động ổn định, các bạn đến bước cuối cùng là kiểm tra cơ sở dữ liệu cron_jobs.

Bước 4 – Xác nhận các job thực thi thành công

Mở lại cơ sở dữ liệu với tài khoản người dùng root để kiểm tra việc crontab tự động thực thi job bằng script tasks.php.

$ sudo mysql -u root -p

Sau khi nhập mật khẩu, các bạn kết nối cơ sở dữ liệu.

mysql> USE cron_jobs;
Output
Database changed

Gọi truy vấn SELECT sau cho bảng tasks.

SELECT task_id, task_name, queued_at, completed_at, is_processed FROM tasks;

Nhìn vào cột completed_at của output, bạn sẽ thấy cứ mỗi 5 giây có một tác vụ được chạy. Những tác vụ này cũng được đánh dấu đã thực thi bằng ký tự Y nghĩa là YES ở cột is_processed.

Output

+-----------+---------------+----------------------------+-----------------------------+-------------------+
 | task_id | task_name |       queued_at           |      completed_at        | is_processed |
+-----------+---------------+----------------------------+-----------------------------+-------------------+
 |       1     |   TASK 1    | 2021-03-06 06:27:19 | 2021-03-06 06:30:01 |          Y            |
 |       2     |   TASK 2    | 2021-03-06 06:27:28 | 2021-03-06 06:30:06 |          Y            |
 |       3     |   TASK 3    | 2021-03-06 06:27:36 | 2021-03-06 06:30:11 |          Y            |
+-----------+---------------+-----------------------------+----------------------------+-------------------+
3 rows in set (0.00 sec)

Đến đây, script PHP của bạn đã hoạt động suôn sẻ. Các bạn đã có thể xử lý các tác vụ cần thiết với khoảng thời gian nghỉ ngắn hơn 1 phút sau khi ghi đè các thiết lập mặc định của crontab daemon.

Lời kết

Hy vọng các bạn đã nắm được cách chạy job PHP nhiều lần một phút với Crontab trên Ubuntu 20.04 sau bài hướng dẫn trên. Nếu có bất kỳ thắc mắc hay phản hồi nào, các bạn hãy để lại bình luận bên dưới để được Vietnix giải đáp.

THEO DÕI VÀ CẬP NHẬT CHỦ ĐỀ BẠN QUAN TÂM

Đăng ký ngay để nhận những thông tin mới nhất từ blog của chúng tôi. Đừng bỏ lỡ cơ hội truy cập kiến thức và tin tức hàng ngày

Chọn chủ đề :

Hưng Nguyễn

Co-Founder
tại

Kết nối với mình qua

Kết nối với mình qua

Theo dõi
Thông báo của
guest
0 Comments
Phản hồi nội tuyến
Xem tất cả bình luận

Tăng tốc độ website - Nâng tầm giá trị thương hiệu

Banner group
Tăng tốc tải trang

95 điểm

Nâng cao trải nghiệm người dùng

Tăng 8% tỷ lệ chuyển đổi

Thúc đẩy SEO, Google Ads hiệu quả

Tăng tốc ngay

SẢN PHẨM NỔI BẬT

Pattern

7 NGÀY DÙNG THỬ HOSTING

NẮM BẮT CƠ HỘI, THÀNH CÔNG DẪN LỐI

Cùng trải nghiệm dịch vụ hosting tốc độ cao được hơn 100,000 khách hàng sử dụng

Icon
ĐĂNG KÝ NHẬN TÀI LIỆU THÀNH CÔNG
Cảm ơn bạn đã đăng ký nhận tài liệu mới nhất từ Vietnix!
ĐÓNG

ĐĂNG KÝ DÙNG THỬ HOSTING

Asset

7 NGÀY MIỄN PHÍ

Asset 1

ĐĂNG KÝ DÙNG THỬ HOSTING

Asset

7 NGÀY MIỄN PHÍ

Asset 1
Icon
XÁC NHẬN ĐĂNG KÝ DÙNG THỬ THÀNH CÔNG
Cảm ơn bạn đã đăng ký thông tin thành công. Đội ngũ CSKH sẽ liên hệ trực tiếp để kích hoạt dịch vụ cho bạn nhanh nhất!
ĐÓNG