likes
comments
collection
share

初识 Go 语言 Web 服务

作者站长头像
站长
· 阅读数 16

Web Server 中 HandlerFunc, Handler, HandleFunc, Handle 理解

相信很多小伙伴在初学习Go Web服务时,都会遇到这么一个懵逼的问题?如图:

 初识 Go 语言 Web 服务 这其中的 HandleFunc, Handle, HandlerFunc, Handler 究竟是什么玩意?尤其是在学习别人代码时,一会使用 HandleFunc,一会又是Handle,一下子就分不清谁是谁了。

先从一个简单的例子说起代码如下:

package main
import (
    "fmt"
    "log"
    "net/http"
)
type CustomHandler struct{}
func (CustomHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hi")
}
func main() {
    err := http.ListenAndServe("0.0.0.0:9527", new(CustomHandler))
    if err != nil {
            log.Fatal(err)
    }
}

启动一个Web服务器的核心就是执行http.ListenAndServe方法,顺着这条藤去摸一摸它的瓜。 在 http.ListenAndServe(addr string, handler http.Handler) error 方法签名中 http.Handler 是一个接口,那么主要实现这个接口就能创建一个简单 web server,因此我创建了CustomHandler struct。(当然自定义其他类型也行,实现ServeHTTP接口即可)。

  • 因此 http.Handler 是一个接口

那再来看看 http.HandlerFunc, 它的定义是这样的

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
// 方法签名,用于适配 func(ResponseWriter, *Request)此类参数方法
type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
// HandlerFunc 实现 http.Handler 接口
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

由它的定义得知 HandlerFunc 是一个自定义的方法签名,它也实现了ServeHTTP接口,其实HandlerFunc就是为了将 func(ResponseWriter, *Request) 此类方法转换成HandlerFunc类型,从而实现http.Handler接口;我用自定义String类型来举一个栗子:

package main
import (
    "fmt"
)
type MyString string
func (s MyString) Say() {
    fmt.Println("say: " + s)
}
func main() {
    var hello = "Hello World!"
    var newHello = MyString(hello)
    newHello.Say() // 输出 "Hello World!"
}

如果看懂上面的例子,那么 http.HandlerFunc 也就懂了。

  • 因此http.HandlerFunc是为了把常规函数转换成 http.Handler

接下来解析http.Handlehttp.HandleFunc, 上面第一个例子在启动过web server 时,执行的是http.ListenAndServe("0.0.0.0:9527", new(CustomHandler))方法,其中第二参数是有值的;而另一种情况是第二参数传nil的场景,当第二个参数为nil的时候,会使用http.DefaultServeMux这个默认处理程序,其实http.ListenAndServe注释也说的很清楚。 其中 http.Handlehttp.HandleFunc 就是围绕http.DefaultServeMux设计的。

// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
// 解析:
// Handle在DefaultServeMux中注册给定模式的处理程序。
// ServeMux的文档解释了如何匹配模式。
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
// 解析
// HandleFunc在DefaultServeMux中为给定模式注册处理函数。
// ServeMux的文档解释了如何匹配模式。
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
	DefaultServeMux.HandleFunc(pattern, handler)
}

因此 http.Handlehttp.HandleFunc 是两个注册路由函数,它给谁注册呢?给http.DefaultServeMux; http.DefaultServeMux是谁的默认实例呢?是http.ServeMux

如果我们不想像第一个例子那样自定义实现 http.Handler,就是使用默认的http.DefaultServeMux,下面是一个例子:

package main

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

func main() {
    http.Handle("/ping", http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "Pong")
    }))

    http.HandleFunc("/pong", func(w http.ResponseWriter, r *http.Request) {
            fmt.Printf("Ping")
    })

    if err := http.ListenAndServe("0.0.0.0:9527", nil); err != nil {
            log.Fatal(err)
    }
}
  • 阶段总结: 在 go 中启动一个 web server 非常简单,仅执行 http.ListenAndServe(addr string, handler http.Handler) 函数即可,其中 http.handler 是一个接口,在Go中,当函数的参数类型为接口时,可以传递 nil 值作为参数;因此就有两种可能:
    • 要么实现http.handler接口
        1. 自定义实现http.Handler接口,第三方HTTP框架或路由器都是自定义实现 http.handler
        1. 使用ServeMux创建一个实例var mux := http.NewServeMux()
    • 要么使用http.ServeMux默认实例DefaultServeMux

了解一下 http.Server

上面说到通过执行 http.ListenAndServe 启动一个Web服务,最终执行的是http.ServerListenAndServe方法。

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {
    if srv.shuttingDown() {
            return ErrServerClosed
    }
    addr := srv.Addr
    if addr == "" {
            addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
            return err
    }
    return srv.Serve(ln)
}

看到上面的 net.Listen("tcp", addr) 就可以知道 Go Web Server 最终还是使用 tcp 实现。