Ngiêm cấm sử đụng PROXY và VPS vào mục đích trái pháp luật bạn sẽ phải chịu toàn bộ trách nhiệm trước pháp luật khi sử dụng dịch vụ của chúng tôi tks!
API trả 202 và cách polling: viết client chờ job đúng cách

API trả 202 và cách polling: viết client chờ job đúng cách

Chuyên viên Kỹ thuật ProxyVN 01-03-2026 Đang tính... phút đọc

API trả 202 và cách polling là bài toán gặp khi gọi endpoint xử lý lâu như xuất báo cáo hay chuyển đổi tệp. Mã 202 Accepted chỉ báo máy chủ đã nhận việc, chưa báo việc đã xong. ProxyVN dựng một API lab trả 202 kèm job ID, viết client Python chờ job có deadline và backoff, rồi chạy 11 ca gồm job lỗi, job bị hủy, 429 và đi qua proxy.

API trả 202 và cách polling: 202 nghĩa là gì và ai quyết định hành vi

Nhiều người nhìn thấy mã 2xx là coi như thành công rồi đọc body như dữ liệu cuối. Với 202 làm vậy là sai từ bước đầu. Phần này nói rõ 202 cam kết gì, không cam kết gì, và phần nào của luồng chờ do API quy định chứ không do HTTP quy định.

202 chỉ là biên nhận

Theo RFC 9110 mục 15.3.3, 202 nghĩa là yêu cầu đã được nhận để xử lý nhưng việc xử lý chưa xong. Tiêu chuẩn còn nói thẳng rằng việc xử lý có thể bị hủy hoặc không bao giờ diễn ra. Máy chủ không có cách gửi lại mã trạng thái của tác vụ bất đồng bộ trên cùng kết nối.

Trang 202 Accepted trên MDN mô tả cùng ý: 202 dùng khi máy chủ giao việc cho tiến trình khác hoặc xử lý theo lô. Body nên cho client biết trạng thái hiện tại và chỗ để theo dõi.

Nhận được 202 thì việc mới bắt đầu, client vẫn phải hỏi lại cho tới khi job có trạng thái kết thúc.

HTTP không quy định cách theo dõi

Tiêu chuẩn chỉ nói nên có chỗ để xem trạng thái. Nó không quy định tên trường, tên trạng thái, khoảng chờ hay cách trả kết quả. Mỗi API tự chọn: có nơi trả header Location trỏ tới /jobs/{id}, có nơi chỉ trả id trong JSON, có nơi đẩy kết quả về webhook.

Vì vậy đừng mang một client polling từ API này sang API khác rồi nghĩ nó sẽ chạy đúng. Trước khi viết code, đọc tài liệu của chính API đó để biết bốn điều: chỗ hỏi trạng thái, các giá trị trạng thái, tín hiệu chờ và cách lấy kết quả.

Mẫu phổ biến mà bài dùng

Trang Asynchronous Request-Reply pattern của Microsoft Azure mô tả mẫu khá phổ biến. Client gửi POST, nhận 202 kèm Location trỏ tới status endpoint và có thể có Retry-After. Client hỏi status endpoint, nhận 200 khi job còn chạy, rồi nhận 303 See Other trỏ tới kết quả khi xong.

API lab trong bài đi theo mẫu này. Lý do chọn: mẫu có đủ các điểm hay gây lỗi như Location tương đối, Retry-After và chuyển hướng 303. Nắm được mẫu này thì đọc tài liệu API khác cũng nhanh hơn.

Ranh giới với bài 429 và rate limit

Polling có chờ và có backoff nên dễ bị nhầm với xử lý lỗi 429. Hai việc khác nhau. Với 429 client đang bị từ chối và phải chờ để gửi lại cùng request. Với 202 việc đã được nhận, client chỉ hỏi trạng thái.

Bài rate limit API khi dùng proxy đã nói về hạn mức và cách giãn request. Bài này chỉ dùng phần đó khi status endpoint trả 429 trong lúc poll. Thân bài tập trung vào job ID, trạng thái, deadline, hủy job và lấy kết quả.

