思路:

  1. 理解fastcgi协议
  2. php-fmp开启监听127.0.0.1:9000,此时它是fastcgi的服务端
  3. golang实现的web服务器监听用户请求
  4. web服务器接收到用户的request请求后,把服务器信息和用户的request请求打包
  5. 把打包好的信息发送到fastcgi服务端(就是请求127.0.0.1:9000),此时web服务器相对来说是fastcgi的客户端
  6. web服务器把fastcgi服务器返回的信息重新打包,返回给用户

实现主要代码
fastcgi 客户端golang的实现包可以上网找,我自己用的是 github.com/tomasen/fcgi_client

// 设置环境变量
func getEnv(r *http.Request, c config.Conf) map[string]string {

    remoteAddr := strings.Split(r.RemoteAddr, ":")

    serverKey := c.ServerIndex[r.Host]
    config    := c.Server[serverKey]

    scriptName        := "/"+config.Index
    scriptFileName := config.Root+"/"+config.Index+r.URL.Path

    if path.Ext(r.URL.Path) == ".php" {
        scriptFileName = config.Root + r.URL.Path
        scriptName       = r.URL.Path
    }

    if strings.Contains(r.URL.Path, ".php") {
        scriptFileName = config.Root+r.URL.Path
    }

    env := make(map[string]string)

    // 设置request 的 header,这里如果没有设置,没办法把cookie带回来
    for header, values := range r.Header {
        env["HTTP_" + strings.Replace(strings.ToUpper(header), "-", "_", -1)] = values[0]
    }

    env["SERVER_SOFTWARE"]     = "go php server/1.0"

    env["REMOTE_ADDR"]         = remoteAddr[0]
    env["REMOTE_PORT"]         = remoteAddr[1]

    env["SERVER_PORT"]         = config.Listen
    env["SERVER_ADDR"]         = config.Addr
    env["DOCUMENT_ROOT"]         = config.Root

    env["SCRIPT_FILENAME"]     = scriptFileName
    env["SERVER_NAME"]         = r.Host
    env["SCRIPT_NAME"]         = scriptName // 脚本相对于根目录下完整路径
    env["QUERY_STRING"]         = r.URL.Query().Encode()
    env["REQUEST_URI"]          = r.URL.RequestURI()

    return env
}
// 设置响应信息
func setResponse(w http.ResponseWriter, resp *http.Response){

    for _, c := range resp.Cookies()  {
        http.SetCookie(w, c)
    }

    for k, v := range resp.Header {

        if k == "Set-Cookie" {
            continue
        }

        headerValue := ""
        for _, s := range v {
            headerValue = headerValue + s
        }

        w.Header().Set(k, headerValue)
    }

    if resp.StatusCode > 0 {
        w.WriteHeader(resp.StatusCode)
    }

    w.Header().Set("Connection", "keep-alive")
    w.Header().Set("Server", "go_php_server")

    content, err := ioutil.ReadAll(resp.Body)
    common.LogErr(err)

    io.WriteString(w, string(content))
}