Browserless là Chrome ở chế độ headless được đóng gói thành một dịch vụ mà bạn tự triển khai. Container Docker của nó luôn chạy, nhận tác vụ qua HTTP hoặc WebSocket, và áp dụng giới hạn tiếp nhận dùng chung thay vì được nhúng vào từng bên gọi. Trong các bài test REST ở đây, dịch vụ giữ 0 tiến trình Chrome khi nhàn rỗi, tạo tiến trình Chrome khi một request đang chạy, rồi quay lại mức 0 sau đó. Thứ được “pool” là năng lực phục vụ và hàng đợi, chứ không phải một tập tiến trình browser đã được làm nóng sẵn đã được xác minh.
Tôi đã chạy v2.55.0 trên một bộ fixture cục bộ có kiểm soát — khởi động được tách thành nhiều giai đoạn, kiểm soát tiếp nhận ở ba cấu hình, kiểm tra độ khớp endpoint so với dữ liệu chuẩn đã biết, chạy bền 30 phiên, và kiểm tra ngưỡng timeout. Điều hữu ích ở đây là hành vi vận hành chứ không phải một cú tăng tốc: giới hạn tiếp nhận khớp với phản hồi mà client nhìn thấy, và các “góc cạnh” chủ yếu nằm ở khâu triển khai.
Kết quả đáng giá nhất không phải là một con số latency. Browserless không làm Chrome khởi động nhanh hơn — nó đặt một chính sách “cửa vào” cho Chrome: chỉ cho một số phiên nhất định đi vào, phần còn lại xếp hàng, và trả về HTTP 429 cho tất cả phần dư. Mức trần đó dịch từ 4 lên 8 rồi 10 khi tôi đổi hai biến môi trường, và chính container cũng ghi nhận đúng mã trạng thái mà client thấy trong từng request.
Browserless thực chất là gì
Nhiều người hay nhầm ngay ở khái niệm. Browserless không phải là một thư viện để import rồi gọi. Nó là một Docker image — ghcr.io/browserless/chromium — mà bạn chạy như một service sống lâu dài. Nó làm trung gian cho công việc browser và mở ra theo hai cách: các REST endpoint (/content, /scrape, /screenshot, /pdf, cùng với /function và /unblock) và một bề mặt CDP/WebSocket để Puppeteer và Playwright connect() vào.
Tôi đã test mặt REST. Đường WebSocket là có thật và được dùng rất nhiều, nhưng tôi không đo nó.
Phiên bản được kiểm tra là v2.55.0, xác minh vào ngày 27/07/2026.
| Hạng mục | Giá trị |
|---|---|
| Phiên bản image | v2.55.0, phát hành ngày 14/07/2026 |
| Chrome | 149.0.7827.0 |
| Node | 24.18.0 |
| Base image | Ubuntu 24.04 |
| GitHub stars | khoảng 13.525, tính đến 27/07/2026 |
Số sao thay đổi theo thời gian; hãy coi đây là số đọc tại một thời điểm cụ thể.
Cổng license: SSPL-1.0 hoặc commercial
Repository cung cấp Browserless theo SSPL-1.0 hoặc giấy phép thương mại của Browserless. Hãy đọc chính xác LICENSE của repository và hướng dẫn triển khai open-source chính thức của Browserless trước khi chọn hướng đi. Bài viết này không thực hiện phân tích pháp lý về sản phẩm thương mại, ứng dụng mã nguồn đóng, hệ thống CI, dịch vụ hosting, hay triển khai nội bộ, nên không gán các kịch bản đó vào một loại license nào. Hãy để bộ phận pháp chế hoặc người phụ trách license phần mềm đánh giá mô hình triển khai và phân phối của bạn.
Browserless cũng bán các gói hosted. Giá và định nghĩa đơn vị sử dụng thay đổi khá nhanh và không nằm trong bài test tự host này, vì vậy hãy kiểm tra trên trang chính thức thay vì xem bảng ở đây như bằng chứng mua sắm.
Mô hình session hoạt động như thế nào bên trong
Đường REST được đo cho thấy hành vi giống như thực thi browser theo từng request, nhưng bộ đo này chưa đào sâu đủ vào nội bộ Browserless để phân biệt một tiến trình browser mới với mọi chiến lược tái sử dụng context có thể có. Điều nó xác nhận đơn giản hơn là: 0 tiến trình Chrome khi nhàn rỗi, 11 tiến trình họ Chrome trong lúc request đang chạy, và quay lại 0 sau khi chạy tuần tự. Không có dấu hiệu nào cho thấy có một pool browser được làm nóng sẵn trong cấu hình này.
Dịch vụ Node chạy lâu dài sẽ nhận một số công việc browser có giới hạn, xếp thêm một số khác vào hàng đợi có giới hạn, rồi từ chối phần còn lại. Hãy xem mô hình tiếp nhận đó như cam kết kiến trúc được đo bên dưới. Đừng suy ra việc tái sử dụng process hay context chỉ vì từ “pool”; bộ đo thời gian không chứng minh được điều đó.
Kiểm soát tiếp nhận có hai núm:
CONCURRENT— số session được chạy cùng lúc.QUEUED— số request bổ sung được phép chờ slot.
Endpoint /config của container báo mặc định là CONCURRENT=10, QUEUED=10, TIMEOUT=30000. Bất kỳ thứ gì vượt quá CONCURRENT + QUEUED sẽ bị từ chối ngay lập tức.
Xác thực không phải tùy chọn. Browserless v2 luôn yêu cầu token — nếu bạn không đặt TOKEN, nó sẽ tự tạo một token ngẫu nhiên và in ra stdout khi khởi động. Mọi REST call đều phải kèm ?token=.
Về quan sát hệ thống, bạn có /pressure (running, queued, CPU, memory, recently rejected), /sessions, và /config. Còn có một JSON export /metrics, nhưng nó cần đặt METRICS_JSON_PATH và tôi không dùng. Image này chạy dumb-init làm PID 1, đúng như câu trả lời đã được tài liệu hóa cho nỗi phiền toái zombie-process đi cùng Chrome trong container suốt nhiều năm.
Thực tế triển khai: chỉ một lệnh, và bốn thứ không ai nhét vào lệnh đó
Dòng cài đặt mà ai cũng trích dẫn thực sự chỉ là một docker run. Còn phần xung quanh mới là thứ bạn cần lên kế hoạch.
Image nặng 4,34 GB. Đây mới là con số nên định hình kỳ vọng của bạn, không phải startup latency. Manifest chứa cả linux/arm64 lẫn linux/amd64; trên máy host arm64 của tôi, Docker kéo bản arm64 native. (User-agent của Chrome bên trong container vẫn hiện X11; Linux x86_64 — đó là UA “hình thức” của Chrome trên Linux, không phải do giả lập. uname -m báo aarch64. Người ta vẫn file bug vì chuyện này.)
Tôi dùng --shm-size=2g cho mọi phép đo. Bộ test không có một lần chạy đối chứng ở /dev/shm mặc định của Docker, nên bài viết này không thể khẳng định 2 GiB là bắt buộc cho mọi trường hợp hay định lượng chính xác điểm thất bại. Hãy chỉnh theo số lượng browser và workload của bạn.
Token là vấn đề triển khai, không phải thủ tục cho có. Không có nó, bất kỳ ai route được tới cổng 3000 đều có thể điều khiển một browser trong mạng của bạn.
Kết nối mạng của container là việc của bạn. Fixture của tôi chạy trên host, nên container truy cập nó qua host.docker.internal (colima map bằng --add-host host.docker.internal:host-gateway). Tôi đã kiểm tra container thực sự chạm được fixture bằng một curl thô trước khi tin bất kỳ phép đo nào.
Môi trường của tôi: colima 0.10.3 (6 CPU / 11.6 GiB) với Docker 29.2.1 trên macOS 26.5.2 arm64. Bộ đo chỉ dùng Python 3 stdlib. Với PNG và PDF, nó chỉ kiểm tra chữ ký file chứ không kiểm tra bộ giải mã, kích thước, số trang, độ đầy đủ hay độ trung thực thị giác.
Một lệnh khởi chạy tương đương tối thiểu sẽ dùng image đã ghim, token rõ ràng, và dung lượng shared memory như ở đây:
docker run --rm -p 3000:3000 --shm-size=2g \
-e TOKEN=replace-with-a-secret \
ghcr.io/browserless/chromium:v2.55.0
Sau khi /pressure?token=... phản hồi, một POST /content?token=... đã xác thực với body JSON chứa URL mục tiêu sẽ kiểm tra đường REST. Các caller trong production cũng cần retry có giới hạn và thêm jitter cho phản hồi 429; retry ngay lập tức chỉ làm mình tranh chấp lại đúng hàng đợi đã đầy.
“Thuế” khởi động, tách nhỏ ra

