Tôi có nên gắn bó hoặc từ bỏ Python để giải quyết đồng thời?


31

Tôi có một dự án 10 nghìn LỘC được viết bằng Django với khá nhiều Celery ( RabbitMQ ) cho các công việc không điển hình và công việc nền cần thiết, và đã đi đến kết luận rằng các phần của hệ thống sẽ được hưởng lợi từ việc viết lại trong một thứ khác ngoài Django để đồng thời tốt hơn . Lý do bao gồm:

  • Tín hiệu xử lý và các đối tượng đột biến. Đặc biệt là khi một tín hiệu kích hoạt tín hiệu khác, việc xử lý chúng trong Django bằng ORM có thể gây ngạc nhiên khi các trường hợp thay đổi hoặc biến mất. Tôi muốn sử dụng một số phương pháp nhắn tin trong đó dữ liệu được truyền đi không thay đổi trong một trình xử lý ( phương pháp sao chép trên văn bản của Clojure có vẻ tốt, nếu tôi hiểu đúng).
  • Các bộ phận của hệ thống không dựa trên web và cần hỗ trợ tốt hơn để thực hiện đồng thời các nhiệm vụ. Ví dụ: hệ thống đọc các thẻ NFC và khi một đèn LED được đọc trong vài giây (tác vụ Celery), âm thanh sẽ được phát (tác vụ Celery khác) và cơ sở dữ liệu được truy vấn (tác vụ khác). Điều này được triển khai như một lệnh quản lý Django, nhưng Django và ORM của nó được đồng bộ hóa và bộ nhớ chia sẻ bị hạn chế (chúng tôi đang nghĩ đến việc thêm nhiều độc giả NFC và tôi không nghĩ rằng phương pháp Django + Celery sẽ hoạt động lâu hơn nữa, Tôi muốn thấy khả năng truyền tin tốt hơn).

Những ưu và nhược điểm của việc sử dụng một cái gì đó như Twisted hoặc Tornado so với việc sử dụng một ngôn ngữ như Erlang hoặc Clojure là gì? Tôi quan tâm đến lợi ích thiết thực và bất lợi.

Làm thế nào bạn đi đến kết luận rằng một số phần của hệ thống sẽ có giá tốt hơn trong ngôn ngữ khác? Bạn đang gặp vấn đề về hiệu suất? Những vấn đề đó nghiêm trọng đến mức nào? Nếu nó có thể nhanh hơn, điều cần thiết là nó nhanh hơn?

Ví dụ 1: Django tại nơi làm việc ngoài yêu cầu HTTP:

  1. Một thẻ NFC được đọc.
  2. Cơ sở dữ liệu (và có thể là LDAP) được truy vấn và chúng tôi muốn làm gì đó khi dữ liệu có sẵn (đèn đỏ hoặc xanh lục, phát âm thanh). Khối này sử dụng Django ORM, nhưng miễn là có nhân viên Celery thì không vấn đề gì. Có thể là một vấn đề với nhiều trạm hơn.

Ví dụ 2: Truyền tin nhắn qua mạng sử dụng tín hiệu Django:

  1. Một post_deletesự kiện được xử lý, các đối tượng khác có thể bị thay đổi hoặc xóa vì điều này.
  2. Cuối cùng, thông báo sẽ được gửi đến người dùng. Ở đây, sẽ rất tuyệt nếu các đối số được chuyển đến trình xử lý thông báo là các bản sao của các đối tượng bị xóa hoặc bị xóa và được đảm bảo không thay đổi trong trình xử lý. (Tất nhiên, nó có thể được thực hiện đơn giản bằng cách không chuyển các đối tượng được quản lý bởi ORM cho trình xử lý.)

Tôi nghĩ câu trả lời tốt hơn sẽ xảy ra nếu bạn giải thích thêm về lý do tại sao bạn đi đến kết luận
Winston Ewert

5
Trước khi bất cứ ai nói rằng các câu hỏi lựa chọn ngôn ngữ là lạc đề, tôi sẽ nói rằng tôi nghĩ câu hỏi này không sao vì đây là một vấn đề thực tế với các yêu cầu cụ thể. Tôi hy vọng nó rút ra một số so sánh chi tiết.
Adam Lear

Xoắn là trái ngược với đồng thời! Nó là một máy chủ luồng duy nhất được điều khiển theo sự kiện, nó sẽ không đưa bạn đến bất cứ đâu nếu bạn cần sự tương tranh thực sự.

