一尘不染

为什么将此简单的Web服务器称为偶数次?

go

我正在尝试学习Go Web编程,这是一个简单的Web服务器:它可以打印出被调用的时间。

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)
}

刷新页面时,我得到:

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的调用顺序是什么?


阅读 252

收藏
2020-07-02

共1个答案

一尘不染

这是因为每个传入请求都被路由到您的HelloWorld()处理函数,并且浏览器在后台进行了多次调用,尤其是/favicon.ico

并且由于您的Web服务器没有发送回有效的网站图标,因此当您在浏览器中刷新页面时,它将再次请求它。

在Chrome上尝试:打开开发人员工具(CTRL+SHIFT+I),然后选择“网络”标签。点击刷新,您将看到2个新条目:

Name          Status   Type
--------------------------------------------------------
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, "You've called me %d times", count)
}

一个简单的“修复”来实现您想要的,将是检查请求路径,如果它不是root "/",请不要增加,例如:

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)
}

您也可以选择仅排除对的请求favicon.ico,例如:

if r.URL.Path == "/favicon.ico" {
    return
}
2020-07-02