在go http处理程序中使用goroutine和channel使ResponseWriter被阻止

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

var ch chan bool

func testTimer1() {
    go func() {
        log.Println("test timer 1")
        ch <- true
    }()

}

func timer1() {
    timer1 := time.NewTicker(2 * time.Second)
    select {
    case <-timer1.C:
        testTimer1()
    }

}

func myhandler(w http.ResponseWriter, r *http.Request) {
    for {
        go timer1()

        a := <-ch
        log.Println("get a: ", a)
        fmt.Fprintf(w, "hello world!!!!", a)
    }

    log.Println("test for break")
}

func main() {
    ch = make(chan bool)
    http.HandleFunc("/", myhandler)
    http.ListenAndServe(":8080", nil)
}

I wrote the above code, put a channel into "myhandler", channel will be given a bool data when the

timer task executed.

then I get the data from channel and write "hello world" into http writer

but I found the client couldn't receive the "hello world", the writer has been blocked!!!!!

Any one knows about this?

looks the running pic on my cmd: enter image description here

enter image description here

The for loop is an infinite loop so printing to the ResponseWriter is not "scheduled" to happen. If you want a comet-like approack (or long-polling URL) you may want to try this method.

There's also a leak of tickers in timer1(). According to the Go Docs:

Stop the ticker to release associated resources.

You're always creating a new ticker every time you call go timer1() and the ticker is never closed so every new ticker just adds-up.

Short answer

Avoid buffering

Client Side

Use curl with --no-buffer set

curl http://localhost:8080 --no-buffer

Server Side

Flush after every fmt.Fprint

w.(http.Flusher).Flush()

Long Answer

   The biggest problem when implementing HTTP streaming is understanding the effect of buffering. Buffering is the practice of accumulating reads or writes into a temporarily fixed memory space. The advantages of buffering include reducing read or write call overhead. For example, instead of writing 1KB 4096 times, you can just write 4096KB at once. This means your program can create a write buffer holding 4096KB of temporary data (which can be aligned to the disk block size), and once the space limit is reached, the buffer is flushed to disk.

Here the above mentioned HTTP component include two components Server(go server) and Client(Curl).Each one of these components can possess adjustable and varied buffering styles and limits.

An unrelated issue, n the program given it has one more problem ie, not stopping timer always stop the ticker to release associated resources.

Here is an implementation with some corrections

Code

package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

var ch chan bool

func testTimer1() {
    go func() {
        log.Println("test timer 1")
        ch <- true
    }()

}

func timer1() {
    timer1 := time.NewTicker(2 * time.Second)
    defer timer1.Stop()
    <-timer1.C
    testTimer1()
}

func myhandler(w http.ResponseWriter, r *http.Request) {
    for {
        go timer1()

        a := <-ch
        log.Println("get a: ", a)
        fmt.Fprintf(w, "hello world!!!! - %v", a)
        w.(http.Flusher).Flush()
    }
}

func main() {
    ch = make(chan bool)
    http.HandleFunc("/", myhandler)
    http.ListenAndServe(":8080", nil)
}

Curl

curl http://localhost:8080 --no-buffer