Câu trả lời:


35

Suy nghĩ mở đầu

Làm thế nào bạn đi đến kết luận rằng một số phần của hệ thống sẽ có giá tốt hơn trong ngôn ngữ khác? Bạn đang gặp vấn đề về hiệu suất? Những vấn đề đó nghiêm trọng đến mức nào? Nếu nó có thể nhanh hơn, điều cần thiết là nó nhanh hơn?

Không đồng bộ một luồng

Có một số câu hỏi và các tài nguyên web khác đã giải quyết các khác biệt, ưu và nhược điểm của sự không đồng bộ đơn luồng so với đồng thời đa luồng. Thật thú vị khi đọc về cách mô hình không đồng bộ một luồng của Node.js thực hiện khi I / O là nút cổ chai lớn và có nhiều yêu cầu được phục vụ cùng một lúc.

Twisted, Tornado và các mô hình không đồng bộ khác sử dụng tuyệt vời của một luồng đơn. Vì rất nhiều chương trình web có rất nhiều I / O (mạng, cơ sở dữ liệu, v.v.), thời gian chờ đợi cho các cuộc gọi từ xa tăng lên đáng kể. Đó là thời gian có thể được dành để làm những việc khác, như khởi động các cuộc gọi cơ sở dữ liệu khác, kết xuất trang và tạo dữ liệu. Việc sử dụng chủ đề đơn đó là cực kỳ cao.

Một trong những lợi ích lớn nhất của sợi đơn không đồng pha là nó sử dụng nhiều ít bộ nhớ. Trong thực thi đa luồng, mỗi luồng yêu cầu một lượng bộ nhớ dự trữ nhất định. Khi số lượng chủ đề tăng lên, số lượng bộ nhớ cần thiết chỉ cho các chủ đề tồn tại. Vì bộ nhớ là hữu hạn, điều đó có nghĩa là có giới hạn về số lượng luồng có thể được tạo bất kỳ lúc nào.


Thí dụ

Trong trường hợp máy chủ web, giả vờ mỗi yêu cầu được cung cấp chủ đề rất riêng của nó. Cần có 1 MB bộ nhớ cho mỗi luồng và máy chủ web có 2GB RAM. Máy chủ web này sẽ có khả năng xử lý (khoảng) 2000 yêu cầu tại bất kỳ thời điểm nào trước khi không còn đủ bộ nhớ để xử lý nữa.

Nếu tải của bạn cao hơn đáng kể so với mức này, các yêu cầu sẽ mất một thời gian rất dài (khi chờ yêu cầu cũ hoàn thành) hoặc bạn sẽ phải ném thêm máy chủ vào cụm để mở rộng số lượng yêu cầu đồng thời có thể .


Đồng thời đa luồng

Thay vào đó, nhiều luồng đồng thời phụ thuộc vào việc thực thi một số tác vụ cùng một lúc. Điều đó có nghĩa là nếu một luồng bị chặn chờ cuộc gọi cơ sở dữ liệu để trả về, các yêu cầu khác có thể được xử lý cùng một lúc. Việc sử dụng luồng thấp hơn, nhưng số lượng luồng thực thi lớn hơn nhiều.

Mã đa luồng cũng khó hơn nhiều để lý do. Có vấn đề với khóa, đồng bộ hóa và các vấn đề tương tranh thú vị khác. Sự không đồng bộ đơn luồng không gặp phải vấn đề tương tự.

Tuy nhiên, mã đa luồng có hiệu năng cao hơn nhiều đối với các tác vụ chuyên sâu của CPU . Nếu không tồn tại cơ hội nào để tạo ra một luồng đối với năng suất, thì đó là một cuộc gọi mạng thường sẽ chặn một mô hình một chuỗi đơn giản, sẽ không có bất kỳ sự tương tranh nào.

Cả hai cùng tồn tại

Tất nhiên có sự chồng chéo giữa hai người; Họ không phải là loại trừ lẫn nhau. Ví dụ, mã đa luồng có thể được viết theo cách không chặn, để sử dụng tốt hơn từng luồng.


Điểm mấu chốt

