Quản lý Luồng Lưu lượng API: Hướng dẫn về Chiến lược Giới hạn Tốc độ (Rate Limiting)

Cảm thấy quá tải bởi quá nhiều yêu cầu? Bộ giới hạn tốc độ API hoạt động như cảnh sát giao thông cho các dịch vụ web của bạn, ngăn chặn quá tải và đảm bảo sử dụng công bằng. Bài viết này là hướng dẫn giúp bạn giữ cho API của mình khỏe mạnh và an toàn.
Quản lý Luồng Lưu lượng API: Hướng dẫn về Chiến lược Giới hạn Tốc độ (Rate Limiting)

Bạn đã bao giờ được yêu cầu nói chậm lại khi bạn đang nói quá nhanh không? Bộ giới hạn tốc độ API hoạt động tương tự cho các chương trình máy tính.

Bộ giới hạn tốc độ API (API Rate Limiter) là gì?

Một bộ giới hạn tốc độ API kiểm soát tần suất người dùng có thể gửi yêu cầu đến một dịch vụ. Nó giống như một cảnh sát giao thông cho dữ liệu, đảm bảo không ai chiếm dụng tất cả tài nguyên.

Tại sao sử dụng bộ giới hạn tốc độ?

  • Ngăn chặn quá tải: Ngăn máy chủ bị sập do quá nhiều yêu cầu
  • Sử dụng công bằng: Đảm bảo mọi người đều có lượt
  • Bảo mật: Giúp chặn các cuộc tấn công làm tràn ngập hệ thống

Triển khai bộ giới hạn tốc độ API

Xác định chính sách giới hạn tốc độ của bạn

  • Quyết định số lượng yêu cầu được phép trong mỗi cửa sổ thời gian
  • Chọn kích thước cửa sổ thời gian (ví dụ: mỗi phút, giờ, ngày)
  • Xác định xem giới hạn áp dụng toàn cục hay theo từng người dùng/IP

Chọn cơ chế lưu trữ

  • Lưu trữ trong bộ nhớ (cho cài đặt máy chủ đơn)
  • Bộ nhớ đệm phân tán như Redis (cho môi trường đa máy chủ)
  • Cơ sở dữ liệu (để lưu trữ lâu dài và truy vấn phức tạp)

Thiết kế thành phần giới hạn tốc độ

  • Tạo một hàm để kiểm tra xem một yêu cầu có được phép hay không
  • Triển khai logic để đếm số yêu cầu trong cửa sổ thời gian
  • Thêm cơ chế để đặt lại bộ đếm sau khi cửa sổ thời gian hết hạn

Triển khai theo dõi yêu cầu

  • Tạo một định danh duy nhất cho mỗi khách hàng (ví dụ: khóa API, địa chỉ IP)
  • Ghi lại mỗi yêu cầu với một dấu thời gian
  • Loại bỏ các yêu cầu cũ nằm ngoài cửa sổ thời gian hiện tại

Thêm kiểm tra giới hạn tốc độ vào API của bạn

  • Tích hợp bộ giới hạn tốc độ vào đường ống xử lý yêu cầu API của bạn
  • Gọi bộ giới hạn tốc độ trước khi xử lý mỗi yêu cầu

Xử lý vi phạm giới hạn tốc độ

  • Trả về mã trạng thái HTTP thích hợp (thường là 429 Too Many Requests)
  • Bao gồm thông tin giới hạn tốc độ trong tiêu đề phản hồi

Tối ưu hóa hiệu suất

  • Sử dụng cấu trúc dữ liệu hiệu quả (ví dụ: tập hợp được sắp xếp trong Redis)
  • Triển khai bộ nhớ đệm để giảm tra cứu lưu trữ

Thêm giám sát và ghi nhật ký

  • Theo dõi các lần chạm giới hạn tốc độ và gần chạm
  • Thiết lập cảnh báo cho các vi phạm lặp đi lặp lại

Triển khai thông tin giới hạn tốc độ trong phản hồi

  • Thêm tiêu đề hiển thị mức sử dụng và giới hạn hiện tại
  • Cân nhắc cung cấp một điểm cuối để người dùng kiểm tra trạng thái giới hạn tốc độ của họ

Kiểm tra bộ giới hạn tốc độ

  • Xác minh nó giới hạn yêu cầu đúng cách
  • Đảm bảo nó đặt lại sau cửa sổ thời gian
  • Kiểm tra các trường hợp biên và các điều kiện đua tiềm ẩn

Lập tài liệu chính sách giới hạn tốc độ

  • Thông báo rõ ràng các giới hạn cho người dùng API
  • Cung cấp hướng dẫn về các phương pháp tốt nhất để tránh chạm giới hạn

Xem xét các tính năng nâng cao

  • Triển khai các cấp giới hạn tốc độ khác nhau cho các người dùng khác nhau
  • Thêm xử lý đột biến để cho phép các đợt tăng đột ngột trong việc sử dụng
  • Triển khai tiêu đề retry-after cho các khóa tạm thời

Ví dụ đơn giản trong Ruby on Rails

Lưu trữ trong bộ nhớ

class InMemoryRateLimiter
  MAX_REQUESTS = 100
  WINDOW_SIZE = 3600 # 1 hour in seconds

  def initialize
    @requests = {}
  end

  def allow_request?(user_id)
    current_time = Time.now.to_i
    @requests[user_id] ||= []
    @requests[user_id].reject! { |timestamp| timestamp < current_time - WINDOW_SIZE }

    if @requests[user_id].size < MAX_REQUESTS
      @requests[user_id] << current_time
      true
    else
      false
    end
  end
end

# In your controller:
def index
  limiter = InMemoryRateLimiter.new
  if limiter.allow_request?(current_user.id)
    # Process the request
  else
    render json: { error: "Too many requests" }, status: :too_many_requests
  end