Thiết kế client cho API trả 202 và cách polling qua status endpoint

Năm câu hỏi của client polling

Năm câu hỏi của client polling

Client polling tốt cần trả lời được năm câu: hỏi ở đâu, hỏi bao lâu một lần, khi nào dừng, dừng vì lý do gì, và nếu bị ngắt giữa chừng thì làm tiếp ra sao. Phần này đi qua từng câu theo thứ tự client gặp phải.

Đọc job ID và Location

Bước đầu là lấy địa chỉ status endpoint từ phản hồi 202. Nếu API trả header Location, ưu tiên dùng nó thay vì tự ghép URL từ id. Location thường là đường dẫn tương đối như /jobs/8fdbe2c0, nên client phải ghép với URL gốc của request.

Lưu job ID vào nhật ký hoặc cơ sở dữ liệu ngay sau khi nhận. Nếu tiến trình chết giữa chừng, có id thì chạy tiếp được mà không phải gửi lại POST và tạo thêm một job trùng.

Khoảng chờ giữa hai lần hỏi

Có ba nguồn để quyết định khoảng chờ, xếp theo thứ tự ưu tiên:

  • Retry-After do máy chủ gửi: giá trị số giây hoặc ngày giờ HTTP, theo mô tả Retry-After trên MDN.
  • Gợi ý trong tài liệu API: ví dụ tài liệu ghi job xuất báo cáo thường mất từ 30 giây tới 5 phút.
  • Backoff của client: tăng dần khoảng chờ, có trần và có nhiễu ngẫu nhiên.

Lab đo được sự khác biệt rõ: cùng một job 6 giây, client hỏi cố định mỗi 0,2 giây gửi 30 request, client theo Retry-After chỉ gửi 8 request. Kết quả về tay cùng thời điểm, máy chủ nhận ít hơn gần 4 lần.

Nhiễu ngẫu nhiên và trần

Khi nhiều worker cùng nhận 202 trong cùng một giây, nếu tất cả chờ đúng 1 giây rồi hỏi lại thì status endpoint nhận dồn request theo nhịp. Nhân khoảng chờ với một hệ số ngẫu nhiên nhỏ, ví dụ từ 0,9 tới 1,1, sẽ làm các lần hỏi lệch nhau.

Bài timeouts, retries and backoff with jitter của AWS Builders' Library phân tích kỹ lý do thêm jitter. Trần khoảng chờ thì giữ để job xong rồi client không phải chờ quá lâu mới biết.

Deadline tổng

Mỗi request đơn lẻ có timeout riêng, ví dụ 10 giây. Nhưng cả quá trình chờ cũng cần một deadline tổng, vì job có thể kẹt ở running mãi. Client nên dừng khi lần chờ kế tiếp sẽ vượt deadline, không đợi tới khi đã vượt.

Khi dừng vì deadline, client không được coi job là lỗi. Job vẫn có thể đang chạy. Báo lại id và URL trạng thái để lượt sau tiếp tục hỏi, hoặc để người vận hành quyết định hủy.

Trạng thái kết thúc

Client cần một danh sách trạng thái kết thúc rõ ràng, lấy từ tài liệu API. Lab dùng ba trạng thái: succeeded, failed và canceled. Mọi giá trị khác, kể cả giá trị lạ chưa có trong tài liệu, được coi là chưa xong và tiếp tục hỏi trong deadline.

Cách làm ngược lại, chỉ dừng khi thấy succeeded, sẽ khiến job lỗi bị hỏi tới hết deadline. Lab ở ca 03 cho thấy client dừng ngay ở lần hỏi đầu tiên thấy failed và trả về mã lỗi của job.

Lab kiểm thử API trả 202 và cách polling: phiên bản và kết quả kỳ vọng

Để có kết quả lặp lại được, ProxyVN không thử trên API thương mại mà tự dựng một máy chủ job nhỏ. Máy chủ cố ý trả các tình huống khó, nhờ đó mỗi ca kiểm có kết quả kỳ vọng ghi trước khi chạy.