Có nhiều vấn đề khác để xem xét, nhưng tôi thích nghĩ về hai vấn đề như thế này:

  • Nếu chương trình của bạn bị ràng buộc I / O , thì tính không đồng bộ của một luồng có thể sẽ hoạt động khá tốt.
  • Nếu chương trình của bạn bị ràng buộc CPU , thì một hệ thống đa luồng có thể sẽ là tốt nhất.

Trong trường hợp cụ thể của bạn, bạn cần xác định loại công việc không đồng bộ nào đang được hoàn thành và tần suất các nhiệm vụ đó phát sinh.

  • Họ có xảy ra trên mọi yêu cầu? Nếu vậy, bộ nhớ có thể sẽ trở thành một vấn đề khi số lượng yêu cầu tăng lên.
  • Là những nhiệm vụ được ra lệnh? Nếu vậy, bạn sẽ phải xem xét đồng bộ hóa nếu sử dụng nhiều luồng.
  • Là những nhiệm vụ CPU chuyên sâu? Nếu vậy, một luồng đơn có thể theo kịp tải không?

Không có câu trả lời đơn giản. Bạn phải xem xét trường hợp sử dụng của bạn là gì, và thiết kế phù hợp. Đôi khi một mô hình đơn luồng không đồng bộ là tốt hơn. Những lần khác, sử dụng một số luồng để đạt được xử lý song song lớn là bắt buộc.

Những ý kiến ​​khác

Có những vấn đề khác bạn cũng cần xem xét, thay vì chỉ mô hình tương tranh bạn chọn. Bạn có biết Erlang hoặc Clojure? Bạn có nghĩ rằng bạn có khả năng viết mã đa luồng an toàn bằng một trong những ngôn ngữ này để bạn cải thiện hiệu suất của ứng dụng của mình không? Sẽ mất nhiều thời gian để đạt được tốc độ trong một trong những ngôn ngữ này, và liệu ngôn ngữ bạn học có mang lại lợi ích cho bạn trong tương lai không?

Làm thế nào về những khó khăn liên quan đến giao tiếp giữa hai hệ thống này? Nó sẽ quá phức tạp để duy trì song song hai hệ thống riêng biệt? Hệ thống Erlang sẽ nhận các nhiệm vụ từ Django như thế nào? Erlang sẽ truyền đạt những kết quả đó trở lại Django như thế nào? Là hiệu suất đủ đáng kể một vấn đề mà sự phức tạp thêm vào là đáng giá?


Suy nghĩ cuối cùng

Tôi luôn thấy Django đủ nhanh và nó được sử dụng bởi một số trang web buôn bán rất nặng. Có một số tối ưu hóa hiệu suất bạn có thể thực hiện để tăng số lượng yêu cầu đồng thời và thời gian phản hồi. Phải thừa nhận rằng cho đến nay, tôi chưa làm gì với Celery, vì vậy việc tối ưu hóa hiệu suất thông thường có lẽ sẽ không giải quyết được bất kỳ vấn đề nào bạn có thể gặp phải với các tác vụ không đồng bộ này.

Tất nhiên, luôn có đề xuất ném thêm phần cứng vào vấn đề. Là chi phí cung cấp một máy chủ mới rẻ hơn chi phí phát triển và bảo trì của một hệ thống con hoàn toàn mới?

Tôi đã hỏi quá nhiều câu hỏi vào thời điểm này, nhưng đó là ý định của tôi. Câu trả lời sẽ không dễ dàng nếu không phân tích và biết thêm chi tiết. Có thể phân tích các vấn đề bắt nguồn từ việc biết các câu hỏi để hỏi, mặc dù vậy, rất hy vọng tôi đã giúp đỡ trên mặt trận đó.

Cảm giác ruột của tôi nói rằng viết lại bằng ngôn ngữ khác là không cần thiết. Sự phức tạp và chi phí có lẽ sẽ quá lớn.


Chỉnh sửa

Trả lời theo dõi

Theo dõi của bạn trình bày một số trường hợp sử dụng rất thú vị.


1. Django làm việc bên ngoài các yêu cầu HTTP

