Предположим, вы используете Django в Linux, и у вас есть представление, и вы хотите, чтобы это представление возвращало данные из подпроцесса с именем cmd, который работает с файлом что создает представление, например вот так:
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 в рабочем пуле, дождавшись ввода и имея < strong>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)? Существуют ли какие-либо библиотеки, которые обрабатывают рабочие пулы параллельных процессов, подобные этой?
Есть ли взаимодействия с Джанго, о которых я должен знать?
Спасибо за чтение! Надеюсь, вы найдете эту проблему столь же интересной, как и я.
Брайан