Môi trường

Lab chạy ngày 29/10/2026 trên Windows 10, Python 3.12.10, requests 2.34.2 và urllib3 2.8.0. Máy chủ job dùng ThreadingHTTPServer của thư viện chuẩn, nghe ở 127.0.0.1 cổng 9520. Proxy HTTP tự dựng nghe cổng 9521, yêu cầu Basic auth với tài khoản user1 và mật khẩu có ký tự @ và #, ghi nhật ký mỗi request.

Session của client đặt trust_env bằng False để biến môi trường proxy trên máy không lẫn vào kết quả. Ca nào cần proxy thì khai báo proxy trực tiếp trong session.

Hành vi của máy chủ job

Máy chủ có ba endpoint: POST /reports tạo job, GET /jobs/{id} xem trạng thái, POST /jobs/{id}/cancel yêu cầu hủy. Thời gian chạy của job do client gửi trong body, để tạo job nhanh hoặc chậm theo ý.

Đây là phần lõi của endpoint trạng thái:

if st == "succeeded":
    return self.send(303, {"id": jid, "status": st},
                     {"Location": f"/results/{jid}"})
if st == "failed":
    return self.send(200, {"id": jid, "status": st,
                     "error": {"code": "SOURCE_TIMEOUT",
                               "message": "upstream export timed out"}})
if st == "canceled":
    return self.send(200, {"id": jid, "status": st})
return self.send(200, {"id": jid, "status": st, "progress": prog},
                 {"Retry-After": "1"})

Khi job còn queued hoặc running, máy chủ trả 200 và Retry-After bằng 1. Khi job xong, máy chủ trả 303 trỏ tới /results/{id}. Chế độ throttle trả 429 kèm Retry-After bằng 2 ở mỗi lần hỏi thứ ba.

Bảng ca kiểm và kỳ vọng

Mười một ca được ghi kỳ vọng trước khi chạy. Bảng dưới là kỳ vọng và kết quả thật:

Bảng: Bảng ca kiểm và kỳ vọng

Ca

Tình huống

Kỳ vọng

Kết quả lab

01

Job 3 giây, không theo 303 tự động

Nhận kết quả sau vài lần hỏi

4 lần hỏi, 3,08 giây

02

Để requests tự theo 303

Mất dấu 303

Nhận 200 ở /results, history [303]

03

Job lỗi

Dừng ngay, trả mã lỗi

JobFailed SOURCE_TIMEOUT, 2,06 giây

04

Hủy ở lần hỏi thứ 2

Dừng với canceled

JobCanceled, 1,95 giây

05

Job 30 giây, deadline 5 giây

Dừng trước deadline, giữ URL

TimeoutError sau 5 lần hỏi, 4,11 giây

06

Status trả 429

Chờ theo Retry-After rồi hỏi tiếp

Mã 200, 200, 429, 200, 200, 429, 303

07

Gửi lại POST cùng Idempotency-Key

Cùng job

Cùng id, reused true

08

Đi qua proxy HTTP có tài khoản

Như ca 01

3 lần hỏi, proxy ghi đủ request

09

Hỏi cố định 0,2 giây, job 6 giây

Nhiều request

30 lần hỏi

10

Theo Retry-After, job 6 giây

Ít request

8 lần hỏi

11

Đọc Location

Đường dẫn tương đối

/jobs/8fdbe2c0

Không ca nào lệch kỳ vọng. Ca 02 là ca đáng chú ý nhất, vì nó cho thấy một thói quen mặc định của thư viện HTTP làm client mất thông tin.

Phần không kiểm

Lab không thử API thương mại thật, không thử webhook, không thử Server-Sent Events hay long polling. Lab không thử proxy SOCKS5 cho luồng này và không đo trên mạng có độ trễ thật. Mọi con số thời gian là trên máy cục bộ.

Code Python cho API trả 202 và cách polling có deadline, backoff