Ví dụ đầu tiên của bạn liên quan đến việc đọc thẻ NFC, sau đó truy vấn cơ sở dữ liệu. Tôi không nghĩ rằng việc viết phần này bằng ngôn ngữ khác sẽ hữu ích cho bạn, đơn giản vì việc truy vấn cơ sở dữ liệu hoặc máy chủ LDAP sẽ bị ràng buộc bởi I / O mạng (và có khả năng thực hiện cơ sở dữ liệu). Mặt khác, số lượng yêu cầu đồng thời sẽ bị ràng buộc bởi chính máy chủ, vì mỗi lệnh quản lý sẽ được chạy như một quy trình riêng của nó. Sẽ có thời gian thiết lập và phân tích ảnh hưởng đến hiệu suất, vì bạn không gửi tin nhắn đến một quy trình đã chạy. Tuy nhiên, bạn sẽ có thể gửi nhiều yêu cầu cùng một lúc, vì mỗi yêu cầu sẽ là một quy trình riêng biệt.

Trong trường hợp này, tôi thấy hai con đường bạn có thể điều tra:

  1. Đảm bảo rằng cơ sở dữ liệu của bạn có khả năng xử lý nhiều truy vấn cùng một lúc với nhóm kết nối. (Ví dụ, Oracle yêu cầu bạn định cấu hình Django phù hợp 'OPTIONS': {'threaded':True}.) Có thể có các tùy chọn cấu hình tương tự ở cấp cơ sở dữ liệu hoặc cấp Django mà bạn có thể điều chỉnh cho cơ sở dữ liệu của riêng mình. Bất kể ngôn ngữ nào bạn viết các truy vấn cơ sở dữ liệu của mình, bạn sẽ phải đợi dữ liệu này trở lại trước khi bạn có thể bật đèn LED. Hiệu năng của mã truy vấn có thể tạo ra sự khác biệt và Django ORM không nhanh như chớp ( nhưng , thường đủ nhanh).
  2. Giảm thiểu thời gian thiết lập / phá hỏng. Có một quá trình liên tục chạy, và gửi tin nhắn đến nó. (Sửa lỗi cho tôi nếu tôi sai, nhưng đây là câu hỏi ban đầu của bạn thực sự tập trung vào.) Liệu quy trình này được viết bằng Python / Django hay ngôn ngữ / khung khác được trình bày ở trên. Tôi không thích ý tưởng sử dụng các lệnh quản lý thường xuyên như vậy. Có thể có một đoạn mã nhỏ chạy liên tục, đẩy các tin nhắn từ đầu đọc NFC vào hàng đợi tin nhắn, sau đó Celery đọc và chuyển tiếp tới Django? Việc thiết lập và phân tích một chương trình nhỏ, ngay cả khi nó được viết bằng Python (chứ không phải Django!), Tốt hơn là bắt đầu và dừng chương trình Django (với tất cả các hệ thống con của nó).

Tôi không chắc máy chủ web nào bạn đang sử dụng cho Django. mod_wsgicho Apache cho phép bạn định cấu hình số lượng quy trình và luồng trong các quy trình mà dịch vụ yêu cầu. Hãy chắc chắn điều chỉnh cấu hình có liên quan của máy chủ web của bạn để tối ưu hóa số lượng yêu cầu có thể dịch vụ.


2. Truyền tin nhắn qua mạng với tín hiệu Django

Trường hợp sử dụng thứ hai của bạn cũng khá thú vị; Tôi không chắc mình có câu trả lời cho điều đó không. Nếu bạn đang xóa các phiên bản mô hình và muốn hoạt động trên chúng sau này, có thể tuần tự hóa chúng JSON.dumpsvà sau đó giải tuần tự hóa JSON.loads. Sau này không thể tạo lại hoàn toàn biểu đồ đối tượng (truy vấn các mô hình liên quan), vì các trường liên quan được tải lười biếng từ cơ sở dữ liệu và liên kết đó sẽ không còn tồn tại.

Tùy chọn khác là bằng cách nào đó đánh dấu một đối tượng để xóa và chỉ xóa nó ở cuối chu kỳ yêu cầu / phản hồi (sau khi tất cả các tín hiệu đã được phục vụ). Nó có thể yêu cầu một tín hiệu tùy chỉnh để thực hiện điều này, thay vì dựa vào post_delete.


1
Rất nhiều FUD và nghi ngờ về việc khóa và những thứ khác không phải là vấn đề với Erlang, không có vấn đề trạng thái chia sẻ truyền thống nào mà bạn liệt kê là những cân nhắc với ngôn ngữ và thời gian chạy được thiết kế riêng để không chia sẻ trạng thái. Erlang có thể xử lý hàng chục ngàn quy trình kín đáo trong rất ít ram, áp lực bộ nhớ cũng không phải là vấn đề.

