2010-05-29 32 views
10

Tôi muốn ngăn chặn nhiều phiên bản của tập lệnh dòng lệnh python chạy dài cùng lúc chạy cùng một lúc và tôi muốn cá thể mới có thể gửi dữ liệu đến phiên bản gốc trước phiên bản mới tử tự. Làm thế nào tôi có thể làm điều này một cách đa nền tảng?có thể là một tập lệnh python biết rằng một thể hiện khác của cùng một tập lệnh đang chạy ... và sau đó nói chuyện với nó?

Cụ thể, tôi muốn để cho phép các hành vi sau đây:

  1. "foo.py" được khởi động từ dòng lệnh, và nó sẽ ở lại chạy trong một ngày time-- dài hoặc vài tuần cho đến khi máy đang khởi động lại hoặc quá trình cha mẹ giết chết nó.
  2. cứ sau vài phút cùng một tập lệnh được khởi chạy lại, nhưng với các tham số dòng lệnh khác nhau
  3. khi khởi chạy, tập lệnh sẽ xem có bất kỳ phiên bản nào khác đang chạy hay không.
  4. nếu các phiên bản khác đang chạy, sau đó, cá thể số 2 sẽ gửi các tham số dòng lệnh của nó tới ví dụ # 1 và sau đó phiên bản # 2 sẽ thoát.
  5. Ví dụ # 1, nếu nó nhận các tham số dòng lệnh từ tập lệnh khác, nên xoay vòng một chuỗi mới và (sử dụng các tham số dòng lệnh được gửi ở bước trên) bắt đầu thực hiện tác vụ mà ví dụ # 2 sẽ thực hiện .

Vì vậy, tôi đang tìm hai điều: làm thế nào một chương trình python biết một thể hiện khác đang chạy, và sau đó làm thế nào một chương trình dòng lệnh có thể giao tiếp với nhau?

Làm việc này phức tạp hơn, cùng một tập lệnh cần chạy trên cả Windows và Linux, vì vậy lý tưởng giải pháp sẽ chỉ sử dụng thư viện chuẩn của Python chứ không phải bất kỳ cuộc gọi cụ thể cho hệ điều hành nào. Mặc dù nếu tôi cần phải có một codepath Windows và một codepath * nix (và một tuyên bố lớn if trong mã của tôi để chọn một hoặc khác), đó là OK nếu một "cùng một mã" giải pháp là không thể.