Phần này là client dùng trong lab, đã rút gọn phần in kết quả. Code dùng requests vì phổ biến, nhưng logic giữ nguyên khi đổi sang httpx hay aiohttp.

Gửi job và lấy status URL

Hàm submit gửi POST, kiểm đúng mã 202, đọc Location rồi ghép với URL gốc:

import random, time, uuid
import requests
 
class JobFailed(Exception): pass
class JobCanceled(Exception): pass
 
def submit(s, base, payload, key=None):
    key = key or str(uuid.uuid4())
    r = s.post(base + "/reports", json=payload,
               headers={"Idempotency-Key": key}, timeout=10)
    if r.status_code != 202:
        r.raise_for_status()
        raise RuntimeError(f"expected 202, got {r.status_code}")
    loc = r.headers["Location"]
    return requests.compat.urljoin(base + "/", loc), r.json(), key

Hàm trả lại cả khóa Idempotency-Key. Nếu POST bị lỗi mạng và phải gửi lại, client dùng lại khóa này để máy chủ nhận ra đó là cùng một yêu cầu. Lưu ý máy chủ phải hỗ trợ khóa này thì mới có tác dụng.

Vòng chờ có deadline

Hàm wait_job là phần chính. Nó tắt tự động theo chuyển hướng, đọc Retry-After, phân biệt các trạng thái kết thúc và kiểm deadline trước mỗi lần ngủ:

def retry_after(resp, default):
    v = resp.headers.get("Retry-After")
    try:
        return float(v)
    except (TypeError, ValueError):
        return default
 
def wait_job(s, status_url, deadline_s=60, first=1.0, cap=8.0):
    t_end = time.monotonic() + deadline_s
    delay = first
    polls = 0
    while True:
        r = s.get(status_url, timeout=10, allow_redirects=False)
        polls += 1
        if r.status_code == 303:
            res = s.get(requests.compat.urljoin(status_url, r.headers["Location"]),
                        timeout=10)
            res.raise_for_status()
            return res.json(), polls
        if r.status_code == 429:
            delay = retry_after(r, min(cap, delay * 2))
        elif r.status_code == 200:
            body = r.json()
            st = body["status"]
            if st == "succeeded":
                return body, polls
            if st == "failed":
                raise JobFailed(body.get("error"))
            if st == "canceled":
                raise JobCanceled(body["id"])
            hint = retry_after(r, None)
            delay = hint if hint is not None else min(cap, delay * 2)
        else:
            r.raise_for_status()
        sleep = min(delay, cap) * random.uniform(0.9, 1.1)
        if time.monotonic() + sleep > t_end:
            raise TimeoutError(f"deadline {deadline_s}s, job still at {status_url}, polls={polls}")
        time.sleep(sleep)

Hàm retry_after trong bản lab chỉ đọc dạng số giây. Nếu API trả Retry-After dạng ngày giờ HTTP, cần thêm nhánh dùng email.utils.parsedate_to_datetime để đổi ra số giây. Bản lab cũng không xử lý trường hợp máy chủ trả trạng thái lạ quá lâu, deadline tổng đảm nhận việc đó.

Vì sao dùng time.monotonic

Deadline tính bằng time.monotonic thay vì time.time. Đồng hồ hệ thống có thể bị chỉnh bởi đồng bộ giờ, làm deadline nhảy tới hoặc lùi. Đồng hồ monotonic chỉ tăng, phù hợp để đo khoảng thời gian.

Chạy lại từ job ID đã lưu

Khi lượt trước dừng vì deadline, lượt sau chỉ cần gọi lại wait_job với URL đã lưu, không gọi submit. Nếu chỉ lưu id thì ghép lại URL theo đúng mẫu tài liệu, ví dụ base + "/jobs/" + id.

Đây là lý do nên lưu job ID ngay khi nhận 202. Client không lưu id sẽ buộc phải gửi lại POST, và nếu API không hỗ trợ Idempotency-Key thì máy chủ tạo thêm một job làm lại toàn bộ công việc.

Tái lập 11 ca API trả 202 và cách polling, gồm job fail và cancel

