在 Golang 中使用全局变量和 Http 处理程序

Usage of global variables with Http handlers in Golang

我知道有一些问题 posts/articles 关于这个问题,但从我的新手角度来看,不完全是。 问题是,我有一个主程序侦听端口并将调用重定向到特定的处理程序。典型结构:

func main() {
    http.HandleFunc("/something", specificHandler)
    http.ListenAndServe(":8080", nil)
}

处理程序类似于:

func specificHandler(w http.ResponseWriter, r *http.Request) {
    somepackage.foo()
}

然后 somepackage,其中包含函数 foo,有一些全局变量,主要是因为函数共享需要它们(例如,当使用使用 container/heap 实现的优先级队列时,它将从全局距离矩阵中获取 Swap 函数中的优先级,这当然是可变的)。还有很多其他的例子。总之,全局变量...

问题是,如您所见,这些变量在对处理程序的所有调用之间共享。这很糟糕。

我该如何真正解决这个问题?一定有一种我还没有做到的简单方法,因为它看起来很平常......

提前致谢。


编辑

为了更清楚。例如,在我的 A* 包中,我有以下全局变量:

var openVerticesAS PriorityQueueAStar

// which vertices are closed
var closedVertices map[int]bool

// which vertices are currently open
var openVertices map[int]bool

// cost from start to node
var gScore map[int]float64

// cost from start to end, passing by node i (g+h)
var fScore map[int]float64

那么,PriorityQueueAStar实现如下:

type PriorityQueueAStar []int // rel id

func (pq PriorityQueueAStar) Len() int { return len(pq) }

func (pq PriorityQueueAStar) Empty() bool { return len(pq) == 0 }

func (pq PriorityQueueAStar) Less(i, j int) bool {
    return fScore[pq[i]] < fScore[pq[j]]
}

func (pq PriorityQueueAStar) Swap(i, j int) {
    pq[i], pq[j] = pq[j], pq[i]
}

func (pq *PriorityQueueAStar) Push(x interface{}) {
    *pq = append(*pq, x.(int))
}

func (pq *PriorityQueueAStar) Pop() interface{} {
    old := *pq
    n := len(old)
    rel := old[n-1]
    *pq = old[0 : n-1]
    return rel
}

func (pq PriorityQueueAStar) Top() interface{} {
    return pq[0]
}

那么问题是,我如何在不将所有这些地图都作为全局变量的情况下继续这样做?如果它们是结构的一部分,我如何从优先级队列函数访问结构?

当您的处理程序需要一个变量时,通常这意味着您应该实现 Handler 接口而不是提供 HandlerFunc 函数。

这是一个糟糕的例子(使用全局变量):

var globalThing string

func specificHandler(w http.ResponseWriter, r *http.Request) {
    w.Write(globalConfigThing)
}

func main() {
    globalThing = "Hello world!"
    http.HandleFunc("/something", specificHandler)
    http.ListenAndServe(":8080", nil)
}

这是一个更好的例子(不使用全局变量):

type specificHandler struct {
    Thing string
}

func (h *specificHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    w.Write(h.Thing)
}

func main() {
    http.Handle("/something", &specificHandler{Thing: "Hello world!"})
    http.ListenAndServe(":8080", nil)
}

可以看到,一个Handler可以封装变量


为了完整性,另一种方法是使用函数闭包。这适用于一次性处理程序,但不可重复使用,并且更难为其编写单元测试。

func main() {
    scopedThing := "Hello world!"
    http.HandleFunc("/something", func (w http.ResponseWriter, r *http.Request) {
        w.Write(scopedThing)
    })
    http.ListenAndServe(":8080", nil)
}

正确完成,您现在可以通过将全局变量作为参数传递等方式避免包中出现全局变量 somepackage

编辑: 例如,您可以使用 somepackage 包中的几个 PriorityQueueAStar 字段定义处理程序结构:

type specificHandler struct {
    QueueA somepackage.PriorityQueueAStar
    QueueB somepackage.PriorityQueueAStar
    QueueC somepackage.PriorityQueueAStar
}

func (h *specificHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    h.QueueA.Push(h.QueueB.Pop)
    h.QueueB.Push(h.QueueC.Pop)
    w.Write([]byte("Queues pushed and popped"))
}

chowey 提到了闭包方法,但需要注意的是它不可测试或不可重用。如果你有一个 return 闭包的函数,它实际上是可测试和可重用的。对于某些类型的数据,这可能有助于更清晰的抽象和实现:

func handleThing(thing string) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        w.Write(thing)
    }
}

func main() {
    http.HandleFunc("/something", handleThing("Hello world!"))
    http.ListenAndServe(":8080", nil)
}