2014-06-18 96 views
5

我有一個長時間運行的Python腳本,它收集來自Twitter的推文,我想知道它每隔一段時間做一次。與長時間運行的python腳本進行交互

目前,我使用的是signal庫捉中斷,在這一點上,我打電話給我的打印功能。事情是這樣的:

import signal 

def print_info(count): 
    print "#Tweets:", count 

#Print out the process ID so I can interrupt it for info 
print 'PID:', os.getpid() 

#Start listening for interrupts 
signal.signal(signal.SIGUSR1, functools.partial(print_info, tweet_count)) 

每當我想我的信息,我打開了一個新的終端併發出中斷我:

$kill -USR1 <pid> 

有沒有更好的方式來做到這一點?我知道我可以在預定的時間間隔內使用我的腳本,但我更感興趣的是瞭解需求並可能發佈其他命令。

+0

這似乎是一個適用的使用信號庫。你爲什麼認爲有更好的辦法? – MrAlias

+0

您可以讓線程偵聽連接的套接字,然後在客戶端連接時將信息寫入套接字。 – larsks

+0

@MrAlias我打算有多個「打印」方法,並且從我可以告訴的是我可以使用有限的中斷,所以我想也許有一種不同的方式在運行時與我的程序進行交互。 – Tyler

回答

0

我個人信息寫入到一個文件,以便我把它之後,雖然這有可能是稍微慢一點,因爲它每一次,或者檢索鳴叫每隔幾次寫入文件的缺點。

反正,如果你把它寫入文件"output.txt",可以在tail output.txt打開bash和任一類型爲文件中的打印的最新10行,也可以鍵入tail -f output.txt,它不斷地與線更新終端提示您正在寫入該文件。如果你想停止,只需Ctrl-C

0

下面是一個長期運行的程序示例,該程序還維護一個狀態套接字。當客戶端連接到套接字時,腳本將一些狀態信息寫入套接字。

#!/usr/bin/python 

import os 
import sys 
import argparse 
import random 
import threading 
import socket 
import time 
import select 

val1 = 0 
val2 = 0 
lastupdate = 0 
quit = False 

# This function runs in a separate thread. When a client connects, 
# we write out some basic status information, close the client socket, 
# and wait for the next connection. 
def connection_handler(sock): 
    global val1, val2, lastupdate, quit 

    while not quit: 
     # We use select() with a timeout here so that we are able to catch the 
     # quit flag in a timely manner. 
     rlist, wlist, xlist = select.select([sock],[],[], 0.5) 
     if not rlist: 
      continue 

     client, clientaddr = sock.accept() 
     client.send('%s %s %s\n' % (lastupdate, val1, val2)) 
     client.close() 

# This function starts the listener thread. 
def start_listener(): 
    sock = socket.socket(socket.AF_UNIX) 

    try: 
     os.unlink('/var/tmp/myprog.socket') 
    except OSError: 
     pass 

    sock.bind('/var/tmp/myprog.socket') 
    sock.listen(5) 

    t = threading.Thread(
     target=connection_handler, 
     args=(sock,)) 
    t.start() 

def main(): 
    global val1, val2, lastupdate 

    start_listener() 

    # Here is the part of our script that actually does "work". 
    while True: 
     print 'updating...' 
     lastupdate = time.time() 
     val1 = val1 + random.randint(1,10) 
     val2 = val2 + random.randint(100,200) 
     print 'sleeping...' 
     time.sleep(5) 

if __name__ == '__main__': 
    try: 
     main() 
    except (Exception,KeyboardInterrupt,SystemExit): 
     quit=True 
     raise 

你可以寫一個簡單的Python客戶端連接到插座,或者你可以使用類似socat

$ socat - unix:/var/tmp/myprog.sock 
1403061693.06 6 152 
0

我以前寫一個類似的應用程序。

這裏是我做過什麼:

當有隻需要幾個命令,我只是用信號像你一樣,只是爲了不使它過於複雜。通過命令,我指的是您希望您的應用程序執行的操作,例如print_info

但是,當應用程序更新,有需要更多不同的命令,我開始用一個特殊的線程監聽套接字端口或讀本地文件接受命令。假設應用程序需要支持prinf_info1print_info2print_info3,所以你可以使用一個客戶端連接到目標端口和寫print_info1使應用程序執行命令print_info1(或者只寫print_info1到本地文件,如果你正在使用讀本地文件機制)。

使用的插座端口機制聽,缺點是它需要更多的工作來寫一個客戶下達命令,其優點是你可以在任何地方發號施令。

當使用讀取本地文件機制時,缺點是你必須讓線程檢查一個循環中的文件,它會使用一些資源,優點是命令很簡單(只需寫一個字符串一個文件),你不需要編寫一個客戶端和套接字監聽服務器。

1

