Предположим, вы работаете с Django в Linux и у вас есть представление, и вы хотите, чтобы это представление возвращало данные из подпроцесса cmd , который работает на файл, который создает представление, например likeso:
def call_subprocess(request):
response = HttpResponse()
with tempfile.NamedTemporaryFile("W") as f:
f.write(request.GET['data']) # i.e. some data
# cmd operates on fname and returns output
p = subprocess.Popen(["cmd", f.name],
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
out, err = p.communicate()
response.write(p.out) # would be text/plain...
return response
Теперь предположим, что cmd имеет очень медленное время запуска, но очень быстрое время работы и изначально не имеет режима демона. Я хотел бы улучшить время отклика этого представления.
Я бы хотел, чтобы вся система работала намного быстрее, запустив несколько экземпляров cmd в рабочем пуле, попросив их дождаться ввода и имея call_process попросить один из этих процессов рабочего пула обработать данные.
Это действительно 2 части:
Часть 1. Функция, которая вызывает cmd и cmd , ожидает ввода. Это можно сделать с помощью труб, т. Е.
def _run_subcmd():
p = subprocess.Popen(["cmd", fname],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
out, err = p.communicate()
# write 'out' to a tmp file
o = open("out.txt", "W")
o.write(out)
o.close()
p.close()
exit()
def _run_cmd(data):
f = tempfile.NamedTemporaryFile("W")
pipe = os.mkfifo(f.name)
if os.fork() == 0:
_run_subcmd(fname)
else:
f.write(data)
r = open("out.txt", "r")
out = r.read()
# read 'out' from a tmp file
return out
def call_process(request):
response = HttpResponse()
out = _run_cmd(request.GET['data'])
response.write(out) # would be text/plain...
return response
Часть 2. Набор работающих в фоновом режиме работников, ожидающих данных. мы хотим расширить вышеприведенное, чтобы подпроцесс уже работал, например, при инициализации экземпляра Django или при первом вызове call_process создается набор этих рабочих
WORKER_COUNT = 6
WORKERS = []
class Worker(object):
def __init__(index):
self.tmp_file = tempfile.NamedTemporaryFile("W") # get a tmp file name
os.mkfifo(self.tmp_file.name)
self.p = subprocess.Popen(["cmd", self.tmp_file],
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
self.index = index
def run(out_filename, data):
WORKERS[self.index] = Null # qua-mutex??
self.tmp_file.write(data)
if (os.fork() == 0): # does the child have access to self.p??
out, err = self.p.communicate()
o = open(out_filename, "w")
o.write(out)
exit()
self.p.close()
self.o.close()
self.tmp_file.close()
WORKERS[self.index] = Worker(index) # replace this one
return out_file
@classmethod
def get_worker() # get the next worker
# ... static, incrementing index
Где-то должна быть инициализация рабочих, например:
def init_workers(): # create WORKERS_COUNT workers
for i in xrange(0, WORKERS_COUNT):
tmp_file = tempfile.NamedTemporaryFile()
WORKERS.push(Worker(i))
Теперь то, что я имею выше, становится чем-то вроде:
def _run_cmd(data):
Worker.get_worker() # this needs to be atomic & lock worker at Worker.index
fifo = open(tempfile.NamedTemporaryFile("r")) # this stores output of cmd
Worker.run(fifo.name, data)
# please ignore the fact that everything will be
# appended to out.txt ... these will be tmp files, too, but named elsewhere.
out = fifo.read()
# read 'out' from a tmp file
return out
def call_process(request):
response = HttpResponse()
out = _run_cmd(request.GET['data'])
response.write(out) # would be text/plain...
return response
Теперь вопросы:
Будет ли это работать? (Я только что набрал это в голове StackOverflow, так что я уверен, что есть проблемы, но концептуально, это будет работать)
Какие проблемы нужно искать?
Есть ли лучшие альтернативы этому? например Могут ли потоки работать так же хорошо (это Debian Lenny Linux)? Существуют ли какие-либо библиотеки, которые обрабатывают рабочие пулы параллельных процессов, как это?
Есть ли взаимодействия с Джанго, о которых я должен знать?
Спасибо за чтение! Я надеюсь, вы найдете эту проблему такой же интересной, как и я.
Brian