为什么这个简单的网络服务器被称为偶数次?

huangapple go评论90阅读模式
英文:

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(&amp;calls, 1)
	fmt.Fprintf(w, &quot;You&#39;ve called me %d times&quot;, count)
}

A simple "fix" to achieve what you want would be to check the request path, and if it is not the root &quot;/&quot;, don't increment, e.g.:

func HelloWorld(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path != &quot;/&quot; {
		return
	}
	count := atomic.AddInt64(&amp;calls, 1)
	fmt.Fprintf(w, &quot;You&#39;ve called me %d times&quot;, count)
}

You may also choose to only exclude requests for favicon.ico, e.g.:

if r.URL.Path == &quot;/favicon.ico&quot; {
	return
}

huangapple
  • 本文由 发表于 2016年2月22日 17:39:00
  • 转载请务必保留本文链接:https://go.coder-hub.com/35550189.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定