Phần này đi vào chi tiết từng nhóm ca, kèm đầu ra thật của lab. Mỗi ca chạy trên một job mới để kết quả không ảnh hưởng nhau.

Luồng thành công và cái bẫy 303

Cái bẫy chuyển hướng 303

Cái bẫy chuyển hướng 303

Ca 01 tạo job 3 giây. Client nhận 202 với body id và status queued, hỏi 4 lần rồi nhận 303 và lấy kết quả. Đầu ra:

01_ok_303_result {'result': [{'id': 'da19133b', 'status': 'queued'},
  {'id': 'da19133b', 'rows': 1240, 'url': '/files/da19133b.csv'}, 4], 'sec': 3.08}
02_default_follow_303 {'result': {'final_status': 200,
  'final_url': 'http://127.0.0.1:9520/results/fa9f0c4d', 'history': [303], ...}}

Ca 02 cho thấy cái bẫy. Requests mặc định tự theo chuyển hướng với GET. Khi job xong, client hỏi /jobs/{id}, requests nhận 303 rồi lặng lẽ lấy /results/{id}, trả về mã 200 và body kết quả.

Nếu code chỉ kiểm r.json()["status"], nó sẽ nhận một body không có trường status và lỗi KeyError. Tệ hơn, nếu code coi mọi 200 là còn chạy, nó sẽ hỏi mãi một body kết quả. Tài liệu Redirection and History của requests ghi rõ hành vi tự theo chuyển hướng này.

Với status endpoint có 303, đặt allow_redirects=False rồi tự lấy Location, hoặc ít nhất kiểm r.history.

Job lỗi và job bị hủy

Ca 03 tạo job ở chế độ fail. Sau 2 giây máy chủ trả status failed kèm mã SOURCE_TIMEOUT. Client ném JobFailed với nguyên phần error của máy chủ, tổng thời gian 2,06 giây, không hỏi thêm lần nào.

Ca 04 tạo job 20 giây, gửi POST /cancel ở lần hỏi thứ hai. Máy chủ trả 202 cho yêu cầu hủy, vì hủy cũng là một việc bất đồng bộ. Lần hỏi kế tiếp trả canceled, client ném JobCanceled sau 1,95 giây.

Hai ca này cho thấy cần tách ba kết cục khác nhau: thành công, thất bại có mã lỗi, và bị hủy. Gộp thất bại với bị hủy làm một sẽ khiến báo cáo vận hành sai, vì job bị hủy thường do người dùng chủ động.

Deadline và lần chạy tiếp

Ca 05 tạo job 30 giây với deadline 5 giây. Client hỏi 5 lần rồi dừng ở giây thứ 4,11, khi lần ngủ kế tiếp sẽ vượt deadline:

