使用通道的Golang handlefunc

9

我想这个问题以前可能已经被提过了(可能不止一次),但是我找不到了……

我正在学习Go语言,并且想要通过向“handler”发送一个通道来扩展经典的Web服务器示例。

我有以下标准示例:

func hello(w http.ResponseWriter, r *http.Request) {
   io.WriteString(w, "Hello world!")
}

func main() {
    http.HandleFunc("/", hello)
    http.ListenAndServe(":8000", nil)
}

现在我希望"hello"函数能够将内容写入到一个通道中,以便其他人可以消费… 我通常创建一个通道来实现“普通”函数的这个功能:

c := make(chan string) 

在函数调用时传递参数 c。类似下面这样:

dosomething(c)

但是,如果我想让“hello”访问通道c,该怎么做呢?
2个回答

23

除了前面的答案中导出通道的方法,还有另外两种方法可以做到这一点。

第一种方法是使用一个函数返回另一个处理程序函数。当函数被返回时,它将在通道周围创建一个闭包。

func makeHello(logger chan string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        logger <- r.Host
        io.WriteString(w, "Hello world!")
    }
}

第二种方法是使用一个结构体作为成员持有通道,然后使用指针接收器方法来处理请求...

type DataPasser struct {
    logs chan string
}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
    p.logs <- r.URL.String()
    io.WriteString(w, "Hello world")
}

这是一个完整的工作示例(只需点击/1和/2以查看两个示例)

package main

import (
    "fmt"
    "io"
    "net/http"
)

func main() {
    // METHOD 1
    logs := make(chan string)
    go logLogs(logs)
    handleHello := makeHello(logs)

    // METHOD 2
    passer := &DataPasser{logs: make(chan string)}
    go passer.log()

    http.HandleFunc("/1", handleHello)
    http.HandleFunc("/2", passer.handleHello)
    http.ListenAndServe(":9999", nil)
}

// METHOD 1

func makeHello(logger chan string) func(http.ResponseWriter, *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        logger <- r.Host
        io.WriteString(w, "Hello world!")
    }
}

func logLogs(logger chan string) {
    for item := range logger {
        fmt.Println("1. Item", item)
    }
}

// METHOD 2

type DataPasser struct {
    logs chan string
}

func (p *DataPasser) handleHello(w http.ResponseWriter, r *http.Request) {
    p.logs <- r.URL.String()
    io.WriteString(w, "Hello world")
}

func (p *DataPasser) log() {
    for item := range p.logs {
        fmt.Println("2. Item", item)
    }
}

1
非常感谢您提供的优秀示例。我最终采用了方法1,它完美地完成了工作。 - plithner
3
作为一个来自Python的人,我无法想象如何做出这样简单的事情。有趣的是,实际上有一个非常简单的解决方案来解决这个问题,所需的只是一种范式转换。 - Esser420
方法2不起作用。每次将通道添加到main()时,服务器会启动但无法工作。 可以通过使用结构体和接收器或全局变量chan来解决。 - Andy
@Andy上面的示例代码不起作用吗?我认为它很清楚地显示了方法2是有效的,但如果我错了,我很感兴趣知道。 - sberry
我最终也使用了第一种方法,它很有用。 - fakturk

2

解决这个问题有几种方法,最简单的方法是在一个包中定义一个导出的通道,并在需要使用该通道的任何地方导入该包。

package mychannel

var Chan = make(chan string)

非常感谢您的帮助!最终我使用了sberry提出的方法,但我仍然非常感激您的帮助! - plithner
这就像是“Go风格的单例模式”。 - Siu Ching Pong -Asuka Kenji-
@SiuChingPong-AsukaKenji- 不,只是一个全局变量。在Go中,您可以使用私有类型的公共构造函数创建单例,并返回该私有变量作为私有类型的实例,从而强制执行单例模式。不过,我不确定是否有人在Go中编写代码时会经常这样做。 - jmaloney

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接