在这里已经获得了一些帮助,这使我在尝试这一概念时向前迈进了一步,但是它仍然无法正常工作,我遇到了我似乎无法解决的冲突。
我在这里尝试在流程图中说明我想要的内容-请注意,客户端可以有许多将通过printjobs发送的客户端,因此我们当时无法答复工人正在处理我们的工作,但是对于大多数情况下(高峰时间不会,因为打印的处理工作会花费时间)。
type QueueElement struct {
jobid string
rw http.ResponseWriter
doneChan chan struct{}
}
type GlobalVars struct {
db *sql.DB
wg sync.WaitGroup
jobs chan QueueElement
}
func (gv *GlobalVars) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/StartJob":
fmt.Printf ("incoming\r\n")
doneC := make(chan struct{}, 1) //Buffered channel in order not to block the worker routine
newPrintJob := QueueElement{
doneChan: doneC,
jobid: "jobid",
}
gv.jobs <- newPrintJob
func(doneChan chan struct{},w http.ResponseWriter) {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
select {
//If this triggers first, then this waiting goroutine would exit
//and nobody would be listeding the 'doneChan'. This is why it has to be buffered.
case <-ctx.Done():
fmt.Fprintf(w, "job is taking more than 5 seconds to complete\r\n")
fmt.Printf ("took longer than 5 secs\r\n")
case <-doneChan:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
fmt.Printf ("instant\r\n")
}
}(doneC,w)
default:
fmt.Fprintf(w, "No such Api")
}
}
func worker(jobs <-chan QueueElement) {
for {
job := <-jobs
processExec ("START /i /b try.cmd")
fmt.Printf ("job done")
// processExec("START /i /b processAndPrint.exe -" + job.jobid)
job.doneChan <- struct{}{}
}
}
上面的代码是serveHTTP的代码,工作人员在这里提供了帮助,最初,ServeHTTP内的func是一个go例程,在这里,整个冲突对我来说都是一个问题-概念是在serveHTTP中它产生了一个进程如果该员工能够在5秒钟内及时处理该工作,它将得到该员工的答复。
如果工作能够在1秒钟内完成,我想在那1秒钟后立即回覆给客户,如果需要3秒钟,我想在3秒钟后回复,如果花费超过5秒钟,我会在之后回复5秒(如果这项工作需要13秒,我仍然想在5秒后回复)-客户必须从现在开始轮询该工作-但冲突是:
a)当ServeHTTP退出时-然后ResponseWriter关闭-为了能够回复客户端,我们必须将答案写到ResponseWriter。
b)如果我阻止了serveHTTP(例如下面的代码示例,其中我未将func作为go例程调用),那么它不仅会影响单个API调用,而且似乎还会影响此后的所有其他API,因此第一次呼叫将得到正确的及时服务,但在第一次呼叫之后的同一时间将被阻塞操作延迟。
c)如果我不阻止它-并将其更改为执行例程:
gv.jobs <- newPrintJob
go func(doneChan chan struct{},w http.ResponseWriter) {
然后没有延迟-可以调用许多api-但问题是在这里serveHTTP立即存在,从而杀死了ResponseWriter,然后我无法回复给客户端!
这真的让我头痛不已,我该如何解决这个冲突,在这种情况下我不会造成任何服务HTTP阻塞,因此我可以并行处理所有请求,但仍能够回复有问题的ResponseWriter。
即使函数退出,是否仍可以阻止serveHTTP关闭responsewriter?
答案 0 :(得分:1)
我已经为您的代码添加了一些更新。现在,它可以按照您所描述的那样工作。
package main
import (
"database/sql"
"fmt"
"log"
"math/rand"
"net/http"
"sync"
"time"
)
type QueueElement struct {
jobid string
rw http.ResponseWriter
doneChan chan struct{}
}
type GlobalVars struct {
db *sql.DB
wg sync.WaitGroup
jobs chan QueueElement
}
func (gv *GlobalVars) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/StartJob":
fmt.Printf("incoming\r\n")
doneC := make(chan struct{}, 1) //Buffered channel in order not to block the worker routine
go func(doneChan chan struct{}, w http.ResponseWriter) {
gv.jobs <- QueueElement{
doneChan: doneC,
jobid: "jobid",
}
}(doneC, w)
select {
case <-time.Tick(time.Second * 5):
fmt.Fprintf(w, "job is taking more than 5 seconds to complete\r\n")
fmt.Printf("took longer than 5 secs\r\n")
case <-doneC:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
fmt.Printf("instant\r\n")
}
default:
fmt.Fprintf(w, "No such Api")
}
}
func worker(jobs <-chan QueueElement) {
for {
job := <-jobs
fmt.Println("START /i /b try.cmd")
fmt.Printf("job done")
randTimeDuration := time.Second * time.Duration(rand.Intn(7))
time.Sleep(randTimeDuration)
// processExec("START /i /b processAndPrint.exe -" + job.jobid)
job.doneChan <- struct{}{}
}
}
func main() {
// create a GlobalVars instance
gv := GlobalVars{
//db: db,
jobs: make(chan QueueElement),
}
go worker(gv.jobs)
// create an http.Server instance and specify our job manager as
// the handler for requests.
server := http.Server{
Handler: &gv,
Addr: ":8888",
}
// start server and accept connections.
log.Fatal(server.ListenAndServe())
}
答案 1 :(得分:0)
select
语句应位于goroutine函数之外,并阻塞请求,直到作业执行结束或达到超时为止。
答案 2 :(得分:0)
是的,您的观点是正确的“ c)如果我不阻止它的话” 。
为了保存响应编写器,您不应在其中调用go例程。相反,您应该将 ServeHTTP 作为常规例程调用,大多数http服务器实现都可以这样做。
这样,您就不会阻止任何api调用,每个api调用都将在不同的go例程中运行,并受到其功能的阻止。
由于您的“ jobs chan QueueElement” 是单个通道(而不是缓冲通道),因此所有进程都在“ gv.jobs <-newPrintJob” 处被阻止。
您应该使用缓冲通道,以便所有api调用都可以将其添加到队列中,并根据工作完成或超时来获取响应。
具有缓冲通道也可以模拟打印机的实际内存限制。 (队列长度1是一种特殊情况)