2011-08-15 27 views
35

Tôi đang cố gắng hiểu đầy đủ các tùy chọn xử lý yêu cầu đồng thời trong Rack. Tôi đã sử dụng async_sinatra để xây dựng một ứng dụng bỏ phiếu dài và hiện đang thử nghiệm với Giá đỡ bằng kim loại trần sử dụng throw :async và/hoặc cờ --threaded của Thin. Tôi cảm thấy thoải mái với chủ đề này, nhưng có một số điều tôi không thể hiểu được. (Không, tôi không hiểu lầm về tính đồng thời cho song song, và có, tôi hiểu những hạn chế áp đặt bởi GIL).Giá đồng thời - rack.multithread, async.callback, hoặc cả hai?

Q1. Các thử nghiệm của tôi cho thấy rằng thin --threaded (tức là rack.multithread=true) chạy yêu cầu đồng thời trong các chuỗi riêng biệt (tôi giả sử sử dụng EM), có nghĩa là yêu cầu chạy dài A sẽ không chặn yêu cầu B (IO sang một bên). Điều này có nghĩa là ứng dụng của tôi không yêu cầu bất kỳ mã hóa đặc biệt nào (ví dụ: gọi lại) để đạt được sự đồng thời (một lần nữa, bỏ qua việc chặn các cuộc gọi DB, IO, v.v.). Đây là những gì tôi tin rằng tôi đã quan sát - nó có đúng không?

Q2. Có một phương tiện khác, được thảo luận nhiều hơn về việc đạt được sự tương tranh, liên quan đến EventMachine.deferthrow :async. Nói đúng ra, các yêu cầu là không phải được xử lý bằng các chuỗi. Chúng được xử lý bằng serially, nhưng vượt qua việc nâng vật nặng và gọi lại cho EventMachine, sử dụng async.callback để gửi phản hồi sau này. Sau khi yêu cầu A đã tải công việc của mình xuống EM.defer, yêu cầu B được bắt đầu. Điều này có đúng không?

Q3. Giả sử ở trên là nhiều hay ít chính xác, có lợi thế cụ thể nào cho một phương thức so với phương thức khác không? Rõ ràng --threaded trông giống như một viên đạn ma thuật. Có bất kỳ nhược điểm nào không? Nếu không, tại sao mọi người lại nói về async_sinatra/throw :async/async.callback? Có lẽ trước đây là "Tôi muốn làm cho ứng dụng Rails của tôi trở nên nhẹ hơn một chút dưới tải nặng" và sau này là ứng dụng phù hợp hơn cho các ứng dụng có nhiều yêu cầu dài hạn? Hoặc có lẽ quy mô là một yếu tố? Chỉ cần đoán ở đây.

Tôi đang chạy Thin 1.2.11 trên MRI Ruby 1.9.2. (FYI, tôi phải sử dụng cờ --no-epoll, vì có a long-standing, supposedly-resolved-but-not-really problem bằng cách sử dụng tính năng epoll và Ruby 1.9.2 của EventMachine. Đó là bên cạnh điểm, nhưng bất kỳ thông tin chi tiết nào đều được chào đón.)

+0

