2012-08-09 21 views
16

Tôi đang làm việc trên dịch vụ web được triển khai ở trên cùng của nginx + gunicorn + django. Các khách hàng là các ứng dụng điện thoại thông minh. Ứng dụng cần thực hiện một số cuộc gọi chạy dài tới các API bên ngoài (Facebook, Amazon S3 ...), vì vậy máy chủ chỉ cần gửi hàng công việc đến máy chủ công việc (sử dụng Celery qua Redis).Xử lý hiệu quả các kết nối HTTP chạy dài trong kiến ​​trúc web nginx/gunicorn/django

Bất cứ khi nào có thể, một khi máy chủ đã xếp hàng công việc, nó sẽ trả về ngay lập tức và kết nối HTTP bị đóng. Điều này hoạt động tốt và cho phép máy chủ duy trì tải rất cao.

client     server     job server 
    .      |      | 
    .      |      | 
    |------HTTP request----->|      | 
    |      |--------queue job------>| 
    |<--------close----------|      | 
    .      |      | 
    .      |      | 

Nhưng trong một số trường hợp, khách hàng cần nhận kết quả ngay sau khi hoàn thành công việc. Thật không may, không có cách nào máy chủ có thể liên lạc với máy khách khi kết nối HTTP bị đóng. Một giải pháp sẽ là dựa vào ứng dụng khách hàng bỏ phiếu cho máy chủ sau mỗi vài giây cho đến khi công việc được hoàn thành. Tôi muốn tránh giải pháp này, nếu có thể, chủ yếu là vì nó sẽ cản trở tính phản ứng của dịch vụ, và cũng bởi vì nó sẽ tải máy chủ với nhiều yêu cầu thăm dò không cần thiết.

Tóm lại, tôi muốn giữ kết nối HTTP và chạy, không làm gì cả (ngoại trừ việc gửi khoảng trắng mỗi lần để giữ kết nối TCP còn sống, chỉ like Amazon S3 does), cho đến khi hoàn thành công việc và máy chủ trả về kết quả.

client     server     job server 
    .      |      | 
    .      |      | 
    |------HTTP request----->|      | 
    |      |--------queue job------>| 
    |<------keep-alive-------|      | 
    |   [...]   |      | 
    |<------keep-alive-------|      | 
    |      |<--------result---------| 
    |<----result + close-----|      | 
    .      |      | 
    .      |      | 

Làm thế nào tôi có thể thực hiện các kết nối HTTP chạy dài một cách hiệu quả, giả sử máy chủ đang được tải rất cao (nó không phải là trường hợp nào, nhưng mục tiêu để có thể duy trì tải trọng cao nhất có thể, với hàng trăm hoặc hàng ngàn yêu cầu mỗi giây)?

Việc tải công việc thực tế xuống máy chủ khác phải đảm bảo mức sử dụng CPU thấp trên máy chủ, nhưng làm cách nào để tránh các quá trình chồng chất và sử dụng RAM của máy chủ hoặc yêu cầu gửi xuống do quá nhiều kết nối mở?

Điều này có lẽ chủ yếu là vấn đề định cấu hình nginx và gunicorn đúng cách. Tôi đã đọc một chút về async workers based on greenlets in gunicorn: tài liệu cho biết rằng công nhân không đồng bộ được sử dụng bởi "Ứng dụng thực hiện cuộc gọi chặn dài (Tức là, dịch vụ web bên ngoài)", điều này nghe có vẻ hoàn hảo. Nó cũng nói "Nói chung, một ứng dụng sẽ có thể sử dụng các lớp công nhân này mà không có thay đổi". Nghe thật tuyệt. Bất kỳ phản hồi về điều này?

Cảm ơn lời khuyên của bạn.

+0

Bạn đã nghiên cứu các giải pháp 'bỏ phiếu dài' của AJAX cho django chưa? Nó có vẻ như về cơ bản giống nhau. – dgel

+0

Vâng, bạn nói đúng, nó có thể giống nhau. AJAX có nghĩa là javascript ở phía máy khách, đó là nhiều hơn về các trình duyệt web, trong trường hợp của tôi, ứng dụng khách là một ứng dụng softphone. Nhưng phía máy chủ có lẽ gần như giống nhau, ý tưởng tuyệt vời. – MiniQuark

Trả lời

28

Tôi là answering my own question, có lẽ ai đó có giải pháp tốt hơn.

Đọc gunicorn's documentation thêm một chút và đọc thêm một chút về eventletgevent, tôi nghĩ rằng gunicorn trả lời câu hỏi của tôi một cách hoàn hảo. Gunicorn có một quy trình tổng thể quản lý một nhóm công nhân. Mỗi công nhân có thể được đồng bộ (đơn luồng, xử lý một yêu cầu tại một thời điểm) hoặc không đồng bộ (mỗi công nhân thực sự xử lý nhiều yêu cầu gần như cùng một lúc).

Nhân viên đồng bộ rất đơn giản để hiểu và gỡ lỗi, và nếu một nhân viên thất bại, chỉ một yêu cầu bị mất. Nhưng nếu một nhân viên bị kẹt trong một cuộc gọi API bên ngoài chạy dài, thì về cơ bản nó đang ngủ. Vì vậy, trong trường hợp tải trọng cao, tất cả công nhân có thể sẽ ngủ trong khi đợi kết quả, và các yêu cầu sẽ kết thúc.

Vì vậy, giải pháp là thay đổi loại công nhân mặc định từ đồng bộ thành không đồng bộ (chọn eventlet hoặc gevent, here's a comparison). Bây giờ mỗi công nhân chạy nhiều green threads, mỗi công việc rất nhẹ. Bất cứ khi nào một luồng phải chờ một số I/O, một luồng màu xanh khác sẽ tiếp tục thực hiện. Điều này được gọi là cooperative multitasking. Nó rất nhanh và rất nhẹ (một nhân viên có thể xử lý hàng nghìn yêu cầu đồng thời, nếu họ đang đợi I/O). Chính xác những gì tôi cần.

Tôi đã tự hỏi làm thế nào tôi nên thay đổi mã hiện tại của mình, nhưng dường như các mô-đun python chuẩn là monkey-patched bởi gunicorn khi khởi động (thực sự bởi eventlet hoặc gevent).

Có một loạt các thông số có thể được tinh chỉnh trong gunicorn, ví dụ số lượng tối đa của khách hàng đồng thời sử dụng worker_connections tham số gunicorn, số lượng tối đa cấp phát các kết nối bằng cách sử dụng tham số backlog vv

Đây chỉ là tuyệt vời, tôi sẽ bắt đầu kiểm tra ngay lập tức!

+0

Bất kỳ phản hồi hoặc cập nhật nào sau khi thử nghiệm? Về để làm theo bước chân của bạn – dsldsl

+0

Hi dsldsl. Tôi khá hạnh phúc với gunicorn và gevent cho đến nay. Tôi đã làm một vài xét nghiệm đơn giản, thỏa đáng, gunicorn hoạt động như nó nói: Tôi đã thử chạy 1 công nhân đơn lẻ rồi mở (và không đóng) một vài kết nối, và điều đó không ngăn được yêu cầu đầy đủ tiếp theo. Nhưng cho đến nay tôi đã không có thời gian để làm điểm chuẩn thực sự. Thưởng thức! – MiniQuark

Các vấn đề liên quan