美文网首页
Go http 包详解

Go http 包详解

作者: rayjun | 来源:发表于2021-06-11 21:17 被阅读0次

    Go 语言中的 http 包提供了创建 http 服务或者访问 http 服务所需要的能力,不需要额外的依赖。在这篇文章中,我们会介绍这些功能的使用,以及看一下 http 包的设计思路。

    1. http 的客户端

    1.1 发送普通请求

    在 Go 语言中发送请求很简单,如果不需要额外的配置,可以直接使用 http 包封装的 http Client 发送请求,比如发送 GET 请求:

    resp, _ := http.Get("https://golang.org")
    defer resp.Body.Close()
    

    发送 POST ,并携带 JSON 数据的请求:

    data := make(map[string]string)
    dataJson, _ := json.Marshal(data)
    reader := bytes.NewBuffer(dataJson)
    
    resp, _ := http.Post("https://golang.org", "application/json;charset=utf-8", reader)
    defer resp.Body.Close()
    

    发送 POST 表单请求:

    resp, _ := http.PostForm("https://golang.org", url.Values{"username":{"rayjun"}, "password":{"password"}})
    defer resp.Body.Close()
    

    在每个请求发完之后,需要手动关闭响应。

    1.2 客户端配置

    在实际使用的过程中,我们通常不会直接上面的方法,而是会自己做一些 Client 的配置,比如调整超时时间:

    client := &http.Client{
        Timeout: 5 * time.Second,
    }
    resp, _ := client.Get("https://golang.org")
    defer resp.Body.Close()
    

    另外在很多时候,我们需要使用 GET 和 POST 之外的 http 方法,那就需要下面这样的配置:

    client := &http.Client{
        Timeout: 5 * time.Second,
    }
    
    req, _ := http.NewRequest("PUT", "https://golang.org", nil)
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    

    比如还需要在请求的 Header 中增加一些字段:

    client := &http.Client{
        Timeout: 5 * time.Second,
    }
    req, _ := http.NewRequest("GET", "https://golang.org", nil)
    req.Header.Add("User-Id", "userid123456")
    resp, _ := client.Do(req)
    defer resp.Body.Close()
    

    或者更进一步,我们需要自定义传输层的一些配置:

    tr := &http.Transport{
        MaxIdleConns:       10,
        IdleConnTimeout:    30 * time.Second,
        DisableCompression: true,
    }
    client := &http.Client{Transport: tr}
    resp, _ := client.Get("https://golang.org")
    defer resp.Body.Close()
    

    http 包中发送请求,提供了不同层次的配置,满足不同场景的使用。

    2. http 的服务端

    除了客户端,使用 http 包来创建 http 服务也很方便。

    2.1 一行代码创建 http 服务

    创建一个 http 服务,在 Go 代码中,只需要一行代码:

    func main() {
        http.ListenAndServe(":8080", nil)
    }
    

    在 main 方法中,写下上面那行代码,然后运行 main 方法,端口号为 8080 的 http 服务就运行起来了, 但目前还处理不了任何请求。

    2.2 添加请求路径

    在上面代码的基础上,需要添加一个路径,这样服务才可以开始处理请求:

    func main() {
      http.Handle("/index", &CustomerHandler{})
        http.ListenAndServe(":8080", nil)
    }
    
    type CustomerHandler struct {
    
    }
    
    func (c *CustomerHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
        fmt.Println("implement http server by self")
        writer.Write([]byte("server echo"))
    }
    

    添加了 /index 路径,在这种方式下,需要为每一个请求都定义一个 Handler,然后 Handler 需要实现 ServeHttp 方法。

    Handler 是一个请求处理器,我们如果使用这种方式,就需要为每一个请求的 url 实现一个 Handler,这样实现很繁琐。

    但我们还有另一个选择,就是使用 HandlerFunc,添加另外一个路径:

    func main() {
        http.HandleFunc("/index", func(writer http.ResponseWriter, request *http.Request) {
            writer.Write([]byte("HandleFunc implement"))
        })
        http.ListenAndServe(":8080", nil)
    }
    

    使用这种方式很简洁,值需要实现 HandlerFunc 类型的一个匿名方法就可以了,HandlerFunc 是一个适配器,可以让我们把一个与 ServeHTTP 签名相同的函数作为一个处理器。

    Handler 和 HandlerFunc 都是通过 DefaultServeMux 来实现的。 DefaultServeMux 才是上面服务的核心。

    在上面的代码,http.ListenAndServe 的第二个参数传入的是 nil,通常情况下,这个参数都是 nil,跟进代码,发现这个参数为 nil 的时候,就是使用 DefaultServeMux 来作为服务端的实现:

    // server.go
    func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
        handler := sh.srv.Handler
        if handler == nil {
            handler = DefaultServeMux
        }
        if req.RequestURI == "*" && req.Method == "OPTIONS" {
            handler = globalOptionsHandler{}
        }
        handler.ServeHTTP(rw, req)
    }
    

    DefaultServeMux 的类型是 ServeMux,这是 Go 语言原生包中 http 服务端的默认实现。ServeMux 同样实现了ServeHttp 这个方法。

    ServeHttp 方法才是整个 http 服务的核心,只要需要处理请求,就必须实现这个方法。Handler 和 HandlerFunc 只是 Go 语言提供的两种实现。

    3. http 的反向代理

    反向代理在开发 Web 应用,特别是开发网关类应用的时候会经常用到, Go 也提供了实现,基本上开箱即用。

    func main() {
        http.HandleFunc("/formawd", func(writer http.ResponseWriter, request *http.Request) {
            director := func(req *http.Request) {
                req.URL.Scheme = "https"
                req.URL.Host = "golang.org"
                req.URL.Path = "upload"
            }
    
            proxy := &httputil.ReverseProxy{Director: director}
            proxy.ServeHTTP(writer, request)
        })
        
        http.ListenAndServe(":8080", nil)
    }
    

    上面的代码会把所有的请求都转发到一个地方,当然也可以通过配置,将请求转发到不同的地方。

    4. 小结

    Go 语言原生的包就自带了 http 包,这个包提供 http 编程所需要的基础能力,开箱即用,不需要额外的依赖。在实际项目中使用,做个简单的封装即可。而且还自带反向代理的能力,可以很方便的写出一个 API 网关。

    文 / Rayjun

    相关文章

      网友评论

          本文标题:Go http 包详解

          本文链接:https://www.haomeiwen.com/subject/ljnveltx.html