Tôi nhận ra mình có thể làm việc theo cách tiếp cận dựa trên tệp (ví dụ: # 1 xem thư mục để thay đổi và mỗi trường hợp thả tệp vào thư mục đó khi muốn làm việc) nhưng tôi hơi lo ngại về dọn dẹp các tệp đó sau khi tắt máy không duyên dáng. Tôi lý tưởng có thể sử dụng một giải pháp trong bộ nhớ. Nhưng một lần nữa tôi linh hoạt, nếu một phương pháp tiếp cận dựa trên tập tin liên tục là cách duy nhất để làm điều đó, tôi sẽ mở ra tùy chọn đó.

Thông tin chi tiết: Tôi đang cố gắng thực hiện điều này vì máy chủ của chúng tôi đang sử dụng công cụ giám sát hỗ trợ chạy tập lệnh python để thu thập dữ liệu giám sát (ví dụ: kết quả truy vấn cơ sở dữ liệu hoặc cuộc gọi dịch vụ web). sau đó sử dụng. Một số kịch bản này rất tốn kém để khởi động nhưng rẻ để chạy sau khi khởi động (ví dụ: tạo kết nối DB và chạy truy vấn). Vì vậy, chúng tôi đã chọn để giữ cho chúng chạy trong một vòng lặp vô hạn cho đến khi quá trình cha mẹ giết chúng.

Điều này làm việc tuyệt vời, nhưng trên các máy chủ lớn hơn 100 phiên bản của cùng một tập lệnh có thể đang chạy, ngay cả khi chúng chỉ thu thập dữ liệu sau mỗi 20 phút. Điều này tàn phá với RAM, giới hạn kết nối DB, vv Chúng tôi muốn chuyển từ 100 tiến trình với 1 chuỗi thành một tiến trình với 100 luồng, mỗi luồng thực thi công việc mà trước đó, một tập lệnh đang thực hiện.

Nhưng việc thay đổi cách các tập lệnh được gọi bởi công cụ giám sát là không thể. Chúng ta cần giữ lời gọi tương tự (khởi chạy một tiến trình với các tham số dòng lệnh khác nhau) nhưng thay đổi các kịch bản để nhận ra rằng một kịch bản khác đang hoạt động, và có kịch bản lệnh mới gửi các chỉ lệnh công việc của nó (từ các tham số dòng lệnh) thành tập lệnh "cũ".

BTW, đây không phải là điều tôi muốn thực hiện trên cơ sở một tập lệnh. Thay vào đó, tôi muốn đóng gói hành vi này vào một thư viện mà nhiều tác giả kịch bản có thể tận dụng-- mục tiêu của tôi là cho phép các tác giả kịch bản viết các kịch bản đơn luồng đơn giản, không biết các vấn đề đa cá thể và xử lý đa luồng và duy nhất-instancing dưới bìa.

+0

Tại sao bạn gắn bó với tập lệnh công nhân giống với tập lệnh gọi lệnh? Kịch bản công nhân có thể là một tiến trình máy chủ nhận các lệnh, được gửi bởi các máy khách chuyển tiếp lệnh, được khung theo dõi của bạn gọi, chỉ có một nhiệm vụ: báo cho máy chủ biết phải làm gì. – Bernd

Trả lời

9

Phương pháp thiết lập kênh truyền thông của Alex Martelli là phù hợp. Tôi sẽ sử dụng một multiprocessing.connection.Listener để tạo một người nghe, theo lựa chọn của bạn. Tài liệu tại: http://docs.python.org/library/multiprocessing.html#multiprocessing-listeners-clients

Thay vì sử dụng AF_INET (ổ cắm), bạn có thể chọn sử dụng AF_UNIX cho Linux và AF_PIPE cho Windows. Hy vọng rằng một "nếu" nhỏ sẽ không bị tổn thương.

Chỉnh sửa: Tôi đoán một ví dụ sẽ không gây tổn hại. Nó là một cơ bản, mặc dù.

#!/usr/bin/env python 

from multiprocessing.connection import Listener, Client 
import socket 
from array import array 
from sys import argv 

def myloop(address): 
    try: 
     listener = Listener(*address) 
     conn = listener.accept() 
     serve(conn) 
    except socket.error, e: 
     conn = Client(*address) 
     conn.send('this is a client') 
     conn.send('close') 

def serve(conn): 
    while True: 
     msg = conn.recv() 
     if msg.upper() == 'CLOSE': 
      break 
     print msg 
    conn.close() 

if __name__ == '__main__': 
    address = ('/tmp/testipc', 'AF_UNIX') 
    myloop(address) 

Tính năng này hoạt động trên OS X, vì vậy cần thử nghiệm với cả Linux và (sau khi thay thế đúng địa chỉ) Windows. Có rất nhiều cảnh báo tồn tại từ một điểm bảo mật, điểm chính là conn.recv bỏ ghim dữ liệu của nó, vì vậy bạn hầu như luôn luôn tốt hơn với recv_bytes.

+0

Câu trả lời hay! Có thể sử dụng một đường ống có tên (windows) hoặc fifo (unix), vì tôi có thể đặt tên cho pipe/fifo sau khi kịch bản sẽ là duy nhất, có vẻ dễ dàng hơn nhiều so với việc giữ bản đồ tại chỗ giữa các kịch bản và số cổng. –

1

Có thể thử sử dụng ổ cắm để liên lạc?

9

Cách tiếp cận chung là có tập lệnh, khi khởi động, thiết lập kênh giao tiếp theo cách được bảo đảm độc quyền (các nỗ lực khác để thiết lập cùng một kênh không theo cách dự đoán được) để các phiên bản tiếp theo của tập lệnh có thể phát hiện người đầu tiên đang chạy nói chuyện với nó.

Yêu cầu đối với chức năng đa nền tảng của bạn hướng tới việc sử dụng ổ cắm làm kênh giao tiếp được đề cập: bạn có thể chỉ định "cổng nổi tiếng" dành riêng cho tập lệnh của mình, nói 12345 và mở ổ cắm trên cổng đó chỉ cho localhost (127.0.0.1). Nếu nỗ lực mở ổ cắm đó không thành công, vì cổng được đề cập là "được lấy", thì bạn có thể kết nối với số cổng đó thay vào đó và điều đó sẽ cho phép bạn giao tiếp với tập lệnh hiện có.

Nếu bạn không quen với lập trình socket, có HOWTO doc here tốt. Bạn cũng có thể xem các chương liên quan trong Python in a Nutshell (Tôi thiên vị về điều đó, tất nhiên ;-).

+0

Xin chào Alex - cảm ơn bạn đã phản hồi nhanh! Mối quan tâm chính của tôi với cách tiếp cận cổng nổi tiếng là khả năng xung đột (chúng tôi không sở hữu máy chủ để các chương trình khác có thể sử dụng các cổng đó) và quản lý số cổng (vì chúng tôi sẽ áp dụng thủ thuật đơn lẻ) cho nhiều tập lệnh được duy trì bởi các tác giả kịch bản khác nhau). Có cách nào xung quanh các vấn đề trên, hoặc tôi sẽ tốt hơn với một cơ chế "có tên IPC"? Tôi nghi ngờ được đặt tên ống trên cửa sổ và ổ cắm miền trên * nix có thể làm điều này, nhưng tôi không biết làm thế nào dễ dàng họ sẽ được sử dụng từ Python. –

+0

@Justin, tôi không chắc chắn cách bạn sẽ sử dụng các cơ chế như ống được đặt tên và ổ cắm miền unix trong nền tảng đa nền tảng và "nội tại loại trừ lẫn nhau". Để hỗ trợ các yêu cầu cụ thể mà bạn xác định, bạn có thể ghi lại các kịch bản lệnh "cổng không được biết đến", kịch bản của tên X được sử dụng, bằng cách truy cập và cập nhật tệp lưu trữ '.dbm' (hoặc sqlite v.v.) vào cổng tương ứng (nếu một kịch bản khi bắt đầu không tìm thấy tên của nó ở đó, nó nhận được một cổng mới từ hệ điều hành và ghi lại nó), có lẽ với một số cơ chế khóa tập tin để tránh điều kiện chủng tộc. –

+0

Câu trả lời của @Muhammad Alkarouri dưới đây (sử dụng gói đa xử lý) có vẻ giống như một giải pháp đa nền tảng khả thi, đồng thời tránh sự phức tạp của các kịch bản ánh xạ tới số cổng. Bất kỳ nhược điểm nào khi sử dụng 'đa xử lý'? –

0

Âm thanh như đặt cược tốt nhất của bạn là gắn bó với tệp pid nhưng không chỉ chứa quá trình Id - có nó cũng bao gồm số cổng mà phiên bản trước đó đang lắng nghe. Vì vậy, khi bắt đầu kiểm tra các tập tin pid và nếu hiện tại xem nếu một quá trình với Id đó đang chạy - nếu như vậy gửi dữ liệu của bạn đến nó và bỏ nếu không ghi đè lên tập tin pid với thông tin của quá trình hiện tại.

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