end

Ưu điểm: Cực kỳ nhanh, đơn giản để triển khai
Nhược điểm: Không lưu trữ lâu dài, không mở rộng được trên nhiều máy chủ

Các phương pháp khác

Có nhiều phương pháp khác để thực hiện giới hạn tốc độ mà không sử dụng lưu trữ trong bộ nhớ. Dưới đây là một số phương pháp thay thế:

  • Redis
  • Lưu trữ trong cơ sở dữ liệu
  • Lưu trữ dựa trên tệp

Mỗi phương pháp này đều có ưu và nhược điểm riêng:

Redis

  • Ưu điểm: Nhanh, có khả năng mở rộng, lưu trữ lâu dài, hỗ trợ hệ thống phân tán
  • Nhược điểm: Yêu cầu máy chủ Redis riêng biệt, cần thêm cơ sở hạ tầng

Redis xuất sắc trong các tình huống yêu cầu hiệu suất cao và hệ thống phân tán, khiến nó trở thành lựa chọn phổ biến cho việc giới hạn tốc độ trong môi trường sản xuất, mặc dù có thêm độ phức tạp trong việc quản lý máy chủ Redis.

class RedisRateLimiter
  MAX_REQUESTS = 100
  WINDOW_SIZE = 3600 # 1 hour in seconds

  def initialize(user_id)
    @user_id = user_id
    @redis = Redis.new
  end

  def allow_request?
    current_time = Time.now.to_i
    key = "rate_limit:#{@user_id}"

    # Removes all elements in the sorted set with a score between min and max (inclusive).
    # We use it to remove all entries older than our time window (1 hour ago).
    # This effectively "slides" our window, keeping only recent entries.
    @redis.zremrangebyscore(key, 0, current_time - WINDOW_SIZE)

    # Returns the number of elements in the sorted set.
    # We use this to count how many requests have been made in our current time window.
    current_count = @redis.zcard(key)

    if current_count < MAX_REQUESTS
      # Adds one or more members to a sorted set, or updates the score if it already exists.
      # Here, we're adding an entry with the current timestamp as the score.
      # The value is a unique string (timestamp plus random hex) to avoid collisions.
      @redis.zadd(key, current_time, "#{current_time}.#{SecureRandom.hex(6)}")
      @redis.expire(key, WINDOW_SIZE)
      true
    else
      false
    end
  end
end

Cơ sở dữ liệu (ví dụ: PostgreSQL)

  • Ưu điểm: Lưu trữ lâu dài, hoạt động với cơ sở dữ liệu hiện có, tốt cho các truy vấn phức tạp
  • Nhược điểm: Chậm hơn Redis hoặc lưu trữ trong bộ nhớ, có thể tăng tải cho cơ sở dữ liệu chính

Dựa trên tệp

  • Ưu điểm: Lưu trữ lâu dài, đơn giản, không yêu cầu dịch vụ bổ sung
  • Nhược điểm: Chậm đối với lưu lượng truy cập cao, có thể gặp vấn đề về đồng thời

Lựa chọn tốt nhất phụ thuộc vào nhu cầu cụ thể của bạn, như yêu cầu về hiệu suất, nhu cầu lưu trữ lâu dài và quy mô của ứng dụng của bạn.

Đối với các ứng dụng Ruby

Các khái niệm và chiến lược giới hạn tốc độ mà chúng ta đã thảo luận có thể được áp dụng trên nhiều ngôn ngữ lập trình và framework khác nhau. Chúng không giới hạn ở Ruby hoặc Rails, mà có thể được điều chỉnh để phù hợp với bất kỳ stack công nghệ nào bạn đang làm việc.
Đối với các nhà phát triển Ruby, có một số gem có sẵn triển khai các khái niệm giới hạn tốc độ này, giúp bạn tiết kiệm công sức viết mọi thứ từ đầu. Một số gem phổ biến bao gồm:

  • Rack::Attack https://github.com/rack/rack-attack: Middleware Rack để chặn & hạn chế các yêu cầu lạm dụng
  • Rate Limiting https://github.com/iugu/rate-limiting: cung cấp công cụ để tạo các quy tắc có thể giới hạn tốc độ các route riêng biệt.
  • Throttling https://github.com/kpumuk/throttling: Gem Throttling cung cấp cách cơ bản nhưng rất mạnh mẽ để hạn chế các hành động khác nhau của người dùng trong ứng dụng của bạn. Về cơ bản, bạn có thể chỉ định số lần một hành động có thể được thực hiện trong một khoảng thời gian nhất định.

Để sử dụng một trong những gem này, bạn thường thêm nó vào Gemfile của bạn và cấu hình nó theo nhu cầu của bạn.

Hãy nhớ rằng, mặc dù những gem này làm cho việc triển khai dễ dàng hơn trong Ruby, nhưng các nguyên tắc cơ bản vẫn giống nhau trên các ngôn ngữ khác. Cho dù bạn đang sử dụng Python, JavaScript, Java hay bất kỳ ngôn ngữ nào khác, bạn có thể áp dụng các chiến lược giới hạn tốc độ này bằng cách sử dụng các thư viện dành riêng cho ngôn ngữ hoặc tự triển khai logic.

Khóa chặt API của bạn: Các biện pháp bảo mật đơn giản (Whitelisting, JSON Web Tokens, API Keys, OAuth 2.0, Basic Authentication, Hash-based Message Authentication Code) https://vulehuan.com/vi/blog/2024/7/khoa-chat-api-cua-ban-cac-bien-phap-bao-mat-don-gian-whitelisting-json-web-tokens-api-keys-oauth-2-0-basic-authentication-hashbased-message-authentication-code-668a4357f0915ca45912b919.html