2011-12-31 39 views
25

Gần đây tôi đã bắt đầu thử nghiệm bằng cách sử dụng Python để phát triển web. Cho đến nay tôi đã có một số thành công bằng cách sử dụng Apache với mod_wsgi và khung web Django cho Python 2.7. Tuy nhiên tôi đã gặp phải một số vấn đề với các quy trình liên tục chạy, cập nhật thông tin và như vậy.Làm cách nào để chạy các quy trình Python dài hạn (vô hạn)?

Tôi đã viết một tập lệnh mà tôi gọi là "daemonManager.py" có thể bắt đầu và ngừng tất cả hoặc các vòng cập nhật python riêng lẻ (Tôi có nên gọi chúng là Daemons không?). Nó làm điều đó bằng cách forking, sau đó tải các mô-đun cho các chức năng cụ thể nó nên chạy và bắt đầu một vòng lặp vô hạn. Nó lưu một tập tin PID trong /var/run để theo dõi quá trình. Càng xa càng tốt. Các vấn đề tôi gặp phải là:

  • Bây giờ, sau đó một trong các quy trình sẽ thoát. Tôi kiểm tra ps vào buổi sáng và quá trình này đã biến mất. Không có lỗi nào được ghi lại (Tôi đang sử dụng mô-đun logging) và tôi bao gồm mọi ngoại lệ mà tôi có thể nghĩ đến và ghi lại chúng. Ngoài ra tôi không nghĩ rằng các quy trình bỏ này có liên quan đến mã của tôi, bởi vì tất cả các quy trình của tôi chạy mã hoàn toàn khác nhau và thoát ra ở các khoảng thời gian tương tự. Tôi có thể sai, tất nhiên. Nó có bình thường đối với các quy trình Python chỉ chết sau khi chúng chạy trong ngày/tuần không? Tôi nên giải quyết vấn đề này như thế nào? Tôi có nên viết một daemon khác định kỳ kiểm tra xem các daemon khác vẫn đang chạy không? Nếu daemon đó dừng lại thì sao? Tôi đang thua lỗ về cách xử lý việc này.

  • Làm cách nào tôi có thể biết lập trình nếu quá trình vẫn đang chạy hay không? Tôi đang lưu các tập tin PID trong /var/run và kiểm tra xem tệp PID có ở đó để xác định liệu quá trình có đang chạy hay không. Nhưng nếu quá trình này chỉ chết vì các nguyên nhân không mong muốn, tệp PID sẽ vẫn còn. Do đó, tôi phải xóa những tập tin này mỗi khi một quá trình bị treo (một vài lần mỗi tuần), mà loại thất bại mục đích. Tôi đoán tôi có thể kiểm tra nếu một quá trình đang chạy tại PID trong tập tin, nhưng nếu một quá trình khác đã bắt đầu và được chỉ định PID của quá trình chết? Daemon của tôi sẽ nghĩ rằng quá trình này đang chạy tốt ngay cả khi nó chết lâu. Một lần nữa tôi thua lỗ chỉ cách giải quyết vấn đề này.

Bất kỳ câu trả lời hữu ích về cách quá trình Python vô hạn chạy tốt nhất, hy vọng cũng hé lộ ánh sáng về những vấn đề trên, tôi sẽ chấp nhận


Tôi đang sử dụng Apache 2.2.14 trên một máy Ubuntu.
Phiên bản Python của tôi là 2.7.2

+0

Nếu bạn thêm một số mẫu mã hiển thị mã cho các daemon đang gặp sự cố, chúng tôi có thể giải quyết các chi tiết cụ thể. Mặc dù vậy, trước hết, tôi sẽ xóa tất cả mã khỏi các tập lệnh của bạn xử lý việc tìm kiếm, giám sát, chuyển hướng, v.v. –

+0

Bạn có thể làm rõ liệu bạn đang sử dụng các quy trình daemon này từ ứng dụng WSGI đang chạy dưới mod_wsgi hay riêng rẽ. Bạn không nên làm việc tạo quy trình như vậy từ một ứng dụng đang chạy dưới mod_wsgi. –

+0

Âm thanh như rất nhiều công khai đang diễn ra ở đây. Ý tôi là, đây là một câu hỏi được đặt đúng, trên đó một câu trả lời được đưa ra cho một công nghệ cụ thể, mà một câu trả lời khác được đưa ra trong đó nó được trả lời một lần nữa "Tôi cũng đã kết thúc bằng cách sử dụng" một công nghệ khác (cạnh tranh?) ... – citn

