英文:
Using goroutine and channel in a go http handler made the ResponseWriter blocked
问题
以下是翻译好的代码:
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)
}
我写了上面的代码,在myhandler
函数中加入了一个通道,当定时任务执行时,通道会被赋予一个布尔值数据。
然后我从通道中获取数据,并将"hello world"写入HTTP响应写入器。
但是我发现客户端无法接收到"hello world",写入器一直被阻塞!
有人了解这个问题吗?
在我的命令行中运行的截图如下:
图片描述
英文:
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
答案1
得分: 1
for
循环是一个无限循环,所以向ResponseWriter
打印内容并不会被“调度”执行。如果你想要类似长轮询或者长连接的效果,你可以尝试使用这种方法。
在timer1()
函数中也存在泄漏的定时器问题。根据Go文档的说明:
> 停止定时器以释放相关资源。
每次调用go timer1()
时,你都会创建一个新的定时器,而且这个定时器从未关闭,所以每次创建新的定时器都会累积。
英文:
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.
答案2
得分: 1
#简短回答
避免缓冲
###客户端
使用带有--no-buffer
选项的curl命令
curl http://localhost:8080 --no-buffer
###服务器端
在每个fmt.Fprint
之后进行刷新
w.(http.Flusher).Flush()
#长回答
实现HTTP流式传输时最大的问题是理解缓冲的影响。缓冲是将读取或写入累积到一个临时固定内存空间的做法。缓冲的优点包括减少读取或写入调用的开销。例如,你可以一次性写入4096KB而不是4096次写入1KB。这意味着你的程序可以创建一个包含4096KB临时数据的写入缓冲区(可以对齐到磁盘块大小),一旦达到空间限制,缓冲区就会被刷新到磁盘上。
上述提到的HTTP
组件包括两个组件:服务器
(Go服务器)和客户端
(Curl)。每个组件都可以具有可调整和不同的缓冲样式和限制。
一个无关的问题,在给定的程序中还有一个问题,即不停止计时器会一直运行以释放相关资源。
这里是一种带有一些修正的实现
代码
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
英文:
#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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论