英文:
Why this simple web server is called even number times?
问题
我正在尝试学习Go Web编程,这是一个简单的Web服务器:它会打印出被调用的次数。
package main
import (
"fmt"
"net/http"
)
var calls int
// HelloWorld 打印被调用的次数。
func HelloWorld(w http.ResponseWriter, r *http.Request){
calls++
fmt.Fprintf(w, "You've called me %d times", calls)
}
func main() {
fmt.Printf("Started server at http://localhost%v.\n", 5000)
http.HandleFunc("/", HelloWorld)
http.ListenAndServe(":5000", nil)
}
当我刷新页面时,我得到了:
You've called me 1 times
You've called me 3 times
You've called me 5 times
....
问题:为什么是1、3、5次,而不是1、2、3...?函数HelloWorld
被调用的顺序是什么?
英文:
I'm trying to learn Go web programming, and here is a simple web server: it prints out the times being called.
package main
import (
"fmt"
"net/http"
)
var calls int
// HelloWorld print the times being called.
func HelloWorld(w http.ResponseWriter, r *http.Request){
calls++
fmt.Fprintf(w, "You've called me %d times", calls)
}
func main() {
fmt.Printf("Started server at http://localhost%v.\n", 5000)
http.HandleFunc("/", HelloWorld)
http.ListenAndServe(":5000", nil)
}
When I refresh the page, I got:
You've called me 1 times
You've called me 3 times
You've called me 5 times
....
Question: Why it is 1, 3, 5 times, rather than 1,2,3...? What's the order of the function HelloWorld
being called?
答案1
得分: 14
这是翻译好的内容:
这是因为每个传入的请求都会路由到你的HelloWorld()
处理函数,并且浏览器在幕后进行多次调用,特别是对/favicon.ico
的调用。
由于你的Web服务器没有返回有效的favicon,所以当你在浏览器中刷新页面时,它会再次请求它。
在Chrome中尝试一下:打开开发者工具(CTRL+SHIFT+I),选择"Network"选项卡。点击刷新,你会看到2个新条目:
名称 状态 类型
--------------------------------------------------------
localhost 200 document
favicon.ico 200 text/plain
由于你的计数器从0
开始(类型int
的默认值),你将它增加一次并发送回1
。然后对于favicon.ico
的请求再次增加它(2
),但结果不会显示。然后如果你刷新,它会再次增加到3
并发送回去,依此类推。
还要注意,多个goroutine可以同时处理请求,所以你的解决方案存在竞争条件。你应该同步访问calls
变量,或者使用sync/atomic
包安全地增加计数器,例如:
var calls int64
func HelloWorld(w http.ResponseWriter, r *http.Request) {
count := atomic.AddInt64(&calls, 1)
fmt.Fprintf(w, "你已经调用了我%d次", count)
}
一个简单的“修复”方法是检查请求路径,如果不是根路径"/"
,就不增加计数,例如:
func HelloWorld(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
return
}
count := atomic.AddInt64(&calls, 1)
fmt.Fprintf(w, "你已经调用了我%d次", count)
}
你也可以选择仅排除对favicon.ico
的请求,例如:
if r.URL.Path == "/favicon.ico" {
return
}
英文:
It is because every incoming request is routed to your HelloWorld()
handler function, and the browser makes multiple calls under the hood, specifically to /favicon.ico
.
And since your web server does not send back a valid favicon, it will request it again when you refresh the page in the browser.
Try it with Chrome: open the Developer tools (<kbd>CTRL+SHIFT+I</kbd>), and choose the "Network" tab. Hit refresh, and you will see 2 new entries:
Name Status Type
--------------------------------------------------------
localhost 200 document
favicon.ico 200 text/plain
Since your counter starts with 0
(default value for type int
), you increment it once and you send back 1
. Then the request for favicon.ico
increments it again (2
), but the result is not displayed. Then if you refresh, it gets incremented again to 3
and you send that back, etc.
Also note that multiple goroutines can serve requests concurrently, so your solution has a race. You should synchronize access to the calls
variable, or use the sync/atomic
package to increment the counter safely, for example:
var calls int64
func HelloWorld(w http.ResponseWriter, r *http.Request) {
count := atomic.AddInt64(&calls, 1)
fmt.Fprintf(w, "You've called me %d times", count)
}
A simple "fix" to achieve what you want would be to check the request path, and if it is not the root "/"
, don't increment, e.g.:
func HelloWorld(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
return
}
count := atomic.AddInt64(&calls, 1)
fmt.Fprintf(w, "You've called me %d times", count)
}
You may also choose to only exclude requests for favicon.ico
, e.g.:
if r.URL.Path == "/favicon.ico" {
return
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论