Series PHP cURL Extension | Bài 4: Vũ khí tối thượng "cURL Multi" - Gọi API bất đồng bộ (Asynchronous)
Chào anh em, trải qua 3 bài viết trước, chúng ta đã thuần thục các kỹ năng cơ bản và nâng cao với cURL. Tuy nhiên, tất cả những gì chúng ta làm cho đến giờ đều là Đồng bộ (Synchronous).Nghĩa là sao? Khi bạn gọi hàm curl_exec(), toàn bộ tiến trình PHP sẽ bị "đóng băng" (I/O blocking) để chờ server đích phản hồi.Hãy tưởng tượng bạn đang viết một module tổng hợp dữ liệu. Bạn cần gọi 3 API khác nhau: API lấy thông tin User (mất 1s), API lấy Lịch sử giao dịch (mất 2s), và API check Trạng thái vé (mất 1.5s). Nếu code tuần tự theo cách cũ, thời gian tổng cộng user phải chờ là:.Trong thế giới hệ thống phân tán và microservices, con số 4.5s là một thảm họa về mặt hiệu năng. Hôm nay, chúng ta sẽ giải quyết bài toán này bằng cURL Multi – công cụ giúp PHP gọi nhiều API cùng một lúc. Khi đó, thời gian tổng chỉ bằng thời gian của API chậm nhất:.
1. Cơ chế hoạt động của cURL Multi
Thay vì khởi tạo và thực thi ngay lập tức từng request, curl_multi hoạt động theo cơ chế gom nhóm:
- Tạo một "Bể chứa" (Multi Handle): Dùng curl_multi_init().
- Tạo các request con (Single Handle): Dùng curl_init() như bình thường, nhưng KHÔNG gọi curl_exec().
- Quăng các request con vào Bể chứa: Dùng curl_multi_add_handle().
- Bấm nút chạy đồng loạt: Dùng một vòng lặp do-while kết hợp curl_multi_exec() để xử lý song song.
- Thu thập kết quả và dọn dẹp: Lấy data của từng request và đóng các kết nối.
2. Code thực hành: Gọi 3 API cùng lúc
Dưới đây là đoạn code kinh điển để xử lý cURL Multi. Trông có vẻ hơi dài và phức tạp hơn bình thường, nhưng logic rất chặt chẽ.
<?php
// 1. Danh sách các API cần gọi đồng thời
$apiUrls = [
'user' => 'https://jsonplaceholder.typicode.com/users/1',
'posts' => 'https://jsonplaceholder.typicode.com/users/1/posts',
'todos' => 'https://jsonplaceholder.typicode.com/users/1/todos'
];
// 2. Khởi tạo "Bể chứa" Multi cURL
$mh = curl_multi_init();
$curlHandles = [];
// 3. Khởi tạo từng request con và ném vào Bể chứa
foreach ($apiUrls as $key => $url) {
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5); // Luôn nhớ set timeout
// Lưu lại handle con để lát nữa lấy kết quả
$curlHandles[$key] = $ch;
// Thêm handle con vào handle tổng
curl_multi_add_handle($mh, $ch);
}
// 4. THỰC THI ĐỒNG LOẠT (Vòng lặp cốt lõi của Asynchronous)
$active = null;
do {
// curl_multi_exec sẽ chạy ngầm các request và cập nhật biến $active
// $active > 0 nghĩa là vẫn còn request đang chạy
$status = curl_multi_exec($mh, $active);
// Nếu có data mới phản hồi trên socket, hàm này sẽ tiếp tục vòng lặp
// Tránh việc CPU bị ăn 100% trong lúc chờ đợi (Busy waiting)
if ($active) {
curl_multi_select($mh);
}
} while ($active && $status == CURLM_OK);
// 5. Thu thập kết quả
$responses = [];
foreach ($curlHandles as $key => $ch) {
// Lấy nội dung trả về của từng request
$responses[$key] = curl_multi_getcontent($ch);
// Gỡ request con ra khỏi Bể chứa và đóng lại
curl_multi_remove_handle($mh, $ch);
curl_close($ch);
}
// 6. Đóng Bể chứa
curl_multi_close($mh);
// 7. Hiển thị kết quả (Dữ liệu đã được lấy về gần như cùng lúc)
echo "Lấy dữ liệu thành công!\n";
echo "- Chiều dài data User: " . strlen($responses['user']) . " bytes\n";
echo "- Chiều dài data Posts: " . strlen($responses['posts']) . " bytes\n";
echo "- Chiều dài data Todos: " . strlen($responses['todos']) . " bytes\n";
?>
Điểm mấu chốt cần lưu ý:
Hàm curl_multi_select($mh) là cực kỳ quan trọng. Nhờ có nó, tiến trình PHP sẽ tạm nghỉ ngơi (sleep) trong vài mili-giây khi không có gói tin mạng nào truyền về, giúp tiết kiệm CPU cho server. Nếu bỏ dòng này đi, vòng lặp do-while sẽ chạy liên tục không kiểm soát và "ăn" sạch tài nguyên hệ thống.
Tổng kết Series
Vậy là chúng ta đã cùng nhau đi từ những khái niệm vỡ lòng của libcurl, cách thiết lập Payload, vượt rào Authentication cho đến việc tối ưu hiệu năng bằng Asynchronous.
Trong thực tế hiện nay, các dự án hiện đại thường dùng các thư viện như GuzzleHTTP hoặc HTTP Client (của Laravel) để code gọn gàng hơn. Tuy nhiên, bên dưới lớp vỏ bọc hào nhoáng đó, bản chất vẫn là những hàm curl_* thuần túy mà chúng ta vừa tìm hiểu. Việc nắm vững gốc rễ sẽ giúp anh em tự tin debug và thiết kế hệ thống vững chãi hơn rất nhiều.
Cảm ơn anh em đã theo dõi series. Chúc anh em viết code ít bug và server luôn "xanh"!
All Rights Reserved