05_deadline {'result': 'TimeoutError: deadline 5s, job still at
  http://127.0.0.1:9520/jobs/208c423e, polls=5', 'sec': 4.11}

Thông báo lỗi giữ nguyên URL trạng thái. Lượt chạy sau có thể tiếp tục hỏi URL này, job vẫn đang chạy ở máy chủ. Đây là điểm khác biệt giữa hết giờ chờ và job lỗi.

429 trong lúc poll

Hỏi dồn và hỏi theo Retry-After

Hỏi dồn và hỏi theo Retry-After

Ca 06 bật chế độ throttle. Status endpoint trả 429 ở mỗi lần hỏi thứ ba, kèm Retry-After bằng 2. Chuỗi mã client nhận được:

06_429_retry_after: polls=7, codes [200, 200, 429, 200, 200, 429, 303]

Client không coi 429 là job lỗi. Nó chờ đúng 2 giây theo header rồi hỏi tiếp, cuối cùng nhận 303 và kết quả. Muốn hiểu sâu hơn về 429, anh em đọc bài lỗi 429 khi dùng proxy.

Idempotency-Key khi gửi lại POST

Ca 07 gửi hai POST liên tiếp với cùng khóa rep-2026-10-29-001. Máy chủ lab trả cùng id 6ef2f149 cho cả hai, lần thứ hai có thêm reused true. Không có job thứ hai được tạo.

Header này đang được IETF chuẩn hóa trong bản nháp The Idempotency-Key HTTP Header Field. Nhiều API thanh toán và xuất dữ liệu đã hỗ trợ từ lâu, nhưng tên header và thời gian giữ khóa khác nhau tùy API. Đọc tài liệu trước khi dựa vào nó.

Khi API trả 409 vì khóa trùng nhưng nội dung khác, đó là một tình huống riêng. Bài lỗi 409 Conflict khi gọi API giải thích cách đọc lỗi này.

Đọc log để phân biệt lỗi khi làm API trả 202 và cách polling

Khi client polling chạy sai, triệu chứng thường giống nhau: chờ lâu, không có kết quả. Nguyên nhân thì nhiều. Phần này chỉ cách đọc log của client và máy chủ để tách từng nguyên nhân.

Bốn triệu chứng hay gặp

Bảng dưới gom các triệu chứng lab đã tái hiện hoặc hay gặp khi gọi API thật:

Bảng: Bốn triệu chứng hay gặp

Triệu chứng

Dấu hiệu trong log

Nguyên nhân gần nhất

Hỏi mãi tới hết deadline

Status luôn running

Job kẹt hoặc deadline quá ngắn

KeyError status

Request cuối có history [303]

Thư viện tự theo chuyển hướng

Job tạo trùng

Nhiều POST, mỗi lần một id

Gửi lại POST không có Idempotency-Key

404 ở status URL

URL ghép sai hoặc job đã hết hạn

Location tương đối ghép sai, kết quả bị xóa

Mỗi dòng cần một hành động khác nhau. Job kẹt thì liên hệ bên API hoặc hủy. Chuyển hướng thì sửa code. Job trùng thì thêm khóa. 404 thì kiểm cách ghép URL và thời hạn giữ kết quả trong tài liệu.

Log nên ghi gì ở client

Mỗi lần hỏi, client nên ghi một dòng gồm job ID, số thứ tự lần hỏi, mã HTTP, trạng thái trong body, giá trị Retry-After và thời gian đã trôi qua. Với 6 trường này, đọc lại log là biết ngay client có theo Retry-After hay không.

Không ghi toàn bộ body kết quả vào log. Kết quả có thể chứa dữ liệu khách hàng. Chỉ ghi số dòng, kích thước hoặc URL tệp như lab đang làm.

Log ở máy chủ và ở proxy

Máy chủ lab ghi phương thức, đường dẫn và Idempotency-Key của mỗi request. Nhờ đó ca 07 kiểm được hai POST mang cùng khóa. Ca 09 và ca 10 đếm được số GET thật sự tới status endpoint.

Khi đi qua proxy, nhật ký proxy là nguồn thứ ba. Nó cho biết request có tới proxy không, có qua xác thực không, và gửi tới đích nào. Ba nguồn log khớp nhau thì lỗi nằm ở logic chờ. Lệch nhau thì lỗi nằm ở tầng mạng.

202 khác 504 và 408

Đôi khi API không trả 202 mà để request đồng bộ chạy lâu rồi trả 504 Gateway Timeout từ cổng phía trước. Lỗi đó do một tầng trung gian hết giờ chờ máy chủ phía sau. Bài 504 Gateway Timeout đi sâu vào trường hợp này.

Còn 408 Request Timeout nghĩa là máy chủ chờ client gửi request quá lâu, như trong bài lỗi 408 Request Timeout. Cả hai không liên quan tới job bất đồng bộ. Nếu API có lựa chọn giữa gọi đồng bộ và gọi bất đồng bộ, việc lâu nên đi đường 202.

API trả 202 và cách polling khi đi qua proxy

Nhiều đội chạy job từ máy chủ có đường ra Internet qua proxy công ty, hoặc dùng proxy có IP cố định để API của đối tác whitelist. Phần này nói proxy ảnh hưởng gì tới luồng chờ và không ảnh hưởng gì.

Kết quả ca đi qua proxy

Ca 08 cho session dùng proxy HTTP cổng 9521 có tài khoản. Mật khẩu chứa @ và # nên phải mã hóa thành %40 và %23 trong URL proxy. Tên máy job.test được proxy phân giải về máy lab:

p = requests.Session()
p.trust_env = False
p.proxies = {"http": "http://user1:p%40ss%232026@127.0.0.1:9521"}
url, body, key = submit(p, "http://job.test:9520", {"seconds": 2})
result, polls = wait_job(p, url)

Client hỏi 3 lần và lấy kết quả như khi đi thẳng. Nhật ký proxy ghi đủ POST /reports, các GET /jobs/d76c826b và GET /results/d76c826b, tất cả qua xác thực với user1. Ca 11 cũng đi qua proxy và đọc được Location /jobs/8fdbe2c0, header này không bị proxy đổi.

Cách viết URL proxy có ký tự đặc biệt được giải thích trong bài định dạng proxy. Nếu requests báo ProxyError, bài requests.exceptions.ProxyError có các bước đọc lỗi.

Giữ cùng đường ra cho POST và GET

Một số API gắn job với khóa API chứ không gắn với IP, nên đổi IP giữa POST và GET không sao. Nhưng có API whitelist IP, hoặc gắn job với phiên đăng nhập. Khi đó POST đi một IP, GET đi IP khác có thể nhận 403 hoặc 404.

Với proxy xoay, chọn chế độ giữ IP trong một khoảng thời gian dài hơn deadline của job. Hoặc dùng proxy tĩnh cho toàn bộ luồng. Lab chưa thử với proxy xoay thật, đây là khuyến nghị theo cách API thường xác thực.

Proxy không làm job chạy nhanh hơn

Proxy chỉ thay đổi đường đi của request. Thời gian xử lý job do máy chủ API quyết định. Đổi IP để hỏi trạng thái dày hơn mà né được hạn mức là vi phạm điều khoản của hầu hết API, và cũng không làm job xong sớm.

Proxy giải quyết chuyện đường ra mạng và IP được phép, không thay thế token, không tăng quota và không rút ngắn thời gian xử lý của job.

Job chạy bao lâu là việc của máy chủ API. Việc của client là hỏi đúng chỗ, đúng nhịp và biết lúc nào dừng.

Proxy công ty và kết nối giữ lâu

Một số proxy công ty cắt kết nối giữ lâu hoặc giới hạn thời gian của mỗi request. Polling ít bị ảnh hưởng vì mỗi lần hỏi là một request ngắn, xong là trả kết nối. Long polling, nơi máy chủ giữ request tới 30 hay 60 giây rồi mới trả lời, thì dễ bị proxy cắt giữa chừng.

Nếu thấy lỗi kết nối lặp lại đúng sau một số giây cố định, hãy đo thời gian đó và so với cấu hình proxy. Lab không thử long polling, nên đây là điểm cần kiểm riêng trên proxy anh em đang dùng.

Khi máy chủ API nằm trong mạng nội bộ

Nếu API job nằm trong mạng nội bộ còn Internet đi qua proxy, cần đưa tên máy nội bộ vào NO_PROXY hoặc tắt proxy cho session đó. Bài no_proxy là gì có cách khai báo. Lab đặt trust_env bằng False để tránh trường hợp biến môi trường âm thầm đổi đường đi.

Chọn proxy cho job chạy định kỳ

Job xuất báo cáo hay crawl hàng loạt thường chạy theo lịch trên máy chủ. Khi API đối tác yêu cầu whitelist IP, một proxy tĩnh có IP cố định sẽ đơn giản hơn so với xin whitelist cả dải IP của nhà cung cấp cloud.

ProxyVN có proxy dân cư tĩnh, datacenter và proxy xoay, hỗ trợ HTTP và SOCKS5, cấp tài khoản user và mật khẩu hoặc xác thực theo IP. Cần một IP cố định để API đối tác whitelist, anh em có thể mua proxy tại trang chủ ProxyVN rồi chạy lại ca 08 với proxy thật trước khi đưa vào lịch.

Checklist và câu hỏi thường gặp về API trả 202 và cách polling

Checklist polling job

Checklist polling job

Phần cuối gom các bước kiểm trước khi đưa client vào chạy thật, và trả lời các câu hỏi hay gặp khi làm luồng chờ job.

Checklist trước khi chạy thật

Đi lần lượt các mục sau, mục nào chưa có thì bổ sung trước:

  • Đọc tài liệu API để biết status endpoint, danh sách trạng thái và cách lấy kết quả.
  • Kiểm mã 202 thật sự, không coi mọi mã 2xx là đã nhận job.
  • Ghép Location với URL gốc, không tự đoán đường dẫn.
  • Lưu job ID ngay khi nhận, trước lần hỏi đầu tiên.
  • Theo Retry-After khi có, backoff có trần và có nhiễu khi không có.
  • Có deadline tổng tính bằng đồng hồ monotonic.
  • Tách ba kết cục: thành công, thất bại, bị hủy.
  • Tắt tự theo chuyển hướng ở status endpoint nếu API dùng 303.
  • Dùng Idempotency-Key cho POST nếu API hỗ trợ.
  • Giữ cùng đường ra mạng cho cả luồng nếu API gắn job với IP.

Mười mục này đều có ca kiểm tương ứng trong lab, trừ mục đường ra mạng với proxy xoay chưa thử.

Khi nào nên dùng webhook thay polling?

Webhook hợp khi job rất lâu, từ vài phút tới vài giờ, và client có địa chỉ công khai nhận được request từ API. Polling hợp khi client chạy sau tường lửa hoặc qua proxy, không mở cổng nhận vào được. Nhiều API cho cả hai, dùng webhook làm tín hiệu chính và polling để đối soát.

Có nên hỏi ngay sau khi nhận 202?

Nếu có Retry-After thì chờ theo nó. Nếu không có, chờ ít nhất bằng khoảng first trong code, lab dùng 1 giây. Hỏi ngay lập tức hầu như chỉ nhận lại trạng thái queued, tốn một request.

Job đã xong nhưng kết quả hết hạn thì sao?

Nhiều API chỉ giữ kết quả trong một khoảng thời gian, ví dụ 24 giờ. Sau đó status URL hoặc result URL trả 404 hay 410. Lấy kết quả ngay khi job xong và lưu về hệ thống của mình. Bài lỗi 410 Gone khi crawl giải thích ý nghĩa mã 410.

Có cần async để poll nhiều job cùng lúc?

Với vài job thì một vòng lặp tuần tự là đủ. Với hàng trăm job, dùng asyncio với httpx hoặc aiohttp, mỗi job một coroutine chạy cùng logic wait_job. Giữ giới hạn số request đồng thời để không tự tạo ra 429.

Body của 202 có đáng tin?

Body của 202 cho biết trạng thái tại thời điểm nhận, thường là queued. Đừng đọc dữ liệu nghiệp vụ trong đó. Một số API trả luôn progress hoặc thời gian ước tính, chỉ dùng để hiển thị.

Giới hạn của bài

Kết quả đến từ API lab tự dựng theo mẫu Asynchronous Request-Reply. API thật có thể đặt tên trạng thái khác, trả kết quả trong body thay vì 303, hoặc không gửi Retry-After. Lab chưa thử webhook, SSE, long polling, proxy SOCKS5 và proxy xoay thật.

API trả 202 và cách polling: những điều cần nhớ

API trả 202 và cách polling đúng gồm lưu job ID, ghép Location, theo Retry-After, có deadline tổng, tách thành công với thất bại và bị hủy, tắt tự theo 303 khi cần. Proxy chỉ lo đường ra mạng, giữ cùng IP cho cả luồng khi API whitelist. Cần proxy ổn định cho API trả 202 và cách polling, liên hệ Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam.

TIN TỨC LIÊN QUAN