Bảo vệ API của bạn với Giới hạn Tốc độ Token Bucket
Khi xây dựng APIs, dịch vụ trò chuyện hoặc hệ thống thời gian thực, một trong những thách thức lớn nhất là ngăn chặn khách hàng làm quá tải máy chủ của bạn với quá nhiều yêu cầu. Nếu không có biện pháp bảo vệ, một làn sóng lưu lượng truy cập có thể làm chậm hiệu suất hoặc thậm chí làm sập hệ thống.
Đây chính là lúc giới hạn tốc độ trở nên cần thiết. Trong số nhiều kỹ thuật có sẵn, Thuật toán Token Bucket được sử dụng rộng rãi vì tính đơn giản, hiệu quả và khả năng cho phép các cú sốc lưu lượng mà không làm mất kiểm soát tổng thể.
Giới hạn Tốc độ là gì?
Giới hạn tốc độ là quá trình kiểm soát số lượng yêu cầu mà một khách hàng có thể gửi đến máy chủ trong một khoảng thời gian nhất định.
Ví dụ:
- Một khách hàng có thể được phép 10 yêu cầu mỗi giây.
- Nếu họ vượt quá giới hạn này, các yêu cầu bổ sung của họ sẽ bị từ chối cho đến khi giây tiếp theo bắt đầu.
Giới hạn tốc độ đảm bảo:
- Sử dụng tài nguyên công bằng giữa các người dùng
- Bảo vệ chống lại lạm dụng, tấn công brute-force hoặc spam
- Cải thiện độ ổn định và độ tin cậy của máy chủ
Thuật toán Token Bucket
Thuật toán Token Bucket hoạt động như sau:
- Mỗi khách hàng được gán một thùng.
- Thùng có một dung lượng nhất định (ví dụ, 10 token).
- Token được nạp lại với một tỷ lệ cố định (ví dụ, 1 token mỗi giây).
- Mỗi yêu cầu tiêu tốn một token.
- Nếu thùng rỗng, yêu cầu sẽ bị từ chối.
Cách tiếp cận này cho phép các cú sốc ngắn hạn của yêu cầu khi có token, trong khi vẫn duy trì một tỷ lệ yêu cầu trung bình lâu dài.
Quy trình Token Bucket
Các bước thực hiện như sau:
- Kiểm tra xem khách hàng có mới không
Nếu có, tạo một thùng cho họ với dung lượng đầy đủ. - Nạp lại token
Dựa trên thời gian đã trôi qua kể từ lần nạp lại cuối cùng. - Kiểm tra xem token có sẵn không
- Nếu có, tiêu tốn một token và chấp nhận yêu cầu.
- Nếu không, từ chối yêu cầu.
Cân bằng này đảm bảo rằng khách hàng có thể thực hiện các yêu cầu nhanh chóng nhưng không thể vượt quá tỷ lệ cho phép trung bình.
Triển khai Token Bucket trong Node.js
Hãy cùng xây dựng một máy chủ HTTP đơn giản với Giới hạn Tốc độ Token Bucket.
Bước 1: Cài đặt
javascript
const http = require('http');
// Cấu hình
const bucketCapacity = 10; // số token tối đa mỗi người dùng
const refillRate = 1; // token mỗi giây
const ipBuckets = new Map(); // lưu trữ thùng cho mỗi IP
Chúng ta định nghĩa kích thước thùng, tỷ lệ nạp lại và một bản đồ để lưu trữ thùng token của từng người dùng.
Bước 2: Hàm Nạp lại
javascript
function refillTokens(bucket) {
const now = Date.now();
const elapsed = (now - bucket.lastRefillTime) / 1000; // giây
const refill = Math.floor(elapsed * refillRate);
if (refill > 0) {
bucket.tokens = Math.min(bucketCapacity, bucket.tokens + refill);
bucket.lastRefillTime = now;
}
}
Hàm này tính toán số lượng token sẽ được thêm vào dựa trên thời gian đã trôi qua kể từ lần nạp lại cuối cùng và cập nhật thùng mà không vượt quá dung lượng của nó.
Bước 3: Middleware Giới hạn Tốc độ
javascript
function rateLimitMiddleware(req, res) {
const ip = req.socket.remoteAddress;
// Nếu người dùng mới, tạo thùng
if (!ipBuckets.has(ip)) {
ipBuckets.set(ip, { tokens: bucketCapacity, lastRefillTime: Date.now() });
}
const bucket = ipBuckets.get(ip);
refillTokens(bucket);
if (bucket.tokens > 0) {
bucket.tokens -= 1; // tiêu tốn một token
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Yêu cầu đã được chấp nhận\n');
} else {
res.writeHead(429, { 'Content-Type': 'text/plain' });
res.end('Quá nhiều yêu cầu\n');
}
}
Hàm này quản lý thùng cho từng IP, tiêu tốn token khi có sẵn và từ chối yêu cầu nếu token rỗng.
Bước 4: Khởi động Máy chủ
javascript
const server = http.createServer(rateLimitMiddleware);
server.listen(3000, () => {
console.log('Máy chủ đang chạy tại http://localhost:3000/');
});
Điều này khởi động máy chủ trên cổng 3000, áp dụng logic giới hạn tốc độ cho mỗi yêu cầu.
Mã đầy đủ
javascript
const http = require('http');
// Cấu hình
const bucketCapacity = 10; // số token tối đa mỗi người dùng
const refillRate = 1; // token mỗi giây
const ipBuckets = new Map(); // lưu trữ thùng cho mỗi IP
// Hàm nạp lại
function refillTokens(bucket) {
const now = Date.now();
const elapsed = (now - bucket.lastRefillTime) / 1000; // giây
const refill = Math.floor(elapsed * refillRate);
if (refill > 0) {
bucket.tokens = Math.min(bucketCapacity, bucket.tokens + refill);
bucket.lastRefillTime = now;
}
}
// Middleware
function rateLimitMiddleware(req, res) {
const ip = req.socket.remoteAddress;
// Nếu người dùng mới, tạo thùng
if (!ipBuckets.has(ip)) {
ipBuckets.set(ip, { tokens: bucketCapacity, lastRefillTime: Date.now() });
}
const bucket = ipBuckets.get(ip);
refillTokens(bucket);
if (bucket.tokens > 0) {
bucket.tokens -= 1; // tiêu tốn một token
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Yêu cầu đã được chấp nhận\n');
} else {
res.writeHead(429, { 'Content-Type': 'text/plain' });
res.end('Quá nhiều yêu cầu\n');
}
}
// Khởi động máy chủ
const server = http.createServer(rateLimitMiddleware);
server.listen(3000, () => {
console.log('Máy chủ đang chạy tại http://localhost:3000/');
});
Các trường hợp sử dụng
- Cổng API: ngăn chặn lạm dụng bằng cách giới hạn yêu cầu mỗi khách hàng
- Ứng dụng trò chuyện: ngăn chặn spam bằng cách kiểm soát tần suất gửi tin nhắn
- Hệ thống xác thực: làm chậm các nỗ lực đăng nhập brute-force
- Thiết bị IoT: quản lý các cú sốc dữ liệu từ cảm biến và thiết bị
Lợi ích của Token Bucket
- Cho phép các cú sốc yêu cầu lên đến dung lượng thùng
- Duy trì một tỷ lệ yêu cầu ổn định lâu dài
- Triển khai đơn giản và hiệu quả
- Hành vi nạp lại dự đoán được
Kết luận
Thuật toán Token Bucket là một cách thực tiễn và hiệu quả để thực hiện giới hạn tốc độ. Nó kết hợp tính linh hoạt và kiểm soát bằng cách cho phép các cú sốc tạm thời trong khi duy trì một tỷ lệ yêu cầu trung bình có thể dự đoán.
Nếu bạn đang xây dựng APIs, hệ thống trò chuyện hoặc ứng dụng thời gian thực, giới hạn tốc độ Token Bucket có thể giúp bạn bảo vệ máy chủ, đảm bảo sự công bằng và cải thiện độ tin cậy của hệ thống.
FAQ
1. Giới hạn tốc độ là gì?
Giới hạn tốc độ là phương pháp kiểm soát số lượng yêu cầu mà một khách hàng có thể gửi đến máy chủ trong một khoảng thời gian nhất định.
2. Token Bucket hoạt động như thế nào?
Token Bucket cho phép mỗi khách hàng có một thùng chứa token, trong đó mỗi yêu cầu sẽ tiêu tốn một token và token được nạp lại theo tỷ lệ nhất định.
3. Có những lợi ích gì khi sử dụng Token Bucket?
Token Bucket cho phép xử lý cú sốc lưu lượng, duy trì tỷ lệ yêu cầu ổn định và dễ dàng triển khai.