Ba lần boot docker run mới, lấy median với min–max:
| Giai đoạn | Median | Khoảng | Thực chất là gì |
|---|---|---|---|
docker run → /pressure trả 200 | 0,78 giây | 0,70–0,87 giây | endpoint HTTP đã phản hồi; kiểm tra này chưa xác nhận browser đã khởi động |
sẵn sàng → render /content đầu tiên | 0,32 giây | 0,28–0,41 giây | request đầu tiên quan sát được: browser làm việc + điều hướng + trả HTML |
các lần gọi /content về sau | 0,15 giây | 0,147–0,154 giây | latency quan sát được ở các request sau trong cùng container |
Dòng ở giữa rất dễ bị hiểu quá mức. Nó không đo riêng việc launch browser và cũng không cho thấy Browserless khởi động Chrome nhanh hơn một thư viện chạy trong process. Nó là một vòng HTTP vào container cộng với công việc browser, điều hướng và chuyển phản hồi. Khoảng chênh khoảng 0,17 giây giữa lần đầu và các lần sau có thể gồm ảnh hưởng từ filesystem, OS, Chrome, Node hoặc cache của container. Vì lúc nhàn rỗi số tiến trình Chrome bằng 0 và bộ đo không capture CDP trace hay timeline tiến trình cho các call này, nên không thể quy khoảng chênh đó cho việc tái sử dụng browser hay chi phí khởi động được “trả dần”.
Thêm một lưu ý: đây là số đo trên colima VM ở macOS. Linux chạy trực tiếp trên máy thật sẽ khác. Đừng đem 0,78 giây nói với SRE của bạn như thể nó portable.
Thực chiến: tìm ngưỡng từ cả hai phía
Cam kết CONCURRENT + QUEUED → 429 được nhắc ở khắp nơi nhưng hiếm khi được chứng minh.
Thiết lập: một route fixture sẽ sleep 5 giây ở phía server, để mỗi request chắc chắn chiếm một session trong thời gian đã biết. Sau đó bắn đồng thời CONCURRENT + QUEUED + 4 request và xem điều gì quay lại — trong khi một thread sampler riêng liên tục polling /pressure để đọc accounting nội bộ của container.
| Cấu hình (CONCURRENT, QUEUED) | Số request bắn | HTTP 200 | HTTP 429 | Đỉnh /pressure phía server (running / queued / recentlyRejected) |
|---|---|---|---|---|
| (2, 2) | 8 | 4 | 4 | 2 / 2 / 4 |
| (3, 5) | 12 | 8 | 4 | 3 / 5 / 4 |
| (5, 5) | 14 | 10 | 4 | 5 / 5 / 4 |
Từ đó rút ra ba điều.
Mức trần luôn đúng bằng CONCURRENT + QUEUED. Số phản hồi thành công lần lượt là 4, 8, và 10 — đúng bằng tổng được cấu hình trong từng trường hợp. Số bị từ chối đúng bằng phần vượt, và phần vượt này đều là 4 trong cả ba lần chạy.
Mức trần có thể thay đổi. Nó không phải con số cố định được đóng sẵn trong image; nó là thứ bạn cấu hình. Việc đi từ 4 → 8 → 10 chỉ bằng cách đổi biến môi trường mới là thứ làm nó hữu ích chứ không phải trivia.
Và hai tín hiệu này độc lập. Mã trạng thái ở client đến từ phản hồi HTTP thật; /pressure đến từ accounting nội bộ của container, được poll bởi một thread khác. Trong ba lần chạy ngắn này, chúng khớp nhau. Điều đó khiến /pressure trở thành một tín hiệu ứng viên cho production, nhưng chưa phải một hợp đồng autoscaling hoàn chỉnh: tần suất scrape, semantics khi reset, cách gộp nhiều replica, và hành vi dưới workload hỗn hợp dài hơn vẫn cần xác thực.
Một nuance mà các con số pass/fail che mất. Request bị xếp hàng không thất bại — nó chờ, và có thể chờ khá lâu. Ở (2, 2) với công việc 5 giây, các phản hồi thành công đến trong khoảng 5,7 giây đến 11,0 giây, median 8,3 giây. Vì vậy latency end-to-end có thể lên tới khoảng hai lần thời lượng một session. Bộ đo không capture timestamp riêng cho admission và execution, nên không thể gán toàn bộ độ trễ cho thời gian chờ hàng đợi.
Điều này trông thế nào với một job thật
Giả sử bạn render 4.000 trang sản phẩm sang PDF mỗi đêm, và mỗi trang mất khoảng 5 giây. Bạn đặt CONCURRENT=5, QUEUED=5. Trần throughput của bạn là 5 trang mỗi 5 giây — tức một trang mỗi giây — nên job mất khoảng 67 phút nếu đường ống luôn đầy đúng mức. Đây là phép tính từ hành vi đã đo, không phải benchmark, nhưng đó mới là phép tính bạn nên làm trước khi triển khai.
Bất kỳ request nào đến sau khi tất cả slot đang chạy và slot chờ đều đã kín có thể nhận 429 ngay; bắn đồng thời không đảm bảo request có thứ tự nào sẽ thua cuộc đua. Một job runner nên coi phản hồi đó là backpressure và dùng retry có giới hạn kèm jitter. Nếu không, nó có thể làm rơi trang trong khi hệ thống job cấp cao hơn vẫn nghĩ mọi thứ đang ổn — đây là rủi ro vận hành, không phải kịch bản lỗi mà bộ đo này đã chứng minh.
Thực chiến: endpoint thực sự nhìn thấy gì
Để kiểm tra độ trung thực render một cách nghiêm túc, trang fixture giấu text đánh dấu của nó khỏi bất kỳ ai không chạy browser thật. Chuỗi nhìn thấy được Runtime Injected Marker 88 được ghép từ các mảnh JavaScript lúc load, nên không tồn tại một literal liền mạch nào cho nó trong byte mà server gửi đi. Một lần fetch tĩnh đơn giản của trang đó trả về 702 byte và không chứa marker nào.
| Endpoint | Kết quả | Số byte |
|---|---|---|
/content | marker được chèn lúc runtime có mặt, cộng thêm cả marker tĩnh | 811 |
/scrape trên #scrape-me (một node do JS chèn vào) | trả về SCRAPE_TARGET_VALUE_CC | 422 |
/screenshot | phản hồi có chữ ký PNG 89 50 4E 47 | 18.621 |
/pdf | phản hồi có chữ ký PDF %PDF- | 40.974 |
| cả bốn endpoint, không có token | HTTP 401 (không phải 403) | — |
Việc /content trả 811 byte kèm marker được chèn cho thấy một Chromium thật đã render trang trước khi HTML được trả về. /scrape lấy được giá trị từ một node vốn không tồn tại cho tới khi JavaScript chạy. Cả hai đều hoạt động mà không cần code automation phía client — chỉ một request POST đã xác thực.
Đó mới là lời hứa thực sự. Cũng trong đợt test này, một static crawler bỏ sót hoàn toàn lớp nội dung kiểu đó, và các thư viện browser chạy trong process (chromedp, rod, Selenium) chỉ bắt được sau khi tôi viết explicit wait. Browserless bắt được bằng một request có hình dáng giống curl. Bạn đang đổi code automation lấy gánh nặng triển khai.
Có hai giới hạn cho nhận định đó. Bằng chứng chỉ bao phủ các lớp nội dung trong fixture của tôi, chứ không phải một khảo sát toàn bộ web hiện đại. Và /unblock, endpoint chống phát hiện, đã được cố ý để yên — không kết quả nào ở đây nên bị hiểu thành tuyên bố về năng lực chống bot. /function, /download, và /performance cũng chưa được test.
Thực chiến: kiểm tra residue ngắn
Chrome trong container có tiếng để lại “xác chết”, nên tôi chạy 30 session tuần tự với CONCURRENT=3 và đếm tiến trình bên trong container.
Trước khi tin bất cứ thứ gì, tôi đã hiệu chỉnh bộ phát hiện. Trong lúc một session đang chạy, bộ enumerator /proc đọc được 11 tiến trình họ Chrome (browser, zygote, GPU, renderer, utility). Điều đó quan trọng: nó chứng minh công cụ đo thực sự nhìn thấy Chrome, nên con số 0 sau khi chạy không phải do mù dữ liệu mà là một phép đo thật. Một bài test leak báo “0 processes” mà không chứng minh được nó đếm được gì thì vô giá trị.
Sau 30 session: 0 chrome processes, 0 zombies. Những gì còn sống chỉ là dumb-init, node, Xvfb, start.sh, và sh. /sessions đọc 0 khi nhàn rỗi.
Bộ nhớ container, từ docker stats (con số mà operator nhìn thấy, không phải RSS của một process đơn lẻ):
| Sau N session | 0 | 5 | 10 | 15 | 20 | 25 | 30 |
|---|---|---|---|---|---|---|---|
| Bộ nhớ container (MiB) | 294 | 300 | 301 | 302 | 302 | 303 | 303 |
Mức tăng ròng sau 30 session: khoảng 9,5 MB, và đường cong mẫu đã chững lại sau session 10. Điều đó không khớp với một leak tuyến tính đơn giản theo từng session trong cửa sổ ngắn này. Warmup của Node là một lời giải thích khả dĩ, chứ không phải điều mà đếm tiến trình và chuỗi bộ nhớ này có thể chứng minh.
Phạm vi: 30 session tuần tự là một bài soak nhỏ, không phải endurance hay concurrency run. Những cảnh báo EventEmitter listener tồn tại lâu nay trong issue tracker là loại vấn đề có thể lộ ra sau hàng giờ và hàng nghìn session, còn tôi thì không chạy tới mức đó. Kết luận có thể hỗ trợ chỉ là: trong cửa sổ này trên v2.55.0, không quan sát thấy tiến trình Chrome hay zombie tích lũy.
Ngưỡng timeout
TIMEOUT được tài liệu mô tả như một núm điều chỉnh. Tôi muốn xem nó kích hoạt.
| Trường hợp | Giữ trang | Trạng thái | Thời gian |
|---|---|---|---|
| Trong ngân sách | 2.000 ms | 200 | 2.406 giây |
| Quá ngân sách | 15.000 ms | 408 | 5.007 giây |
Với TIMEOUT=5000, một session cố giữ trang trong 15 giây đã trả về HTTP 408 tại mốc 5,007 giây thay vì treo vô thời hạn. Một quan sát đơn lẻ như vậy xác nhận việc thực thi gần ngưỡng đã cấu hình. Nó không cho biết cách triển khai timer hay chứng minh việc slot được giải phóng; một bài test mạnh hơn sẽ lặp lại thử nghiệm, quan sát /sessions và /pressure trở về idle, rồi xác nhận request sau đó lấy được slot vừa được giải phóng.
Bẫy khi migrate: PREBOOT là im lặng và không nói cho bạn biết
Trong tất cả những gì tôi đo được, đây là kết quả mà tôi muốn được ai đó nói trước khi nâng cấp nhất.
Browserless 2.0.0 đã gỡ PREBOOT và KEEP_ALIVE — changelog nói rằng chúng bị bỏ vì gây nhầm lẫn, ít tác dụng, và gây lỗi. Quyết định đó khá hợp lý. Vấn đề là chuyện gì xảy ra khi một cấu hình v1 bị copy-paste sang v2, vốn là cách nâng cấp phổ biến nhất.
Tôi chạy container với -e PREBOOT=true và đo so với mặc định:
| Tín hiệu | PREBOOT=true | Mặc định, không bật cờ |
|---|---|---|
| Thời gian sẵn sàng | 0,716 giây | 0,776 giây |
| Cold render | 0,314 giây | 0,318 giây |
| Warm render | 0,163 giây | 0,150 giây |
| Chrome processes khi nhàn rỗi | 0 | 0 |
Mọi timing đều nằm trong dải min–max của chính nhánh mặc định — đó là nhiễu, không phải hiệu ứng. Và không có gì được làm nóng sẵn cả: container PREBOOT=true ở trạng thái idle nhưng không giữ browser nào, y hệt như container không có cờ đó. Thêm hai tín hiệu nữa, nhưng không phải là con số:
/confighoàn toàn không có keypreboot. Các key làconcurrent,queued,timeout,token,maxCPU,maxMemory,retries, và các key khác.- Không lỗi. Không cảnh báo. Không có gì trong log của container.
Vì vậy, một cấu hình v1 PREBOOT trên v2 sẽ trở thành no-op im lặng trong kiểm tra khởi động thông thường và kiểm tra log. Key /config bị thiếu cộng với hành vi không đổi là tín hiệu có thể phát hiện; Browserless không phát ra một rejection hay warning rõ ràng nào. Một bài kiểm tra migration cần xem cấu hình đã được áp dụng chứ không thể coi một lần startup màu xanh là bằng chứng rằng mọi biến môi trường đều có hiệu lực.
KEEP_ALIVE là trường hợp ngược lại, và hai thứ này không nên bị gom chung. Nó bị gỡ trong cùng bản phát hành, nhưng không im lặng — một probe nhỏ vào container cho thấy nó log ra Environment variable of "KEEP_ALIVE" is deprecated and ignored. ngay trong stdout. Đó là warning đúng kiểu cho người vận hành. Tôi không đưa KEEP_ALIVE qua cùng bộ đo như PREBOOT, nên tôi chỉ báo cáo nó như một kiểm tra chứ không phải một phép đo. Nhưng chiều hướng thì đã đủ rõ để đáng chú ý: chỉ PREBOOT mới là cái bẫy im lặng. Browserless trung thực về KEEP_ALIVE hơn nhiều so với cách một câu tóm tắt kiểu “v2 bỏ qua flag v1 của bạn” có thể khiến người ta nghĩ.
Ưu và nhược điểm
Ưu điểm
- Kiểm soát tiếp nhận hoạt động đúng như mô tả và thay đổi theo cấu hình — đã chứng minh ở ba mức trần khác nhau, đồng thời qua mã trạng thái phía client và accounting nội bộ của server.
/pressurekhớp với số running, queued, và rejected mà client nhìn thấy trong ba lần chạy ngắn; có thể xem đây là một đầu vào ứng viên cho autoscaling và alerting.- Render Chromium thật mà không cần code automation phía client: chỉ một POST đã xác thực đã bộc lộ DOM chèn bởi JS mà fetch tĩnh của cùng trang không thể thấy.
- Không quan sát thấy Chrome processes tích lũy trong một run tuần tự 30 session; sau run, số đếm là 0 Chrome processes và 0 zombies.
- Một lần thử
TIMEOUTtrả 408 ở mốc 5,007 giây so với ngân sách 5,000 giây; việc cleanup và giải phóng slot chưa được xác minh riêng. - Mặc định đã có auth: cả bốn REST endpoint đều trả 401 nếu không có token.
- Chỉ một
docker runlà có service sẵn sàng trong khoảng 0,78 giây, và render đầu tiên sau đó 0,32 giây.
Nhược điểm
- Image 4,34 GB. Đây là chi phí thật và nó hiện diện trong registry, cache CI, và thời gian cold deploy của bạn.
- SSPL-1.0 hoặc license thương mại của Browserless. Hãy đánh giá điều khoản hiện hành so với mô hình triển khai và phân phối của bạn.
PREBOOTtừ v1 vẫn được nhận nhưng bị bỏ qua im lặng trên v2 — không lỗi, không cảnh báo, không có key/config.- Bạn đang vận hành một service chứ không chỉ thêm một dependency: container, token, đường mạng, trần tiếp nhận, và trách nhiệm nâng cấp.
- Request bị xếp hàng đẩy end-to-end latency lên khoảng hai lần thời lượng session trong lần chạy
(2, 2); thời gian chờ hàng đợi riêng không được đo. - Timing REST được đo bao gồm một vòng HTTP và không tách riêng chi phí launch browser hay chứng minh việc tái sử dụng browser.
Ai nên dùng, ai không nên dùng
Browserless đáng tiền khi hơn một thứ cần đến browser. Một dịch vụ render dùng chung cho nhiều app, một team muốn screenshots và PDFs qua HTTP endpoint thay vì phải kéo dependency Chrome vào từng service, một pipeline công việc thực sự cần một mức trần năng lực có backpressure đo được — đó là hình dạng mà nó phù hợp. Nếu bạn đã chạy Docker và có người chịu trách nhiệm triển khai, câu chuyện vận hành khá rõ ràng: tiếp nhận dự đoán được, backpressure quan sát được, và không thấy Chrome processes hay zombies tích lũy trong bài kiểm tra tuần tự 30 session.
Nó cũng là lựa chọn đúng nếu phương án thay thế là mọi service trong stack đều tự cài Chromium. Gom tất cả vào một container với token và một mức trần là một đánh đổi kiến trúc thực sự hợp lý.
Đừng dùng nó nếu bạn chỉ viết một script. Kéo về 4,3 GB và chạy container chỉ để một file Python đơn lẻ lấy một trang đã render là quá nhiều nghi thức cho một việc nhỏ — một browser library chạy trong process làm được điều đó mà không cần service riêng. Đừng dùng nếu điều khoản SSPL không phù hợp với sản phẩm thương mại của bạn và bạn không thể giải quyết được. Đừng dùng nếu thứ bạn thật sự muốn là một browser được làm nóng sẵn, không có chi phí lạnh, vì PREBOOT trên v2 không cho bạn điều đó. Và đừng dùng nếu vấn đề thực sự của bạn là xử lý anti-bot, vì nó nằm ở một endpoint mà tôi cố ý không test và sẽ không bảo chứng.
Các lựa chọn thay thế, gồm cả vị trí của Thunderbit
So sánh đáng làm không phải là Browserless với một container khác. Nó là browser nằm ở đâu và ai chịu trách nhiệm giữ cho nó sống.
Bài review liên quan: Browsertrix Crawler review.
Bài review liên quan: chromedp review.
| Browser library (chromedp, rod, Selenium, Playwright) | Browserless tự host | Thunderbit extraction được quản lý | |
|---|---|---|---|
| Browser chạy ở đâu | Trong process của bạn | Trong container của bạn | Hạ tầng của bên khác |
| Chi phí thiết lập | Cài package | Image 4,3 GB + container + token | API key |
| Timing được đo ở đây | Không đo trong bài này | Render đầu tiên 0,32 giây sau khi HTTP sẵn sàng; các lần sau median 0,15 giây | Không đo trong bài này |
| Bạn phải viết gì | Code automation với explicit waits | Một POST đã xác thực | Một HTTP call |
| Bạn nhận về gì | Bất cứ thứ gì bạn script | HTML, PNG/PDF khớp chữ ký, node được scrape | JSON hoặc Markdown có cấu trúc theo sản phẩm |
| Giới hạn năng lực | Máy của bạn | CONCURRENT + QUEUED, rồi 429 | Gói của nhà cung cấp |
| Ai trực ca | Bạn | Bạn | Họ |
Nếu bạn muốn browser chạy ngay trong process của mình và không ngại viết wait, một thư viện sẽ nhẹ hơn và không cần deploy service riêng. Tôi đã viết về hướng đó trong bài so sánh Playwright và Puppeteer và bài tổng hợp các dự án scraping open-source.
Nếu bạn không muốn tự vận hành browser nào cả, Thunderbit của chúng tôi là một lựa chọn được quản lý. Browserless trả về material đã render để code của bạn diễn giải; Thunderbit có thể trả Markdown hoặc dữ liệu khớp schema trong khi nhà cung cấp chịu trách nhiệm hạ tầng render. Bài viết này không benchmark latency, năng lực, hành vi lỗi, chất lượng trích xuất hay chi phí của Thunderbit, nên bảng trên chỉ mô tả ranh giới trách nhiệm chứ không phải so sánh hiệu năng.
Bài đọc liên quan từ cùng đợt test: Crawl4AI review nói về pipeline Markdown có browser do chính bạn vận hành, còn tổng quan web scraping tools thì đặt toàn cảnh rộng hơn của ngành.
Dùng thử Thunderbit để trích xuất dữ liệu web
Kết luận
Có nên chạy Browserless không? Có, nếu nhiều bên gọi cần browser work, có người có thể vận hành container, và review license của bạn chấp nhận mô hình triển khai đó. Trong ba lần chạy synthetic về admission, số request được chấp nhận đúng bằng CONCURRENT + QUEUED, phần vượt bị trả 429, và /pressure khớp với số mà client nhìn thấy. Trong một bài kiểm tra tuần tự 30 session riêng biệt, không có Chrome processes hay zombies tích lũy. Một lần thử timeout trả 408 gần ngưỡng cấu hình. Đó là những quan sát hữu ích có biên giới rõ ràng, không phải bảo đảm tuyệt đối.
Nhưng hãy tính chi phí một cách trung thực. Đây là image 4,34 GB và là một service bạn phải vận hành, không phải một dependency chỉ cần thêm vào; bài test này cũng không chứng minh được tốc độ so với browser library chạy trong process. Thứ nó mang lại là một browser mà bạn có thể phân phối theo suất: một mức trần rõ ràng và backpressure có thể đo. Trong bài kiểm tra tuần tự 30 session, không quan sát thấy Chrome processes hay zombies tích lũy. Cái giá phải trả là gánh nặng triển khai và một bộ điều khoản license bạn cần đọc. Nếu bạn chỉ render vài trang từ một script, sự đánh đổi đó không đáng. Nếu bạn đang chạy một tầng render mà nhiều service phụ thuộc, thì đáng — chỉ cần nhớ kiểm tra các biến môi trường v1 của bạn khi đi vào, vì PREBOOT sẽ ngồi đó trông như đang hoạt động trong khi thực tế chẳng làm gì cả.
Dùng thử Thunderbit để trích xuất dữ liệu web Get Started Free
Câu hỏi thường gặp
Browserless có làm headless Chrome nhanh hơn không?
Bài test này không thể trả lời điều đó. Endpoint HTTP đã sẵn sàng sau 0,78 giây kể từ docker run; sau đó lần gọi /content đầu tiên mất 0,32 giây và các lần sau trong cùng container mất khoảng 0,15 giây. Những con số đó gộp một vòng HTTP, công việc browser, điều hướng và chuyển phản hồi. Bộ đo không tách riêng thời gian khởi động, không trace việc tái sử dụng process, và cũng không công bố benchmark in-process tương đương. Hãy dùng Browserless như một boundary service dùng chung và một lớp admission control, rồi tự benchmark đường latency của bạn.
Điều gì xảy ra khi vượt giới hạn concurrency của Browserless?
Bạn sẽ nhận HTTP 429 ngay lập tức. Mức trần chính xác là CONCURRENT + QUEUED, và tôi đã xác nhận ở ba cấu hình: (2,2) chấp nhận 4 và từ chối 4, (3,5) chấp nhận 8 và từ chối 4, (5,5) chấp nhận 10 và từ chối 4. Endpoint /pressure của server báo đúng số running, queued, và recentlyRejected trong mọi lần. Điều đáng nhớ: request bị xếp hàng không thất bại, chúng chờ — ở (2,2) với công việc 5 giây, các phản hồi thành công mất từ 5,7 đến 11,0 giây. Hãy xây client sao cho coi 429 là backpressure kèm retry và backoff.
PREBOOT còn hoạt động trong Browserless v2 không?
Không. PREBOOT đã bị gỡ từ 2.0.0, và v2 chấp nhận -e PREBOOT=true mà không báo lỗi hay cảnh báo nào, trong khi thực tế không làm gì với nó. Tôi đã xác nhận tính vô hiệu theo ba cách: latency không khác gì mặc định, container PREBOOT=true nhàn rỗi không có 0 chrome processes chờ sẵn, và /config hoàn toàn không có key preboot. Nếu bạn migrate cấu hình v1, các instance của bạn không được làm nóng sẵn. Lưu ý rằng KEEP_ALIVE, cũng bị gỡ trong cùng bản phát hành, lại có log cảnh báo "deprecated and ignored" — nên vấn đề failure im lặng là riêng của PREBOOT.
Browserless có miễn phí cho mục đích thương mại không? Repository cung cấp SSPL-1.0 hoặc giấy phép thương mại của Browserless, nhưng bài viết này không ánh xạ các kịch bản thương mại hay mã nguồn đóng cụ thể vào một trong hai lựa chọn đó. Hãy xem lại LICENSE hiện hành và hướng dẫn triển khai chính thức, rồi để người phụ trách license phần mềm đánh giá mô hình triển khai và phân phối của bạn.
Browserless có để lại các tiến trình Chrome zombie không?
Không thấy tích lũy trong cửa sổ ngắn đã test. Sau 30 session tuần tự, container có 0 Chrome processes và 0 zombies, chỉ còn dumb-init, node, Xvfb, start.sh, và sh. Bộ phát hiện đếm được 11 tiến trình họ Chrome khi một session đang sống, nên nó không bị mù. Bộ nhớ container tăng từ 294 MiB lên 303 MiB rồi chững lại trong các mẫu đo. Đây không phải là kết quả endurance nhiều giờ, chạy đồng thời, hay hàng nghìn session.