@Jarrod, cá nhân tôi không biết Erlang nên tôi sẽ chấp nhận những gì bạn nói về vấn đề đó. Mặt khác, gần như mọi thứ khác tôi đề cập đều có liên quan. Chi phí, độ phức tạp và liệu các công cụ hiện tại có được sử dụng đúng cách hay không.
Josh Smeaton


Đây là loại câu trả lời sử thi mà tôi thực sự thích đọc ^^. +1, làm tốt lắm!
Laurent Bourgault-Roy

Ngoài ra nếu bạn có các mẫu DJango, chúng có thể được sử dụng trong erlang với Erlydtl
Zachary K

8

Tôi đã thực hiện một số phát triển có khả năng mở rộng rất tinh vi cho một ISP lớn của Hoa Kỳ . Chúng tôi đã thực hiện một số con số nghiêm trọng khi sử dụng máy chủ Twisted và đó là một cơn ác mộng phức tạp khi khiến Python / Twisted mở rộng quy mô trên bất cứ thứ gì bị ràng buộc bởi CPU . Giới hạn I / O không phải là vấn đề, nhưng CPU bị ràng buộc là không thể. Chúng tôi có thể nhanh chóng kết hợp các hệ thống, nhưng việc mở rộng chúng cho hàng triệu người dùng đồng thời là một cơn ác mộng về cấu hình và độ phức tạp nếu bị ràng buộc bởi CPU.

Tôi đã viết một bài đăng trên blog về nó, Python / Twisted VS Erlang / OTP .

TLDR; Erlang đã thắng.


4

Các vấn đề thực tế với Twisted (mà tôi yêu thích và đã sử dụng khoảng năm năm):

  1. Các tài liệu để lại một cái gì đó được mong muốn, và mô hình là khá phức tạp để tìm hiểu dù sao. Tôi thấy thật khó để khiến các lập trình viên Python khác làm việc với mã Twisted.
  2. Tôi đã kết thúc bằng cách sử dụng chặn I / O và truy cập cơ sở dữ liệu vì thiếu API chặn tốt. Điều này thực sự có thể làm tổn thương hiệu suất.
  3. Dường như không có một cộng đồng lớn và cộng đồng lành mạnh sử dụng Twisted; chẳng hạn, Node.js có sự phát triển tích cực hơn nhiều, đặc biệt là cho lập trình back-end web.
  4. Nó vẫn là Python và ít nhất CPython không phải là thứ nhanh nhất xung quanh.

Tôi đã thực hiện một chút công việc bằng cách sử dụng Node.js với CoffeeScript và nếu hiệu suất đồng thời là mối quan tâm của bạn thì đó có thể là bước nhảy vọt.

Bạn đã xem xét việc chạy nhiều phiên bản của Django với một số sắp xếp để phân tán khách hàng giữa các phiên bản chưa?


1
Tài liệu Python trong lá nói chung cái gì đó để được mong muốn: / (Không nói nó xấu, nhưng đối với một ngôn ngữ mà phổ biến người ta sẽ mong đợi cho nó được tốt hơn rất nhiều).
Rook

3
Tôi thấy tài liệu Python và tài liệu Django cụ thể là một trong những tài liệu tốt nhất cho bất kỳ ngôn ngữ nào. Nhiều thư viện của bên thứ ba để lại một cái gì đó được mong muốn mặc dù.
Josh Smeaton

1

Tôi sẽ đề xuất những điều sau đây trước khi bạn xem xét chuyển sang ngôn ngữ khác.

  1. Sử dụng LTTng để ghi lại các sự kiện hệ thống như lỗi trang, chuyển đổi ngữ cảnh và chờ cuộc gọi hệ thống.
  2. Chuyển đổi bất cứ nơi nào mất quá nhiều thời gian để sử dụng thư viện C và sử dụng bất kỳ mẫu thiết kế nào bạn thích (đa luồng, dựa trên sự kiện tín hiệu, gọi lại async hoặc Unix truyền thống select) phù hợp với I / O trong đó.

Tôi sẽ không sử dụng luồng trong Python khi ứng dụng được ưu tiên về hiệu năng. Tôi sẽ chọn tùy chọn trên, có thể giải quyết nhiều vấn đề như tái sử dụng phần mềm, kết nối với Django , hiệu suất, dễ phát triển, v.v.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.