У меня есть концепция, которую я не знаю, как мне правильно решить, с минимальным влиянием на систему в Go.
Я делаю 'спулер печати', где клиенты могут звонить наAPI (/ StartJob) для обработки заданий на печать.
Поскольку существует только один принтер, поэтому узким местом является отдельный работник, который обрабатывает каждое задание за раз, но клиенты могут передавать одно задание в любой момент времени, поэтомупросто поставьте в очередь, и работник будет обрабатывать каждую работу за время, которое требуется шаг за шагом.
Способ, которым я делаю это, заключается в том, что ServeHTTP передает работу на канал (обратите внимание, здесь я просто передаю идентификаторесли рабочий будет искать данные печати из этого списка):
func (gv *GlobalVariables) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/StartJob":
newPrintJob := QueueElement {jobid: "jobid"}
gv.printQueue <- newPrintJob
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
default:
fmt.Fprintf(w, "No such Api")
}
}
Затем работник просто все время запускает и обрабатывает любые поступающие задания. Реального кода здесь немного больше, но в конце он выполняетвнешний процесс:
func worker(jobs <-chan QueueElement) {
for {
job := <-jobs
processExec ("START /i /b processAndPrint.exe -"+job.jobid)
}
Дело в том, что внешний процесс может занять некоторое время, иногда его мгновение, но при некоторых обстоятельствах выполнение задачи может занять 1 минутупрежде чем он вернется.
Моя проблема здесь в том, что теперь в serverHTTP я мгновенно переписываюсь с клиентом, не зная, было ли задание первым в очереди и его можно было мгновенно обработать или если оно было поставлено в очередьи, возможно, до обработки будет несколько секунд или минут:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
Я хотел бы дать клиенту до 5 секунд, чтобы получить ответ, если его работа была обработана за это время, илиесли нет, скажите ему, что ему нужно перезвонить позже, чтобы проверить статус своей работы.
Я имел в виду несколько подходов:
В моем QueueElemnt я передаюhttp.ResponseWriter, так что я могу писать в ответчик (ответ обратно клиенту) от рабочего.Это я могу сделать, если я оставлю serveHTTP в спящем режиме, так как ResponseWriter завершит работу, когда подпрограмма go существует.Поэтому здесь мне нужно было бы подождать в serveHTTP, а затем, когда его ожидание, работнику разрешено писать в ResponseWriter.
Проблема с этим подходом состоит в том, что, если задание находится на расстоянии нескольких минут, работник не будет писатьчто-нибудь для этого ResponseWriter, и serveHTTP не будет знать, был ли отправлен ответ от работника.
Я мог бы создать канал для каждого элемента QueueElement, чтобы serveHTTP, а не только работникно фактическое задание, если оно обрабатывается работником, может общаться друг с другом.
Этот подход я не проверял, но я также беспокоюсь, что он излишний и тяжелый для системы, поскольку мы можем иметьситуация, когда у нас появляется много-много запросов API и, следовательно, большая очередь, которая обрабатывается, так что, хотя мне нужно было бы тайм-аут / отменить его через 5 секунд, я думаю, что концепция излишняя?
Я мог бы передать мьютексированное значение в queueElement, которое оба serveHTTP могли проверять до 5 секундs и очередь может проверять / манипулировать, но в случае, если задание завершено, queueElement исчезает, поэтому это может привести к конфликтам.
Я мог бы сделать вариант № 1), где я пишумой собственный обработчик ответов и используйте флаг, если что-то уже было записано в него, так что serveHTTP проверит до 5 секунд, чтобы проверить, не написал ли Worker ответ клиенту, и в этом случае выйдет serveHTTP без ответаили, в случае отсутствия записи, serveHTTP запишет сообщение обратно клиенту, немного по строчке this .
Но я не чувствую ничего из этогоочень плавный, и я не хочу запускать бесконечное количество подпрограмм или каналов или блокировать себя везде, где есть муксы, поскольку я не знаю, как это влияет на систему.
Может ли кто-нибудь мне помочь?работать правильно, чтобы реализовать такую вещь?Я читал страницу вверх страницу вниз и не нашел хороший и чистый способ достижения этого.