2011-02-08 27 views
8

Tôi nhớ cách đây 2 hoặc 3 năm để đọc một vài bài báo mà mọi người cho rằng các thư viện luồng hiện đại đã trở nên tốt đến nỗi các máy chủ theo yêu cầu sẽ không chỉ dễ viết hơn các máy chủ không chặn mà chúng sẽ nhanh hơn , quá. Tôi tin rằng điều này thậm chí đã được chứng minh trong Java với một JVM đã ánh xạ các luồng Java tới các phần tử pthread (tức là chi phí đầu vào Java nio lớn hơn chi phí chuyển ngữ cảnh).Mô hình đề xuất cho mỗi yêu cầu có nhanh hơn I/O không chặn không?

Nhưng bây giờ tôi thấy tất cả các máy chủ "cắt cạnh" sử dụng thư viện không đồng bộ (Java nio, epoll, thậm chí node.js). Điều này có nghĩa là async đã thắng?

Trả lời

6

Không theo ý kiến ​​của tôi. Nếu cả hai mô hình đều được triển khai tốt (đây là yêu cầu lớn), tôi nghĩ rằng khái niệm về NIO nên chiếm ưu thế.

Tại trung tâm của máy tính là lõi. Không có vấn đề gì bạn làm, bạn không thể song song ứng dụng của bạn nhiều hơn bạn có lõi. tức là nếu bạn có một máy tính 4 lõi, bạn chỉ có thể làm 4 việc cùng một lúc (tôi đang chú ý đến một số chi tiết ở đây, nhưng điều đó đủ cho đối số này).

Mở rộng suy nghĩ đó, nếu bạn có nhiều chủ đề hơn lõi, bạn sẽ lãng phí. Chất thải đó có hai dạng. Đầu tiên là chi phí của các chủ đề phụ. Thứ hai là thời gian chuyển đổi giữa các luồng. Cả hai đều có thể nhỏ, nhưng họ đang có.

Lý tưởng nhất là bạn có một chuỗi trên mỗi lõi và mỗi luồng trong số đó đang chạy ở tốc độ xử lý 100% trên lõi của chúng. Chuyển đổi tác vụ sẽ không xảy ra trong lý tưởng. Tất nhiên có hệ điều hành, nhưng nếu bạn lấy một máy tính cốt lõi 16 và để lại 2-3 chủ đề cho hệ điều hành, sau đó 13-14 còn lại đi về phía ứng dụng của bạn. Những chủ đề đó có thể chuyển đổi những gì họ đang thực hiện trong phạm vi ứng dụng của bạn, như khi chúng bị chặn bởi yêu cầu IO, nhưng không phải trả chi phí đó ở cấp hệ điều hành. Viết nó ngay vào ứng dụng của bạn.

Một ví dụ tuyệt vời về việc chia tỷ lệ này được hiển thị trong SEDA http://www.eecs.harvard.edu/~mdw/proj/seda/. Nó cho thấy khả năng mở rộng quy mô tốt hơn nhiều theo tải so với mô hình chuỗi yêu cầu theo yêu cầu.

Trải nghiệm cá nhân của tôi là với Netty. Tôi đã có một ứng dụng đơn giản. Tôi đã triển khai tốt trong cả Tomcat và Netty. Sau đó, tôi tải thử nghiệm nó với 100 yêu cầu đồng thời (lên tới 800 tôi tin). Cuối cùng Tomcat chậm lại để thu thập dữ liệu và thể hiện hành vi cực kỳ bùng nổ/chậm chạp. Trong khi thực hiện Netty chỉ đơn giản là tăng thời gian đáp ứng, nhưng tiếp tục với thông lượng tổng thể cực kỳ.

Xin lưu ý, bản lề này được thực hiện vững chắc. NIO vẫn còn tốt hơn với thời gian. Chúng tôi đang tìm hiểu cách điều chỉnh các hệ điều hành máy chủ để làm việc tốt hơn với nó cũng như cách triển khai các JVM để tận dụng tốt hơn chức năng của hệ điều hành. Tôi không nghĩ rằng một người chiến thắng có thể được tuyên bố, nhưng tôi tin rằng NIO sẽ là người chiến thắng cuối cùng, và nó đã làm khá tốt rồi.

+0

Đây là một thử nghiệm thực tế rất thú vị. Đối với một số ứng dụng, 800 yêu cầu đồng thời thực sự không phải là nhiều. Tôi tự hỏi nếu sự chậm trễ trong Tomcat là do bối cảnh chuyển đổi (hoặc GC, như @irreputable gợi ý), có nghĩa là mô hình thread-per-yêu cầu chính nó là lỗi hơn là việc thực hiện Tomcat của nó. – Graham