向進程發送信號會中斷進程。下面你會發現一種使用專用線程來模擬python控制檯的方法。控制檯暴露爲unix套接字。

import traceback 
import importlib 
from code import InteractiveConsole 
import sys 
import socket 
import os 
import threading 
from logging import getLogger 

# template used to generate file name 
SOCK_FILE_TEMPLATE = '%(dir)s/%(prefix)s-%(pid)d.socket' 

log = getLogger(__name__) 


class SocketConsole(object): 
    ''' 
    Ported form :eventlet.backdoor.SocketConsole:. 
    ''' 
    def __init__(self, locals, conn, banner=None): # pylint: diable=W0622 
     self.locals = locals 
     self.desc = _fileobject(conn) 
     self.banner = banner 
     self.saved = None 

    def switch(self): 
     self.saved = sys.stdin, sys.stderr, sys.stdout 
     sys.stdin = sys.stdout = sys.stderr = self.desc 

    def switch_out(self): 
     sys.stdin, sys.stderr, sys.stdout = self.saved 

    def finalize(self): 
     self.desc = None 

    def _run(self): 
     try: 
      console = InteractiveConsole(self.locals) 
      # __builtins__ may either be the __builtin__ module or 
      # __builtin__.__dict__ in the latter case typing 
      # locals() at the backdoor prompt spews out lots of 
      # useless stuff 
      import __builtin__ 
      console.locals["__builtins__"] = __builtin__ 
      console.interact(banner=self.banner) 
     except SystemExit: # raised by quit() 
      sys.exc_clear() 
     finally: 
      self.switch_out() 
      self.finalize() 


class _fileobject(socket._fileobject): 
    def write(self, data): 
     self._sock.sendall(data) 

    def isatty(self): 
     return True 

    def flush(self): 
     pass 

    def readline(self, *a): 
     return socket._fileobject.readline(self, *a).replace("\r\n", "\n") 


def make_threaded_backdoor(prefix=None): 
    ''' 
    :return: started daemon thread running :main_loop: 
    ''' 
    socket_file_name = _get_filename(prefix) 

    db_thread = threading.Thread(target=main_loop, args=(socket_file_name,)) 
    db_thread.setDaemon(True) 
    db_thread.start() 
    return db_thread 


def _get_filename(prefix): 
    return SOCK_FILE_TEMPLATE % { 
     'dir': '/var/run', 
     'prefix': prefix, 
     'pid': os.getpid(), 
    } 


def main_loop(socket_filename): 
    try: 
     log.debug('Binding backdoor socket to %s', socket_filename) 
     check_socket(socket_filename) 

     sockobj = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) 
     sockobj.bind(socket_filename) 
     sockobj.listen(5) 
    except Exception, e: 
     log.exception('Failed to init backdoor socket %s', e) 
     return 

    while True: 
     conn = None 
     try: 
      conn, _ = sockobj.accept() 
      console = SocketConsole(locals=None, conn=conn, banner=None) 
      console.switch() 
      console._run() 
     except IOError: 
      log.debug('IOError closing connection') 
     finally: 
      if conn: 
       conn.close() 


def check_socket(socket_filename): 
    try: 
     os.unlink(socket_filename) 
    except OSError: 
     if os.path.exists(socket_filename): 
      raise 

實施例的程序:

make_threaded_backdoor(prefix='test') 
while True: 
    pass 

實施例的會話:

[email protected]:~$ rlwrap nc -U /var/run/test-3196.socket 
Python 2.7.6 (default, Mar 22 2014, 22:59:56) 
[GCC 4.8.2] on linux2 
Type "help", "copyright", "credits" or "license" for more information. 
(InteractiveConsole) 
>>> import os 
>>> os.getpid() 
3196 
>>> quit() 
[email protected]:~$ 

這是一個可被用於一個相當強大的工具:

  • 轉儲線程,
  • 檢查進程的內存,
  • 連接調試需求,pydev debugger(工作Eclipse和pycharm)
  • 力GC,在飛行

  • 猴補丁函數定義。

  • 0

    rpyc是完成此任務的理想工具。

    總之,您定義了一個rpyc.Service類,它公開您想要公開的命令,並啓動一個rpyc.Server線程。

    然後,您的客戶端連接到您的進程,並調用映射到您的服務公開的命令的方法。

    就像那樣簡單幹淨。無需擔心套接字,信號,對象序列化。

    它還有其他很酷的功能,例如協議是對稱的。

    0

    您的問題涉及進程間通信。您可以通過unix套接字或TCP端口進行通信,使用共享內存或使用消息隊列或緩存系統(如RabbitMQ和Redis)來實現此目的。

    This post討論使用mmap來實現共享內存進程間通信。

    以下是如何開始使用redisRabbitMQ,兩者都相當容易實現。

    相關問題