Bạn cần gì để sử dụng proxy trong Node.js?
Fetch tích hợp sẵn trong Node.js hoạt động với thư viện undici và mặc định không đọc cài đặt proxy. Vì vậy, HTTP proxy được sử dụng bằng cách truyền ProxyAgent của undici vào dispatcher, và SOCKS5 proxy được sử dụng bằng cách truyền socks-proxy-agent vào agent của module https Node.
Ví dụ sử dụng IP 198.51.100.23, cổng HTTP 8000, cổng SOCKS5 1080, username spx_user, password spx_pass. Tất cả code đều ở định dạng module ES (.mjs).
npm install undici socks-proxy-agentLàm thế nào để kết nối proxy HTTP với fetch?
Nhập địa chỉ proxy vào ProxyAgent và tên người dùng cùng mật khẩu vào tùy chọn token dưới dạng giá trị xác thực Basic. Truyền nó vào fetch từ undici như một dispatcher sẽ khiến yêu cầu đó đi qua proxy. Địa chỉ HTTPS được truyền qua theo phương thức CONNECT đã được mã hóa.
Dùng lại cùng ProxyAgent cho nhiều yêu cầu giúp tái sử dụng kết nối, nhanh hơn. Tạo một agent cho mỗi proxy và luân phiên sử dụng.
import { fetch, ProxyAgent } from "undici";
const agent = new ProxyAgent({
uri: "http://198.51.100.23:8000",
token: "Basic " + Buffer.from("spx_user:spx_pass").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher: agent });
console.log(res.status, await res.json()); // "ip" should be the proxy IPLàm thế nào để kết nối proxy SOCKS5?
socks-proxy-agent là một agent được sử dụng cùng với các module http và https của Node. Sử dụng địa chỉ socks5h để proxy xử lý việc phân giải tên miền. Đoạn mã dưới đây chuyển agent vào https.get và thu thập và in ra nội dung phản hồi.
Với thư viện hoạt động trên module http như axios, chỉ cần thêm cùng agent vào httpAgent và httpsAgent là yêu cầu sẽ đi qua SOCKS5.
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
https.get("https://api.ipify.org?format=json", { agent }, (res) => {
let body = "";
res.on("data", (chunk) => (body += chunk));
res.on("end", () => console.log(res.statusCode, body));
}).on("error", (err) => console.error("request failed:", err.message));Làm thế nào để nhận danh sách qua API và luân phiên sử dụng?
Khi bắt đầu, nhận danh sách qua API và tạo agent cho mỗi proxy, sau đó luân phiên sử dụng agent tiếp theo cho mỗi yêu cầu. Đoạn code dưới đây nhận danh sách HTTP ở định dạng ip:port:user:pass để tạo mảng ProxyAgent, nếu thất bại sẽ thử lại với proxy tiếp theo.
Hãy thêm giới hạn thời gian bằng AbortSignal.timeout vào mỗi yêu cầu. Nếu các yêu cầu không có phản hồi tích tụ lại, toàn bộ quá trình sẽ chậm đi.
import { fetch, ProxyAgent } from "undici";
const API = "https://socks5proxy.io/api/v1";
const KEY = "YOUR_API_KEY";
const text = await (await fetch(API + "/proxies/download?format=ip:port:user:pass&protocol=http", {
headers: { Authorization: "Bearer " + KEY },
})).text();
const agents = text.trim().split("\n").map((line) => {
const [ip, port, user, pass] = line.trim().split(":");
return new ProxyAgent({
uri: "http://" + ip + ":" + port,
token: "Basic " + Buffer.from(user + ":" + pass).toString("base64"),
});
});
let i = 0;
async function get(url, tries = 3) {
for (let t = 0; t < tries; t++) {
const dispatcher = agents[i++ % agents.length];
try {
const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
if (res.ok) return await res.text();
} catch {
// try the next proxy
}
}
return null;
}
console.log((await get("https://example.com/"))?.length);Code thay đổi thế nào khi dùng xác thực IP?
Khi bạn đăng ký IPv4 của máy chủ chạy Node.js trên bảng điều khiển, bạn chỉ cần nhập địa chỉ mà không cần token trong ProxyAgent. SOCKS5 cũng có thể được sử dụng mà không cần tên người dùng và mật khẩu, ví dụ như socks5h://198.51.100.23:1080. Bạn không cần lưu mật khẩu trong biến môi trường hoặc mã, giúp dễ dàng triển khai trên nhiều máy chủ.
Nếu đang dùng axios thì kết nối như thế nào?
axios hoạt động trên module http, https của Node nên có thể dùng proxy bằng cách truyền agent. Với SOCKS5, dùng socks-proxy-agent cho httpAgent và httpsAgent, đặt proxy option của axios thành false để tránh trùng lặp cài đặt.
HTTP proxy cũng có thể được thiết lập bằng cách thêm host, port và auth vào tùy chọn proxy của axios, nhưng đối với địa chỉ HTTPS, sử dụng agent là cách ổn định hơn. Gói https-proxy-agent cho phép bạn kết nối HTTP proxy với cùng cấu trúc.
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false, timeout: 20000 });
const { data } = await client.get("https://api.ipify.org?format=json");
console.log(data);Cần lưu ý gì khi gửi nhiều yêu cầu cùng lúc?
Nếu bạn bắt đầu hàng nghìn yêu cầu cùng lúc với Promise.all, quá nhiều kết nối sẽ được mở và gây áp lực lên cả proxy và trang web đích. Hãy đặt giới hạn số lượng yêu cầu xử lý đồng thời và sử dụng hàng đợi công việc để chỉ chạy số lượng đó. Bạn có thể tự tạo một hàm nhỏ hoặc sử dụng các gói như p-limit.
Chia đều số kết nối đồng thời giữa các proxy tránh tập trung yêu cầu vào một IP. Gói chia sẻ bao gồm 500 kết nối đồng thời, hãy giữ tổng số yêu cầu đồng thời trong giới hạn này.
Có thể áp dụng proxy cho toàn bộ yêu cầu thông qua biến môi trường không?
EnvHttpProxyAgent của undici đọc các biến môi trường HTTP_PROXY, HTTPS_PROXY và NO_PROXY để xác định proxy. Đăng ký bằng setGlobalDispatcher để tất cả các fetch trong quy trình đó đi qua proxy. Điều này tiện lợi khi nhiều thư viện sử dụng fetch nội bộ.
Cài đặt toàn cục sẽ sử dụng cùng một IP cho tất cả các yêu cầu, phù hợp hơn cho các công cụ hoặc thử nghiệm cần truy cập từ một quốc gia cụ thể thay vì các công việc thu thập dữ liệu cần chia sẻ IP. Nếu cần luân chuyển, hãy chuyển dispatcher cho mỗi yêu cầu như đã đề cập trước đó.
// HTTPS_PROXY=http://spx_user:spx_pass@198.51.100.23:8000 node app.mjs
import { EnvHttpProxyAgent, setGlobalDispatcher, fetch } from "undici";
setGlobalDispatcher(new EnvHttpProxyAgent());
const res = await fetch("https://api.ipify.org");
console.log(await res.text());Làm thế nào để giải quyết các vấn đề thường gặp?
Nếu bạn truyền dispatcher vào fetch tích hợp sẵn của Node.js mà nó dường như bị bỏ qua, hãy kiểm tra xem bạn đã nhập fetch từ gói undici chưa. Nếu phiên bản fetch tích hợp sẵn và undici đã cài đặt khác nhau, agent có thể không tương thích, vì vậy cách chắc chắn nhất là nhập cả fetch và ProxyAgent từ cùng một gói.
Phản hồi 407 cho biết thông tin xác thực không đúng. Kiểm tra giá trị token sau Basic đã chứa tên người dùng:mật khẩu được mã hóa base64 chưa. ECONNREFUSED thường do sai cổng - dùng cổng HTTP cho agent HTTP, cổng SOCKS5 cho agent SOCKS.
- fetch và ProxyAgent đều nằm trong gói undici
- 407: Kiểm tra giá trị token và IP đã đăng ký
- ECONNREFUSED: Phân biệt cổng HTTP và SOCKS5
- Giới hạn thời gian cho mọi yêu cầu