-1
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

曹少琨
  • 19
  • 1
  • I got nothing by use " curl http://localhost:8080" – 曹少琨 Mar 23 '17 at 14:54
  • Try using `Fprintln` instead of `Fprintf`? – kennytm Mar 23 '17 at 14:54
  • yes I've tried Fprintln, but it also didn't work @kennytm – 曹少琨 Mar 23 '17 at 14:55
  • Does it work if you [flush `w`](http://stackoverflow.com/questions/19292113/not-buffered-http-responsewritter-in-golang/19292461#19292461)? – kennytm Mar 23 '17 at 14:56
  • T_T @kennytm no use, I've tried just now – 曹少琨 Mar 23 '17 at 14:59
  • Curl is line-buffering the chunked output. Add a newline to the output then flush the RepsonseWriter after writing. – JimB Mar 23 '17 at 15:24
  • BTW, you're leaking a `Ticker` from every loop in your handler. – JimB Mar 23 '17 at 15:24
  • @JimB I used fmt.Fprintln instead of fmt.Fprintf and added flush, it should work, but if I use w.Write() to send byte stream, it won't work too – 曹少琨 Mar 23 '17 at 16:09
  • @曹少琨: I don't understand what you're saying. If you call Flush on the `ResponseWriter` the output will be written to the network. How your client buffers and displays the output isn't related to this issue. – JimB Mar 23 '17 at 17:37

2 Answers2

1

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.

Patrick D'appollonio
  • 2,722
  • 1
  • 18
  • 34
1

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 
Kaedys
  • 9,600
  • 1
  • 33
  • 40