为啥这个简单的 Web 服务器会被调用偶数次?

Posted

技术标签:

【中文标题】为啥这个简单的 Web 服务器会被调用偶数次?【英文标题】:Why this simple web server is called even number times?为什么这个简单的 Web 服务器会被调用偶数次? 【发布时间】:2016-06-03 16:12:52 【问题描述】:

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

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

【问题讨论】:

打印请求 uri 以查看从您的浏览器发送的其他请求。 HandleFunc being called twice的可能重复 【参考方案1】:

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

由于您的网络服务器没有发回有效的网站图标,当您在浏览器中刷新页面时,它会再次请求它。

用 Chrome 试试:打开开发者工具 (CTRL+SHIFT+I),然后选择“网络”选项卡。点击刷新,您将看到 2 个新条目:

Name          Status   Type
--------------------------------------------------------
localhost     200      document
favicon.ico   200      text/plain

由于您的计数器以0int 类型的默认值)开头,因此您将其递增一次并返回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)

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

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

【讨论】:

以上是关于为啥这个简单的 Web 服务器会被调用偶数次?的主要内容,如果未能解决你的问题,请参考以下文章

为啥使用有状态的 Web 服务是不好的编程,为啥会被允许?

《程序员代码面试指南》第七章 位运算 在其他数都出现偶数次的数组中找到出现奇数次的数

一个布尔递归函数,用于判断一个数字是不是在整数中出现偶数次

^运算的运用

^运算的运用

^运算的运用