2010-08-18 29 views
9

Xem xét ứng dụng web PHP có mục đích là chấp nhận yêu cầu của người dùng để bắt đầu công việc không đồng bộ chung và sau đó tạo quy trình/chuỗi công nhân để chạy công việc. Các công việc không đặc biệt là CPU hoặc bộ nhớ chuyên sâu, nhưng dự kiến ​​sẽ chặn trên các cuộc gọi I/O khá thường xuyên. Không nên khởi động nhiều hơn một hoặc hai lần mỗi giây, nhưng do thời gian chạy dài nên có thể có nhiều công việc đang chạy cùng một lúc.xử lý không đồng bộ với PHP - một công nhân cho mỗi công việc

Do đó, điều quan trọng nhất là các công việc chạy song song. Ngoài ra, mỗi công việc phải được giám sát bởi người quản lý có trách nhiệm giết người lao động, hủy bỏ công nhân theo yêu cầu của người dùng, v.v.

Cách tốt nhất để thực hiện một hệ thống như thế này là gì? Tôi có thể thấy:

  1. Đưa công nhân ra khỏi trình quản lý - đây dường như là tùy chọn cấp thấp nhất và tôi phải tự mình thực hiện hệ thống giám sát. Apache là máy chủ web, do đó, nó xuất hiện rằng tùy chọn này sẽ yêu cầu bất kỳ công nhân PHP nào được bắt đầu thông qua FastCGI.
  2. Sử dụng một số loại hàng đợi công việc/tin nhắn. (gearman, beanstalkd, RabbitMQ, vv) - Ban đầu, điều này dường như là sự lựa chọn hiển nhiên. Sau một số nghiên cứu, tôi hơi bối rối với tất cả các tùy chọn. Ví dụ, Gearman trông giống như nó được thiết kế cho các hệ thống phân tán lớn, nơi có một nhóm công nhân cố định ... vì vậy tôi không biết liệu nó có phù hợp với những gì tôi cần (một công nhân cho mỗi công việc) hay không.

Trả lời

8

Vâng, nếu bạn sử dụng Linux, bạn có thể sử dụng pcntl_fork để chia nhỏ trẻ em. Các "bậc thầy" sau đó xem các em. Mỗi đứa trẻ hoàn thành nhiệm vụ của nó và sau đó tồn tại bình thường.

Cá nhân, trong việc triển khai của tôi, tôi chưa bao giờ cần hàng đợi thư. Tôi đơn giản sử dụng một mảng trong "master" với các khóa. Khi một đứa trẻ có một công việc, nó sẽ viết một tập tin khóa với số id công việc. Thầy sẽ đợi cho đến khi đứa trẻ đó ra đi. Nếu tập tin khóa vẫn còn tồn tại sau khi đứa trẻ đã thoát, sau đó tôi biết nhiệm vụ không được hoàn thành, và khởi động lại một đứa trẻ với cùng một công việc (sau khi gỡ bỏ tập tin khóa). Tùy thuộc vào tình huống của bạn, bạn có thể thực hiện hàng đợi trong một bảng cơ sở dữ liệu đơn giản. Chèn công việc vào bảng và kiểm tra bảng trong tổng thể sau mỗi 30 hoặc 60 giây cho công việc mới. Sau đó, chỉ xóa chúng khỏi bảng sau khi trẻ hoàn thành (và đứa trẻ đã xóa tệp khóa). Điều này sẽ có vấn đề nếu bạn có nhiều hơn một "master" chạy cùng một lúc, nhưng bạn có thể thực hiện một "tập tin pid tổng thể" toàn cầu để phát hiện và ngăn chặn nhiều trường hợp ...

Và tôi sẽ không đề nghị sử dụng FastCGI . Nó có thể dẫn đến một số vấn đề rất mơ hồ vì môi trường có nghĩa là tồn tại. Thay vào đó, sử dụng CGI nếu bạn phải có giao diện web, nhưng lý tưởng nhất là sử dụng một ứng dụng CLI (một deamon). Để giao tiếp với tổng thể từ các quy trình khác, bạn có thể sử dụng ổ cắm cho giao tiếp TCP hoặc tạo để liên lạc.

Đối với việc phát hiện công nhân bị treo, bạn có thể triển khai hệ thống "nhịp tim", trong đó đứa trẻ phát hành SIG_USR1 cho quy trình tổng thể sau mỗi giây. Sau đó, nếu bạn không nghe từ đứa trẻ trong hai hoặc ba lần thời gian đó, nó có thể bị treo. Nhưng vấn đề là vì PHP không phải là đa luồng, bạn không thể biết liệu một đứa trẻ có bị treo hay không nếu nó chỉ đang chờ một tài nguyên chặn (giống như một cuộc gọi cơ sở dữ liệu) ... Để thực hiện "nhịp tim" , bạn có thể sử dụng một số tick function để tự động hóa nhịp tim (nhưng hãy ghi nhớ, việc chặn cuộc gọi vẫn không thực hiện được) ...

+0

Cảm ơn. Tôi đã làm điều này một vài lần bây giờ, và nó hoạt động thực sự tốt. Vâng, tôi nên nói nó hoạt động thực sự tốt nếu trường hợp sử dụng của bạn được liên kết với những hạn chế của hệ thống (IPC là khá tốn kém, vv). Nếu chúng không được liên kết rất tốt, bạn nên sử dụng thực thi luồng đúng và một ngôn ngữ khác với PHP ... – ircmaxell

+2

Hãy cẩn thận với 'pcntl_fork()'. Tôi đã có vấn đề với các kết nối cơ sở dữ liệu được chia sẻ theo những cách kỳ lạ giữa các tiến trình cha và con. Tôi sẽ không ngạc nhiên nếu một số phần mở rộng PECL chia sẻ những điều kỳ quặc tương tự. Tôi e ngại bỏ qua PHP và tạo ra các tiến trình riêng biệt thông qua 'exec()' và tương tự, chỉ để giữ cho mọi thứ đơn giản là –

+0

Vâng, tôi rõ ràng mở lại tất cả các kết nối trong đứa trẻ sau khi forking vì lý do đó. Ngã ba là không có gì phải sợ (tôi sử dụng nó khá thường xuyên). Nhưng đó là rất nhiều thử và sai vì không có nhiều tài liệu về chủ đề này. Vấn đề với việc thực hiện thông qua 'exec', nó làm cho việc giao tiếp và giám sát trở nên khó khăn hơn (vì một lệnh' exec' đang chặn, và hai khó khăn hơn để có được quá trình id của một cuộc gọi 'exec' không chặn (một cuộc gọi) với một '&' được nối vào cuối)) ... – ircmaxell

1

trong khi bạn làm chạy không đồng bộ một nhiệm vụ với nhiều công việc với pcntl_fork hoặc bạn sẽ tạo ra truy vấn kiên trì mỗi (s) giây, hãy cẩn thận với mức tiêu thụ CPU cao, bạn có thể bị treo bộ nhớ xử lý vì không thể cấp phát lại bộ nhớ, tôi nghĩ lựa chọn tốt nhất bạn có thể xây dựng hoàn toàn với Gearman hoặc bạn có thể thử với nhân viên đám mây như IronWorker.

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