Sự cố epoll phải được khắc phục như được nói trong vé đó, đây là [cam kết] (https://github.com/eventmachine/eventmachine/commit/d684cc3b77a6c401295a3086b5671fe4ec335a64) mà họ đang trỏ đến. – Bitterzoet

+0

Nếu tôi xóa cờ --no-epoll, các yêu cầu luồng của tôi sẽ chuyển từ mili giây sang phút. EM 0.12.10, Ruby 1.9.2-p180. Tôi cho rằng tôi có thể thử biên soạn p290 ... – bioneuralnet

+0

Câu hỏi hay. Tôi đã hỏi một câu hỏi rất giống ở đây: http://stackoverflow.com/questions/8146851/how-to-deploy-a-threadsafe-asynchronous-rails-app và đã thực hiện một số thử nghiệm ở đây: https: // github. com/jjb/threaded-rails-ví dụ (lưu ý rằng trong khi luồng mỏng là thành công không đồng bộ, nó tiêu chuẩn chậm hơn) –

Trả lời

24

Lưu ý: Tôi sử dụng Từ đồng nghĩa chung cho tất cả máy chủ web triển khai phần mở rộng async Rack (tức là Rainbows !, Ebb, phiên bản tương lai của Puma, ...)

Q1. Chính xác. Nó sẽ quấn thế hệ phản hồi (còn gọi là call) trong EventMachine.defer { ... }, điều này sẽ khiến EventMachine đẩy nó vào nhóm chủ đề tích hợp của nó.

Q2. Sử dụng async.callback kết hợp với EM.defer thực sự không có ý nghĩa quá nhiều, vì về cơ bản nó sẽ sử dụng nhóm luồng, kết thúc bằng một cấu trúc tương tự như được mô tả trong Q1. Sử dụng async.callback có ý nghĩa khi chỉ sử dụng thư viện eventmachine cho IO. Thin sẽ gửi phản hồi cho khách hàng khi env['async.callback'] được gọi với một phản hồi Rack bình thường làm đối số.

Nếu nội dung là EM::Deferrable, Mỏng sẽ không đóng kết nối cho đến khi thành công đó có thể trì hoãn. Một bí mật khá tốt: Nếu bạn muốn nhiều hơn bình chọn dài (tức là giữ kết nối mở sau khi gửi phản hồi một phần), bạn cũng có thể trả trực tiếp một đối tượng thân thể mà không cần phải sử dụng throw :async hoặc mã trạng thái -1.

Q3. Bạn đoán đúng. Phân phối theo luồng có thể cải thiện tải trên ứng dụng Rack không thay đổi. Tôi thấy cải thiện 20% cho các ứng dụng Sinatra đơn giản trên máy của tôi với Ruby 1.9.3, thậm chí nhiều hơn khi chạy trên Rubinius hoặc JRuby, nơi tất cả các lõi có thể được sử dụng. Cách tiếp cận thứ hai là hữu ích nếu bạn viết ứng dụng của bạn theo cách được tổ chức.

Bạn có thể ném rất nhiều ma thuật và hacks lên trên Rack để có ứng dụng không phải là sự kiện sử dụng các cơ chế đó (xem đồng bộ hóa hoặc đồng bộ sinatra), nhưng điều đó sẽ khiến bạn bị gỡ rối và phụ thuộc vào địa ngục .

Cách tiếp cận không đồng bộ có ý nghĩa thực sự với các ứng dụng có xu hướng được giải quyết tốt nhất với cách tiếp cận có sự kiện, chẳng hạn như a web chat. Tuy nhiên, tôi sẽ không khuyên bạn nên sử dụng phương pháp tiếp cận luồng để thực hiện bỏ phiếu dài vì mọi kết nối bỏ phiếu sẽ chặn một chuỗi. Điều này sẽ để lại cho bạn một tấn chủ đề hoặc kết nối mà bạn không thể xử lý. Hồ bơi thread của EM có kích thước 20 luồng theo mặc định, giới hạn cho bạn 20 kết nối chờ đợi cho mỗi quá trình.

Bạn có thể sử dụng máy chủ tạo chuỗi mới cho mọi kết nối gửi đến, nhưng tạo chuỗi là tốn kém (ngoại trừ trên MacRuby, nhưng tôi sẽ không sử dụng MacRuby trong bất kỳ ứng dụng sản xuất nào). Ví dụ: servnet-http-server. Lý tưởng nhất, những gì bạn muốn là một bản đồ n: m của các yêu cầu và chủ đề. Nhưng không có máy chủ nào cung cấp điều đó.

Nếu bạn muốn tìm hiểu thêm về chủ đề: Tôi đã trình bày về điều này tại Rocky Mountain Ruby (và một tấn hội nghị khác). Bạn có thể tìm thấy bản ghi video on confreaks.

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