Взаимодействие с длительным скриптом python

У меня есть длинный сценарий Python, который собирает твиты из Twitter, и я хотел бы знать, как это происходит каждый раз через некоторое время.

В настоящее время я использую библиотеку 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> 

Есть лучший способ сделать это? Я знаю, что мог бы иметь свой скрипт что-то по расписанию, но меня больше интересует знание по требованию и, возможно, выдача других команд.

Отправка сигнала для обработки приведет к прерыванию процесса. Ниже вы найдете подход, который использует выделенный поток для эмуляции консоли 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 

Пример сеанса:

 mmatczuk@cactus:~$ 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() mmatczuk@cactus:~$ 

Это довольно надежный инструмент, который можно использовать для:

  • сбрасывать нити,
  • проверять память процесса,
  • приложить отладчик по требованию, отладчик pydev (работа как для eclipse, так и для pycharm)
  • сила GC,
  • определение функции monkeypatch на лету

и даже больше.

Я лично пишу информацию в файл, чтобы потом у меня это получилось, хотя у этого есть недостаток, возможно, немного медленный, потому что он должен каждый раз записывать файл каждый раз или каждые несколько раз извлекает твит.

В любом случае, если вы напишете его в файл "output.txt" , вы можете открыть bash и либо напечатать tail output.txt для последних 10 строк, напечатанных в файле, либо вы можете ввести tail -f output.txt , который постоянно обновляет приглашение терминала строками, которые вы пишете в файл. Если вы хотите остановиться, просто Ctrl-C .

Вот пример долговременной программы, которая также поддерживает сокет состояния. Когда клиент подключается к сокету, сценарий записывает некоторую информацию о статусе в сокет.

 #!/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 

Раньше я писал подобное приложение.

Вот что я сделал:

Когда требуется только несколько команд, я просто использую сигнал, как вы, просто потому, что он не слишком усложняется. По команде, я имею в виду то, что вы хотите, чтобы приложение выполнялось, например print_info в вашем сообщении.

Но когда приложение обновляется, требуется больше разных команд, я начал использовать специальный поток, прослушивающий порт сокета, или чтение локального файла для принятия команд. Предположим, что приложение должно поддерживать prinf_info1 print_info2 print_info3 , поэтому вы можете использовать клиент для подключения к целевому порту и написать print_info1, чтобы заставить приложение выполнить команду print_info1 (или просто написать print_info1 в локальный файл, если вы используете локальный файловый механизм чтения) ,

При использовании прослушивания в механизме портового сокета недостатком является то, что потребуется немного больше работы, чтобы написать клиенту, чтобы давать команды, преимущество в том, что вы можете отдавать заказы где угодно.

При использовании чтения локального файлового механизма недостатком является то, что вы должны заставить поток проверять файл в цикле, и он будет использовать бит-ресурс, преимущество дает заказы очень просто (просто напишите строку в файл) и вам не нужно писать сервер прослушивания клиентов и сокетов.

rpyc – идеальный инструмент для выполнения этой задачи.

Короче говоря, вы определяете класс rpyc.Service который предоставляет команды, которые вы хотите открыть, и rpyc.Server поток rpyc.Server .

Затем ваш клиент подключается к вашему процессу и вызывает методы, которые сопоставляются с командами, предоставляемыми вашим сервисом.

Это так просто и чисто. Не нужно беспокоиться о сокетах, сигналах, сериализации объектов.

Он имеет и другие интересные функции, например, протокол является симметричным.

Ваш вопрос касается межпроцессного общения. Вы можете добиться этого, передав через сокет unix или TCP-порт, используя разделяемую память или используя очередь сообщений или систему кэширования, такую ​​как RabbitMQ и Redis.

В этом сообщении говорится об использовании mmap для обеспечения обмена информацией между общими памятью.

Вот как начать работу с redis и RabbitMQ , и они довольно просты в реализации.