在 Go + FastCGI 中,使用多个处理程序有意义吗?

In Go + FastCGI, does it make any sense to use multiple handlers?

这里是 Gopher 新手。请善待:-)

我有一个设置,我在共享服务器上有一个帐户,运行s Apache + FastCGI 我无法控制。不过,它与 Go 的接口很流畅。我更习惯将 Go 与 net/http 一起使用,但弄清楚如何将它与 net/http/fcgi 一起使用似乎很简单。这是我的最小测试应用程序:

package main

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

func handler(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-type", "text/plain; charset=utf-8")
    fmt.Fprintln(w, "This was generated by Go running as a FastCGI app")
}

func main() {
    /*
     *  
     *  Everything that is done here should be setup code etc. which is retained between calls
     *  
     */
    http.HandleFunc("/", handler)
    // This is what actually concurrently handles requests
    if err := fcgi.Serve(nil, nil); err != nil {
            panic(err)
    }
}

现在它可以完美无瑕地工作了:将它编译成 go-fcgi-test.fcgi 并将其放在适当的目录下后,Go 代码是 运行 来自 URL 之类的 http://my.shared.web.server/go-fcgi-test.fcgi。为了简单起见,我省略了大部分实际处理——但这与提取表单参数、ENV 变量(在 Go 1.9 下!)等等完美配合,所以我知道基本设置一定没问题。

让我们尝试一个稍微复杂一点的例子:

package main

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

func handler1(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-type", "text/plain; charset=utf-8")
    fmt.Fprintln(w, "This comes from handler1")
}

func handler2(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-type", "text/plain; charset=utf-8")
    fmt.Fprintln(w, "This comes from handler2")
}

func main() {
    http.HandleFunc("/first", handler1)
    http.HandleFunc("/", handler2)
    if err := fcgi.Serve(nil, nil); err != nil {
            panic(err)
    }
}

现在,在这种情况下,我希望 http://my.shared.web.server/go-fcgi-test.fcgi 输出 This comes from handler2,事实上,这正是发生的情况。

但为什么 http://my.shared.web.server/go-fcgi-test.fcgi/first 实际上也调用了 handler2,即 handler1 被完全忽略了?请注意 handler2 确实 得到了 URL 的 /first 位 — Apache 没有 剥离它— 因为我可以阅读 r.URL.Path[1:] 并确认这是发送到 Go 应用程序的整个路径。

我在 Web 上找到的所有使用 FastCGI 类似框架的示例仅显示 一个 处理程序。这是 FastCGI 包本身的限制吗? FastCGI 协议的限制(但为什么整个路径都正确发送?)?在施加此限制的 Apache 配置上做了什么(记住,我不能触及 Apache 配置)?还是我做错了什么?

(为了完整起见,我应该补充一点,是的,我已经尝试了上述的几种变体,重命名 Go 应用程序,使用子文件夹,使用 几个 处理程序和不只是一个,等等)

我的真实场景实际上是一个小型应用程序,它应该 运行 作为独立的 Web 服务器使用 net/http 作为FastCGI 应用程序在单机模型不被支持甚至被禁止的情况下(某些共享环境提供商就是这种情况)。由于这两种情况的实际处理完全相同,唯一的区别是调用 fcgi.Serve() 而不是 http.ListenAndServe()。但是,如果能够在 FastCGI 下使用具有不同处理程序的 net/http 包的路由功能,那就太好了。

提前感谢您的任何见解。即使答案是“是的,这就是 确切地 FastCGI 实现在 Go 下的工作方式——只有一个处理程序!”这仍然有用——这意味着我只需要解决我自己的代码并以不同的方式做事(基本上,根据通过 Form 接口传递的参数创建我自己的 router/dispatcher——没什么大不了的,这是可行的!)

我意识到这是一个老问题 post,但我自己刚开始使用 Go 和 fcgi 时遇到了同样的问题。

简短的回答是肯定的,使用多个处理程序确实有意义。你的例子中的缺陷是你没有考虑 go-fcgi-test.fcgi 是你的 URL.

的一部分

当 Go 的 ServeMux 处理 URL 时,它使用请求的完整 URL,而不仅仅是 fcgi 进程正在处理的部分。在 http://my.shared.web.server/go-fcgi-test.fcgi/first 的情况下,程序正在寻找最接近 /go-fcgi-test.fcgi/first 的匹配项,即 /.

看完@Kodiak 提供的答案后,我重新阅读了 documentation for ServeMux 并看到了这段话:

Note that since a pattern ending in a slash names a rooted subtree, the pattern "/" matches all paths not matched by other registered patterns, not just the URL with Path == "/".

If a subtree has been registered and a request is received naming the subtree root without its trailing slash, ServeMux redirects that request to the subtree root (adding the trailing slash). This behavior can be overridden with a separate registration for the path without the trailing slash. For example, registering "/images/" causes ServeMux to redirect a request for "/images" to "/images/", unless "/images" has been registered separately.

(斜体是我的)

我的 假设ServeMuxnginx and/or Apache [=12] 的基于规则的模式匹配功能非常相似=] 模块,我。 e.规则根据它们注册的顺序进行处理,因此我期望 /first 会首先匹配(双关语意),只有在没有找到匹配项时,才会匹配 / 下一个.

但这不是文档所说的。相反,注册的顺序并没有真正的区别。 ServeMux,在我给定的场景中,因为我忘记添加尾部斜线,将 always 'fall back' 到 "/" 的处理程序,而不是因为一些错误或匹配算法的变态,但因为这是 Go 开发人员编码的预期行为!换句话说,如果您有一个 "/" 的处理程序,它可以作为每个非斜杠终止子树的包罗万象。

我只是没有正确阅读文档! (或者也许在 2017 年那段话还不够清楚)