+1

Để công bằng, đây là một máy chủ duy nhất đang được thử nghiệm chứ không phải một cụm. Đối với 800 yêu cầu. Theo kinh nghiệm của tôi, 800 đồng thời là khá cao. Điều quan trọng là phải phân biệt các yêu cầu đồng thời từ các kết nối. Nếu bạn nhận được thời gian phản hồi trong phạm vi 100ms tại tải đó, bạn đang hỗ trợ 8k yêu cầu mỗi giây. Đó không phải là xấu cho một hộp duy nhất. Quy mô mà ra cho cả ngày và bạn xử lý 700 triệu yêu cầu với một máy chủ duy nhất. Không có nhiều ứng dụng cần nhiều. – rfeak

6

Nó nhanh hơn miễn là có đủ bộ nhớ.

Khi có quá nhiều kết nối, hầu hết trong số đó là không hoạt động, NIO có thể lưu chủ đề do đó tiết kiệm bộ nhớ và hệ thống có thể xử lý nhiều người dùng hơn mô hình chuỗi kết nối.

CPU không phải là nhân tố trực tiếp tại đây. Với NIO, bạn thực sự cần phải tự mình triển khai một mô hình luồng, vốn không thể nhanh hơn các luồng của JVM.

Trong cả hai lựa chọn, bộ nhớ là nút cổ chai cuối cùng. Khi tải tăng và bộ nhớ sử dụng các phương pháp tiếp cận tối đa, GC sẽ rất bận rộn, và hệ thống thường thể hiện các triệu chứng của CPU 100%.

+0

Điều đó có ý nghĩa. Một tìm kiếm nhanh chóng của Google cho thấy ngăn xếp mặc định HotSpot cho mỗi luồng trên các máy 64 bit là 1MB (tôi biết đây không phải là đống, nhưng nó sẽ làm giảm lượng bộ nhớ còn lại cho heap). Điều đó một mình có nghĩa là thread-per-request không phù hợp với C10K, ít nhất là trong Java. Nhưng nếu bộ nhớ là hạn chế, nó có thể phù hợp với hàng trăm kết nối đồng thời. – Graham

4

Một số thời gian trước, tôi đã tìm thấy rather interesting presentation cung cấp một số thông tin chi tiết về "lý do mô hình luồng trên mỗi khách hàng cũ tốt hơn". Thậm chí còn có số đo. Tuy nhiên tôi vẫn nghĩ về nó. Theo tôi, câu trả lời hay nhất cho câu hỏi này là "nó phụ thuộc" bởi vì hầu hết (nếu không phải tất cả) các quyết định kỹ thuật đều là các lệnh giao dịch.

+0

Tôi nghĩ rằng đây có thể là bản trình bày mà tôi đang nhớ. Những quan sát này (nio có thể chậm) kết hợp với sự đánh giá mới về sự đồng thời dựa trên diễn viên ở Erlang và Scala khiến tôi nghĩ rằng thế giới đang tiến tới các mô hình đồng bộ. Nhưng bên ngoài những ngôn ngữ đó (và Scala chủ yếu là gian lận), tôi nghĩ rằng tôi sẽ gắn bó với đồng bộ như mặc định cho đến khi tôi gặp phải các vấn đề trong thế giới thực như những vấn đề mà @rfeak gặp phải. – Graham

1

Giống như bản trình bày đó đã nói - có tốc độ và có khả năng mở rộng.

Một trường hợp trong đó chuỗi yêu cầu gần như chắc chắn sẽ nhanh hơn bất kỳ giải pháp không đồng bộ nào là khi bạn có số lượng khách hàng tương đối nhỏ (ví dụ: < 100), nhưng mỗi khách hàng có khối lượng rất cao. ví dụ. một ứng dụng thời gian thực mà không có hơn 100 khách hàng đang gửi/tạo 500 tin nhắn mỗi giây. Mô hình đề xuất cho mỗi yêu cầu chắc chắn sẽ hiệu quả hơn bất kỳ giải pháp vòng lặp sự kiện async nào. Async cân bằng tốt hơn khi có nhiều yêu cầu/khách hàng bởi vì nó không lãng phí các chu kỳ chờ đợi trên nhiều kết nối máy khách, nhưng khi bạn có một số lượng lớn các máy khách có ít chờ đợi, nó kém hiệu quả hơn. Từ những gì tôi thấy, các tác giả của Node và Netty đều nhận ra rằng những khung công tác này chủ yếu nhằm giải quyết các tình huống khả năng mở rộng kết nối với khối lượng lớn, thay vì nhanh hơn cho một số lượng nhỏ các máy khách có khối lượng lớn hơn.

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