【问题标题】:Usage of global variables with Http handlers in Golang在 Golang 中使用全局变量和 Http 处理程序
【发布时间】:2016-08-13 18:29:25
【问题描述】:

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

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

处理程序类似于:

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

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

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

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

提前致谢。


编辑

为了更清楚。例如,在我的 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]
}

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

【问题讨论】:

  • 您认识到您在不同调用之间修改了变量这一事实表明这是设计。听起来你想要某种容器。您需要设计一个结构以满足您的需求,并将该结构传递给 somepackage 中对您的函数的调用。
  • 是的,这听起来不错,或者为整个包创建一个结构并让它包含变量。但是堆接口中的像 Less(i, j int) 这样的函数呢?这些将需要访问该结构,我看不到将其传递给他们的方法...
  • 查看频道chan,他们解决了很多与共享内存相关的问题。
  • 你不需要传递函数。您将定义一个实现排序功能的结构类型 。然后你可以在任何你需要的地方做 foo.Sort() 。 @jcasado94 如果您可以提供您正在使用的数据类型,这可能会帮助我们提出一个好的解决方案。查看stackoverflow.com/a/28999886/4746361 开始使用。
  • 这个包基本上运行了一些寻路算法,并且必须用路径来回答。因此变量主要是实现容器/堆接口的优先级队列和开/关顶点的映射等,当然还有一个Graph struct...

标签: go global-variables httphandler


【解决方案1】:

当您的处理程序需要一个变量时,通常这意味着您应该实现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"))
}

【讨论】:

  • 这正是我所缺少的。处理程序作为封装变量的结构。谢谢,现在要努力了! ;)
  • 抱歉,快速提问。我已经设法将所有函数添加到结构中以在其中实现容器/堆优先级队列(Len、Push、Pop ......),并且它适用于一个。但是如果我需要在同一个结构中实现两个或多个容器/堆对象怎么办?如何为所有这些功能定义这些功能?谢谢。
  • 在另一个结构中嵌入一个结构。
  • 很好的答案和例子!但是,在我的 go1.12.5 中,它给出了错误 "cannot use &specificHandler literal (type *specificHandler) as type func(http.ResponseWriter, *http.Request) in argument to http.HandleFunc"。如何让它再次工作?
  • 如果您提供的结构体实现了http.Handler,请使用http.Handle 而不是http.HandleFunc
【解决方案2】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-04-29
    • 1970-01-01
    • 2021-07-14
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    • 2022-01-23
    • 1970-01-01
    相关资源
    最近更新 更多