初探

net/httphttp.HandleFunc()http.ListenAndServe()
 package main
 ​
 import (
     "fmt"
     "net/http"
 )
 ​
 func hello(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "Hello!")
 }
 ​
 func main() {
     http.HandleFunc("/hello", hello)
     http.ListenAndServe(":8080", nil)
 }
main()http.ListenAndServe(":8080“,nil)
http://localhost:8080/hello
ListenAndServe()

事件处理器的Handler接口定义如下:

 type Handler interface {
     ServeHTTP(ResponseWriter, *Request)
 }

只要实现了这个接口,就可以实现自己的handler处理器。Go语言在net/http包中已经实现了这个接口的公共方法:

 type HandlerFunc func(ResponseWriter, *Request)
 ​
 // ServeHTTP calls f(w, r).
 func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
     f(w, r)
 }
http://127.0.0.1:8080DefaultServeMux

多路复用器的概念在后面再进行详解。

用户可以通过Server结构体对服务器进行更详细的配置,包括设置地址,为请求读取操作设置超过时间等等。

 s := &http.Server{
     Addr:           ":8082",
     Handler:        myHandler,
     ReadTimeout:    10 * time.Second,
     WriteTimeout:   10 * time.Second,
     MaxHeaderBytes: 1 << 20,
 }

Go web服务器的请求和响应流程如下:


响应流程如下:

  1. 客户端发送请求
  2. 服务器的多路复用器收到请求
  3. 多路复用器根据请求的URL找到注册的处理器,将请求交由处理器处理
  4. 处理器执行程序逻辑,与数据库交互
  5. 调用模板引擎选择模板
  6. 服务器端将数据通过Http响应返回给客户端
  7. 客户端拿到数据呈现给用户

接受请求

ServeMux与DefaultServeMux

ServeHTTP()

DefaultServeMux是net/http包的默认多路复用器,其实就是ServeMux的一个实例。

 //Go source code
 var DefaultServeMux = &defaultServeMux
 var defaultServeMux ServeMux

HandleFunc()函数用于为指定的URL注册一个处理器。HandleFunc()处理器函数会在内部调用DefaultServeMux对象对应的方法,其内部实现:

 // HandleFunc registers the handler function for the given pattern
 // in the DefaultServeMux.
 // The documentation for ServeMux explains how patterns are matched.
 func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
     DefaultServeMux.HandleFunc(pattern, handler)
 }

由此看出,我们也可以使用默认多路复用器注册多个处理器,达到与处理器一样的作用。

总结

我们通过以上分析大概明白了如何通过默认多路复用器创建自己的服务器。下面我们来检验这一点:

 package main
 ​
 import (
     "fmt"
     "net/http"
 )
 ​
 //定义多个处理器
 type handle1 struct{}
 func (h1 *handle1) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "handle one")
 }
 ​
 type handle2 struct{}
 func (h2 *handle2) ServeHTTP(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "handle two")
 }
 ​
 func main() {
     handle1 := handle1{}
     handle2 := handle2{}
     //Handler:nil表明服务器使用默认的多路复用器DefaultServeMux
     s := &http.Server{
         Addr:    "127.0.0.1:8080",
         Handler: nil,
     }
     
     //Handle函数调用的是多路复用器DefaultServeMux.Handle方法
     http.Handle("/handle1", &handle1)
     http.Handle("/handle2", &handle2)
 ​
     s.ListenAndServe()
 }

我们通过使用自己的handle1和handle2来指定两个处理器,http.Handle()函数可以调用DefaultServeMux.Handle()方法来处理请求。

 func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }

服务器的每个请求都会调用对应的ServeHTTP方法。该方法在net/http包中定义如下:

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)
}
handle == nilHandle :nil;

在ServeMux对象的ServeHTTP()方法中,根据URL查找我们注册的服务器然后请求交给它处理。

虽然默认的多路复用器很好用,但仍然不推荐使用,因为它是一个全局变量,所有的代码都可以修改它。有些第三方库中可能与默认复用器产生冲突。所以推荐的做法是自定义。

自定义多路复用器

mux := http.NewServeMux()
mux.handleFunx("/",nil)

我来演示以下如何自定义多路复用器:

package main

import (
	"fmt"
	"net/http"
)

func newservemux(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "NewServeMux")
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/", newservemux)

	s := &http.Server{
		Addr:    ":8081",
		Handler: mux,
	}

	s.ListenAndServe()
}
Handle:mux
func NewServeMux() *ServeMux { return new(ServeMux) }

可以看到的是NewServeMux实质上还是ServeMux。

ServeMux的路由匹配

如果我们现在需要绑定三个URL分别为/,/happy,/bad。我们该如何做?与上面的代码类似

 package main
 ​
 import (
     "fmt"
     "net/http"
 )
 ​
 func newservemux(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "NewServeMux")
 }
 ​
 func newservemuxhappy(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "Newservemuxhappy")
 }
 ​
 func newservemuxbad(w http.ResponseWriter, r *http.Request) {
     fmt.Fprintf(w, "NewServeMuxbad")
 }
 func main() {
     mux := http.NewServeMux()
     mux.HandleFunc("/", newservemux)
     mux.HandleFunc("/happy", newservemuxhappy)
     mux.HandleFunc("/bad", newservemuxbad)
     s := &http.Server{
         Addr:    ":8080",
         Handler: mux,
     }
 ​
     s.ListenAndServe()
 }

HttpRouter包简介

ServeMux的一个缺陷是无法使用变量实现URL模式匹配。而HttpRouter可以,HttpRouter是一个高性能的第三方HTTP路由包,弥补了net/http包中的路由不足问题。

如何使用?

 go get  github.com/julienschmidt/httprouter

httprouter的使用首先得使用New()函数,生成一个*Router路由对象,然后使用GET(),方法去注册匹配的函数,最后再将这个参数传入http.ListenAndServe函数就可以监听服务。

 package main
 ​
 import (
     "net/http"
     "github.com/julienschmidt/httprouter"
 )
 ​
 func Hello(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
     w.Write([]byte("Hello,httprouter!"))
 }
 ​
 func main() {
     router := httprouter.New()
     router.GET("/", Hello)
     http.ListenAndServe(":8080", router)
 }

HttpRouter包为常用的HTTP方法提供了GET(),POST(),方法都提供了定义。

更为重要的是,它为URL提供了两种匹配模式:

/user/:pac 精准匹配 /user/pac
/user/*pac 匹配所有模式 /user/hello

包的地址提供了详情:

 package main
 ​
 import (
     "fmt"
     "net/http"
     "log"
 ​
     "github.com/julienschmidt/httprouter"
 )
 ​
 func Index(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
     fmt.Fprint(w, "Welcome!\n")
 }
 ​
 func Hello(w http.ResponseWriter, r *http.Request, ps httprouter.Params) {
     fmt.Fprintf(w, "hello, %s!\n", ps.ByName("name"))
 }
 ​
 func main() {
     router := httprouter.New()
     router.GET("/", Index)
     router.GET("/hello/:name", Hello)
 ​
     log.Fatal(http.ListenAndServe(":8080", router))
 }

当然这里还有POST(),DELETE()函数的详情,就不一一解释了。

以上大概就带大家了解了http包的所有内容,以及httprouter包为http所作的URL模式匹配的拓展

下篇文章带大家继续探索go语言的其他部分