I thought I'd found an easy way to return an http response immediately then do some work in the background without blocking. However, this doesn't work.
func MyHandler(w http.ResponseWriter, r *http.Request) {
//handle form values
go doSomeBackgroundWork() // this will take 2 or 3 seconds
w.WriteHeader(http.StatusOK)
}
It works the first time--the response is returned immediately and the background work starts. However, any further requests hang until the background goroutine completes. Is there a better way to do this, that doesn't involve setting up a message queue and a separate background process.
I know this question was posted 4 years ago, but I hope someone can find this useful.
Here is a way to do that
There are something called worker pool https://gobyexample.com/worker-pools Using go routines and channels
But in the following code I adapt it to a handler. (Consider for simplicity I'm ignoring the errors and I'm using jobs as global variable)
package main
import (
"fmt"
"net/http"
"time"
)
var jobs chan int
func worker(jobs <-chan int) {
fmt.Println("Register the worker")
for i := range jobs {
fmt.Println("worker processing job", i)
time.Sleep(time.Second * 5)
}
}
func handler(w http.ResponseWriter, r *http.Request) {
jobs <- 1
fmt.Fprintln(w, "hello world")
}
func main() {
jobs = make(chan int, 100)
go worker(jobs)
http.HandleFunc("/request", handler)
http.ListenAndServe(":9090", nil)
}
The explanation:
main()
Runs the worker in background using a go routine
Start the service with my handler
note that the worker in this moment is ready to receive a job
worker()
it is a go routine that receives a channel
the for loop never ends because the channel is never closed
when a the channel contain a job, do some work (e.g. waits for 5 seconds)
handler()
writes to the channel to active a job
immediately returns printing "hello world" to the page
the cool thing is that you can send as many requests you want and because this scenario only contains 1 worker. the next request will wait until the previous one finished.
This is awesome Go!
Go multiplexes goroutines onto the available threads which is determined by the GOMAXPROCS environment setting. As a result if this is set to 1 then a single goroutine can hog the single thread Go has available to it until it yields control back to the Go runtime. More than likely doSomeBackgroundWork is hogging all the time on a single thread which is preventing the http handler from getting scheduled.
There are a number of ways to fix this.
First, as a general rule when using goroutines, you should set GOMAXPROCS to the number of CPUs your system has or to whichever is bigger.
Second, you can yield control in a goroutine by doing any of the following:
runtime.Gosched()
ch <- foo
foo := <-ch
select { ... }
mutex.Lock()
mutex.Unlock()
All of these will yield back to the Go runtime scheduler giving other goroutines a chance to work.
Related
Timeout handler moves ServeHTTP execution on a new goroutine, but not able to kill that goroutine after the timer ends. On every request, it creates two goroutines, but ServeHTTP goroutines never kill with context.
Not able to find a way to kill goroutines.
Edit For-loop with time.Sleep function, represents huge computation which goes beyond our timer. Can replace it with any other function.
package main
import (
"fmt"
"io"
"net/http"
"runtime"
"time"
)
type api struct{}
func (a api) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// For-loop block represents huge computation and usually takes more time
// Can replace with any code
i := 0
for {
if i == 500 {
break
}
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
time.Sleep(1 * time.Second)
i++
}
_, _ = io.WriteString(w, "Hello World!")
}
func main() {
var a api
s := http.NewServeMux()
s.Handle("/", a)
h := http.TimeoutHandler(s, 1*time.Second, `Timeout`)
fmt.Printf("#goroutines: %d\n", runtime.NumGoroutine())
_ = http.ListenAndServe(":8080", h)
}
ServeHTTP goroutine should kill along with request context, normally which does not happen.
Use context.Context to instruct go-routines to abort their function. The go-routines, of course, have to listen for such cancelation events.
So for your code, do something like:
ctx := req.Context() // this will be implicitly canceled by your TimeoutHandler after 1s
i := 0
for {
if i == 500 {
break
}
// for any long wait (1s etc.) always check the state of your context
select {
case <-time.After(1 * time.Second): // no cancelation, so keep going
case <-ctx.Done():
fmt.Println("request context has been canceled:", ctx.Err())
return // terminates go-routine
}
i++
}
Playground: https://play.golang.org/p/VEnW0vsItXm
Note: Context are designed to be chained - allowing for multiple levels of sub-tasks to be canceled in a cascading manner.
In a typical REST call one would initiate a database request. So, to ensure such a blocking and/or slow call completes in a timely manner, instead of using Query one should use QueryContext - passing in the http request's context as the first argument.
I found, if you do not have any way to reach to your channel then there is no way to kill or stop goroutine when it is running.
In the large computational task, you have to watch the channel on a specific interval or after specific task completion.
I have a concept here which I don't know how I should solve correctly with minimum impact on the system in Go.
I'm making a 'print-spooler' where clients can call in on an API (/StartJob) to process printjobs.
Since there is only one printer so the bottleneck is a single worker that processes each job at a time, but clients can pass one job at any given time, it will just queue up and the worker will process each job in the time it takes step by step.
The way I'm doing it is that ServeHTTP pushes the job onto the channel (note here I just pass on the ID it the worker will look up the printdata from that):
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")
}
}
The Worker then just runs all the time and processes any jobs coming in. Real code there is a bit more but in the end it executes an external process:
func worker(jobs <-chan QueueElement) {
for {
job := <-jobs
processExec ("START /i /b processAndPrint.exe -"+job.jobid)
}
The thing here is the external process can take time to execute, sometimes its instant but under some circumstances it can take 1 minute to perform the task before it returns.
My problem here is that now in serverHTTP, I write back to the client instantly with no knowledge if the job was the first in line and instantly can be processed, or if it has been queued up and maybe will be seconds away or minutes away before its processed:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
What I would like is to give the client up to 5 seconds to get a reply back if his job was processed within that time, or if not, tell him he needs to call back later to check the status of his job.
I had several approaches in mind:
In my QueueElemnt I pass on the http.ResponseWriter so I'm able to write to the responsewriter (reply back to client) from the Worker. This I can do if I let the serveHTTP sleep as the ResponseWriter will shut down when the go routine exists. So here I would need to wait in serveHTTP and then when its waiting the worker is allowed to write to the ResponseWriter.
The problem with this approach is that if the job is minutes away, the Worker won't write anything to that ResponseWriter, and the serveHTTP wouldn't know if a reply had been sent from the worker.
I could create a channel for each QueueElement so the serveHTTP and not only the worker but the actual job if being processed by the worker are able to communicate to each other.
This approach I haven't tested, but I also worry here that its overkill and heavy on the system as we can have a situation where we have many many many api requests coming in and thereby a big queue that's being processed, so even though I would need to timeout/cancel it after 5 seconds, I think the concept is overkill?
I could maybe pass on a mutexed value in the queueElement that both serveHTTP could check for up to 5 seconds and the queue could check/manipulate but in case a job is finished then the queueElement disappears so this maybe results in conflicts.
I could do a variation of no 1) where I write my own responsewriter and use the flag if something has been written to it already, so the serveHTTP would check for up to 5 seconds on that to check if the Worker already wrote a reply back to the client and in that case exit serveHTTP with no answer, or in case no writes then serveHTTP would write the message back to the client, a bit along the lines of this.
But none of these I feel are very smooth and I don't want to launch forever amount of go-routines or channels or lock myself into mutuxes everywhere as I don't know the impact of what it has on the system.
Can anyone assist me in a working correct way to implement such a thing? I've been reading page up page down and haven't found a nice and clean way of achieving this.
I think the easiest approach is the first one slightly modified. You can pass the http.ResponseWriter to the worker, which spanws another worker that actually carries out the job, while the "parent" worker waits for its completition or a timeout. It will reply to the HTTP client as soon as one of the two events occurs first.
func (gv *GlobalVariables) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/StartJob":
newPrintJob := QueueElement {writer: w, 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 {
done := make(chan struct{})
job := <-jobs
go func(d chan struct{}, q QueueElement) {
processExec ("START /i /b processAndPrint.exe -"+q.jobid)
d <- struct{}{}
}(done, job)
select {
//Timeout
case <-time.After(time.Second * 5):
fmt.Fprintf(w, "job is taking more than 5 seconds to complete\r\n")
//Job processing finished in time
case <-done:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
}
}
You can spawn the "waiting" goroutine as soon as you receive the HTTP request. In this way the timeout timer will take into account the entire processing of the request/job.
Example:
package main
import (
"context"
"fmt"
"net/http"
"time"
)
func (gv *GlobalVariables) ServeHTTP(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/StartJob":
doneC := make(chan struct{}, 1) //Buffered channel in order not to block the worker routine
newPrintJob := QueueElement{
doneChan: doneC,
jobid: "jobid",
}
go func(doneChan chan struct{}) {
ctx, cancel := context.WithTimeout(ctx, 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")
case <-doneChan:
fmt.Fprintf(w, "instant reply from serveHTTP\r\n")
}
}(doneC)
gv.printQueue <- newPrintJob
default:
fmt.Fprintf(w, "No such Api")
}
}
func worker(jobs <-chan QueueElement) {
for {
job := <-jobs
processExec("START /i /b processAndPrint.exe -" + job.jobid)
job.doneChan <- struct{}{}
}
}
I would avoid holding the request for a long time because we are not sure when the job will be processed.
One approach I can think of is:
Initially reply from the server as accepted / queued and return job_id.
{
"job_id": "1",
"status": "queued"
}
The client can poll(say every 5 seconds) or use long polling to check the status of the job.
When is it running:
{
"job_id": "1",
"status": "processing"
}
After completion:
{
"job_id": "1",
"status": "success/failed"
}
package main
import (
"fmt"
"time"
)
func main() {
p := producer()
for c := range p {
fmt.Println(c)
}
}
func producer() <-chan string {
ch := make(chan string)
go func() {
for i := 0; i < 5; i++ {
ch <- fmt.Sprint("hello", i)
time.Sleep(1 * time.Second)
}
// commented the below to show the issue
// close(ch)
}()
return ch
}
Running the above code will print 5 messages and then give a "all go routines are a sleep - deadlock error". I understand that if I close the channel the error is gone.
The thing I would like to understand is how does go runtime know that the code will be waiting infinitely on the channel and that there is nothing else that will be sending data into the channel.
Now if I add an additional go routine to the main() function.. it does not throw any error and keeps waiting on the channel.
go func() {
for {
time.Sleep(2 * time.Millisecond)
}
}()
So does this mean.. the go runtime is just looking for presence of a running go routine that could potentially send data into the channel and hence not throwing the deadlock error ?
If you want some more insight into how Go implements the deadlock detection, have a look at the place in the code that throws the "all goroutines are asleep - deadlock!": https://github.com/golang/go/blob/master/src/runtime/proc.go#L3751
It looks like the Go runtime keeps some fairly simple accounting on how many goroutines there are, how many are idle, and how many are sleeping for locks (not sure which one sleep on channel I/O will increment). At any given time (serialized with the rest of the runtime), it just does some arithmetic and checks if all - idle - locked > 0... if so, then the program could still make progress... if it's 0, then you're definitely deadlocked.
It's possible you could introduce a livelock by preventing a goroutine from sleeping via an infinite loop (like what you did in your experiment, and apparently sleep for timers isn't treated the same by the runtime). The runtime wouldn't be able to detect a deadlock in that case, and run forever.
Furthermore, I'm not sure when exactly the runtime checks for deadlocks- further inspection of who calls that checkdead() may yield some insight there, if you're interested.
DISCLAIMER- I'm not a Go core developer, I just play one on TV :-)
The runtime panics with the "all go routines are a sleep - deadlock error" error when all goroutines are blocked on channel and mutex operations.
The sleeping goroutine does not block on one of these operations. There is no deadlock and therefore no panic.
Is there any API to let the main goroutine sleep forever?
In other words, I want my project always run except when I stop it.
"Sleeping"
You can use numerous constructs that block forever without "eating" up your CPU.
For example a select without any case (and no default):
select{}
Or receiving from a channel where nobody sends anything:
<-make(chan int)
Or receiving from a nil channel also blocks forever:
<-(chan int)(nil)
Or sending on a nil channel also blocks forever:
(chan int)(nil) <- 0
Or locking an already locked sync.Mutex:
mu := sync.Mutex{}
mu.Lock()
mu.Lock()
Quitting
If you do want to provide a way to quit, a simple channel can do it. Provide a quit channel, and receive from it. When you want to quit, close the quit channel as "a receive operation on a closed channel can always proceed immediately, yielding the element type's zero value after any previously sent values have been received".
var quit = make(chan struct{})
func main() {
// Startup code...
// Then blocking (waiting for quit signal):
<-quit
}
// And in another goroutine if you want to quit:
close(quit)
Note that issuing a close(quit) may terminate your app at any time. Quoting from Spec: Program execution:
Program execution begins by initializing the main package and then invoking the function main. When that function invocation returns, the program exits. It does not wait for other (non-main) goroutines to complete.
When close(quit) is executed, the last statement of our main() function can proceed which means the main goroutine can return, so the program exits.
Sleeping without blocking
The above constructs block the goroutine, so if you don't have other goroutines running, that will cause a deadlock.
If you don't want to block the main goroutine but you just don't want it to end, you may use a time.Sleep() with a sufficiently large duration. The max duration value is
const maxDuration time.Duration = 1<<63 - 1
which is approximately 292 years.
time.Sleep(time.Duration(1<<63 - 1))
If you fear your app will run longer than 292 years, put the above sleep in an endless loop:
for {
time.Sleep(time.Duration(1<<63 - 1))
}
It depends on use cases to choose what kind of sleep you want.
#icza provides a good and simple solution for literally sleeping forever, but I want to give you some more sweets if you want your system could shutdown gracefully.
You could do something like this:
func mainloop() {
exitSignal := make(chan os.Signal)
signal.Notify(exitSignal, syscall.SIGINT, syscall.SIGTERM)
<-exitSignal
systemTeardown()
}
And in your main:
func main() {
systemStart()
mainloop()
}
In this way, you could not only ask your main to sleep forever, but you could do some graceful shutdown stuff after your code receives INT or TERM signal from OS, like ctrl+C or kill.
Another solution to block a goroutine. This solution prevents Go-Runtime to complain about the deadlock:
import "time"
func main() {
for {
time.Sleep(1138800 * time.Hour)
}
}
I've following code using go routines:
package main
import (
"fmt"
"time"
)
func thread_1(i int) {
time.Sleep(time.Second * 1)
fmt.Println("thread_1: i: ",i)
}
func thread_2(i int) {
time.Sleep(time.Second * 1)
fmt.Println("thread_2: i: ",i)
}
func main() {
for i := 0; i < 100; i++ {
go thread_1(i)
go thread_2(i)
}
var input string
fmt.Scanln(&input)
}
I was expecting each go routine would wait for a second and then print its value of i.
However, both the go routines waited for 1 second each and printed all the values of i at once.
Another question in the same context is:
I've a server-client application where server spawns a receiver go routine to receive data from client one for each client connection. Then the receiver go routine spawns a worker go routine called processor to process the data. There could be multiple receiver go routines and hence multiple processor go routines.
In such a case some receiver and processor go routines may hog indefinitely.
Please help me understand this behaviour.
You span 100 goroutines running thread_1 and 100 goroutines running thread_2 in one big batch. Each of these 200 goroutines sleeps for one second and then prints and ends. So yes, the behavior is to be expected: 200 goroutines sleeping each 1 second in parallel and then 200 goroutines printing in parallel.
(And I do not understand your second question)