Trả lời

24

Tôi sẽ mở bằng cách cho biết đây là một cách quản lý quy trình chạy dài (LRP) - không thực tế bằng bất kỳ khoảng thời gian nào.

Theo kinh nghiệm của tôi, sản phẩm tốt nhất có thể đến từ việc tập trung vào vấn đề cụ thể mà bạn đang xử lý, trong khi ủy thác công nghệ hỗ trợ cho các thư viện khác. Trong trường hợp này, tôi đề cập đến hành động của các quá trình nền (nghệ thuật của ngã ba đôi), theo dõi và chuyển hướng nhật ký.

giải pháp ưa thích của tôi là http://supervisord.org/

Sử dụng một hệ thống như supervisord, về cơ bản bạn viết một kịch bản python thông thường mà thực hiện một nhiệm vụ khi bị mắc kẹt trong một vòng lặp "vô hạn".

#!/usr/bin/python 

import sys 
import time 

def main_loop(): 
    while 1: 
     # do your stuff... 
     time.sleep(0.1) 

if __name__ == '__main__': 
    try: 
     main_loop() 
    except KeyboardInterrupt: 
     print >> sys.stderr, '\nExiting by user request.\n' 
     sys.exit(0) 

Viết kịch bản của bạn theo cách này làm cho nó đơn giản và thuận tiện để phát triển và gỡ lỗi (bạn có thể dễ dàng bắt đầu/dừng nó trong một thiết bị đầu cuối, xem các dữ liệu ghi nhận như các sự kiện diễn ra). Khi đến lúc đưa vào sản xuất, bạn chỉ cần xác định cấu hình giám sát gọi tập lệnh của bạn (đây là ví dụ đầy đủ để xác định "chương trình", phần lớn là tùy chọn: http://supervisord.org/configuration.html#program-x-section-example).

Supervisor có một bó tùy chọn cấu hình vì vậy tôi sẽ không liệt kê chúng, nhưng tôi sẽ nói rằng nó đặc biệt giải quyết những vấn đề mà bạn mô tả:

  • Việc background/daemonizing
  • PID theo dõi (có thể được cấu hình để khởi động lại quy trình nếu nó chấm dứt đột ngột)
  • Đăng nhập bình thường trong tập lệnh của bạn (trình xử lý luồng nếu sử dụng mô-đun ghi nhật ký thay vì in) nhưng hãy để người giám sát chuyển hướng đến tệp cho bạn.
+0

Tôi đã kết thúc việc bán giải pháp daemonize của riêng mình vì hóa ra tôi không có đủ kinh nghiệm về chủ đề này. Tôi cũng đã kết thúc bằng cách sử dụng [forever by nodejitsu] (https://github.com/nodejitsu/forever) là một ứng dụng không cần cấu hình (nhưng có thể cấu hình có thể) mà bạn chỉ cần chỉ định thực thi và các đối số, và tập lệnh sẽ chạy dưới dạng daemon cho đến giờ, khởi động lại khi gặp sự cố. Tôi cũng đã giải quyết một số lỗi chạy dài bằng cách kiểm tra các bản ghi đầu ra tự động. Tôi chấp nhận câu trả lời của bạn là câu trả lời gần nhất với giải pháp của tôi – Hubro

+0

Giám sát viên có khởi động lại tập lệnh của bạn sau khi bạn thoát khỏi tập lệnh của mình theo cách thủ công không? –

+1

@Jakobud khi người giám sát đang quản lý một quá trình thoát (thông qua 'sys.exit()', một ngoại lệ không bị bắt, hoặc nếu kịch bản khác đạt đến kết thúc - có thể không có vòng lặp?), Nó sẽ cố gắng khởi động lại nó. Có các cài đặt để kiểm soát số lần khởi động lại và thời gian chờ đợi giữa các lần thử. Một khi tất cả các nỗ lực đã được chi tiêu, nó sẽ bỏ cuộc. Nếu bạn muốn dừng công việc đang chạy, bạn nên sử dụng supervisorctl để tắt nó. –

2

Tôi cho rằng bạn đang chạy Unix/Linux nhưng bạn không thực sự nói. Tôi không có lời khuyên trực tiếp nào về vấn đề của bạn. Vì vậy, tôi không mong đợi là câu trả lời "đúng" cho câu hỏi này. Nhưng có điều gì đó để khám phá ở đây.

Trước tiên, nếu daemon của bạn bị lỗi, bạn nên khắc phục điều đó. Chỉ các chương trình có lỗi mới bị lỗi. Có lẽ bạn nên khởi chạy chúng dưới một trình gỡ lỗi và xem điều gì xảy ra khi chúng sụp đổ (nếu có thể). Bạn có bất kỳ dấu vết đăng nhập trong các quá trình này? Nếu không, hãy thêm chúng. Điều đó có thể giúp chẩn đoán sự cố của bạn.

Thứ hai, các trình nền của bạn có cung cấp dịch vụ (mở đường ống và chờ yêu cầu) hay đang thực hiện dọn dẹp định kỳ? Nếu chúng là các quá trình dọn dẹp định kỳ, bạn nên sử dụng cron để khởi động chúng định kỳ thay vì sau đó có chúng chạy trong một vòng lặp vô hạn. Các quy trình Cron nên được ưu tiên hơn các quy trình daemon. Tương tự, nếu chúng là các dịch vụ mở cổng và các yêu cầu dịch vụ, bạn có nghĩ rằng chúng làm việc với INETD không? Một lần nữa, một daemon đơn (inetd) nên được ưu tiên cho một loạt các quy trình daemon.

Thứ ba, lưu PID trong tệp không hiệu quả lắm, như bạn đã khám phá. Có lẽ một IPC được chia sẻ, giống như một semaphore, sẽ hoạt động tốt hơn. Tôi không có bất kỳ chi tiết ở đây mặc dù.

Thứ tư, đôi khi tôi cần nội dung để chạy trong ngữ cảnh của trang web. Tôi sử dụng một quá trình cron gọi wget với một URL bảo trì. Bạn đặt một cookie đặc biệt và bao gồm thông tin cookie với dòng lệnh wget. Nếu cookie đặc biệt không tồn tại, trả lại 403 thay vì thực hiện quá trình bảo trì. Lợi ích khác ở đây là đăng nhập vào cơ sở dữ liệu và các vấn đề môi trường khác tránh được vì mã phục vụ các trang web bình thường đang phục vụ cho quá trình bảo trì.

Hy vọng cung cấp cho bạn các ý tưởng. Tôi nghĩ rằng tránh daemon nếu bạn có thể là nơi tốt nhất để bắt đầu. Nếu bạn có thể chạy python của bạn trong mod_wsgi để tiết kiệm cho bạn phải hỗ trợ nhiều "môi trường". Gỡ lỗi một quá trình thất bại sau khi chạy trong nhiều ngày tại một thời điểm chỉ là tàn bạo.

+1

Cảm ơn lời khuyên tốt đẹp. Tôi chỉ định rằng tôi đang chạy Ubuntu bằng cách này :) – Hubro

+0

Oh, ở cuối. Không nhìn thấy nó. – jmucchiello

2

Bạn nên xem xét các quy trình Python có thể chạy "mãi mãi" giả sử bạn không có bất kỳ rò rỉ bộ nhớ nào trong chương trình, trình thông dịch Python hoặc bất kỳ thư viện/mô-đun Python nào bạn đang sử dụng. (Ngay cả khi đối mặt với rò rỉ bộ nhớ, bạn có thể chạy mãi mãi nếu bạn có đủ không gian hoán đổi trên máy tính 64 bit. Nhiều thập kỷ, nếu không phải hàng thế kỷ, sẽ có thể thực hiện được. hai năm trên phần cứng hạn chế - trước khi phần cứng cần được di chuyển.)

chương trình Đảm bảo khởi động lại khi họ chết từng là rất đơn giản trở lại khi các bản phân phối Linux được sử dụng SysV-style init - bạn chỉ cần thêm một dòng mới vào /etc/inittabinit(8) sẽ đẻ trứng chương trình của bạn lúc khởi động và tái spawn nó nếu nó chết. (Tôi biết không có cơ chế nào để nhân rộng chức năng này với upstartinit -trọng lượng mới mà nhiều bản phân phối đang sử dụng những ngày này. Tôi không nói điều đó là không thể, tôi chỉ không biết cách thực hiện.)

Nhưng ngay cả cơ chế init(8) của năm trôi qua cũng không linh hoạt như một số người đã thích. Gói daemontools của DJB là một ví dụ về các công cụ kiểm soát và giám sát quy trình nhằm giữ cho daemon sống mãi mãi. Bộ Linux-HA cung cấp một công cụ tương tự khác, mặc dù nó có thể cung cấp quá nhiều chức năng "phụ" để được hợp lý cho tác vụ này. monit là một tùy chọn khác.

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