用于了解 http 包的一些运行机制

首先一个简单的例子

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package main

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

func sayhelloName(w http.ResponseWriter, r *http.Request) {
  r.ParseForm()                         //解析参数,默认不解析
  fmt.Println(r.Form)                   //在终端中打印出表单内容
  fmt.Println("Path", r.URL.Path)
  fmt.Println("scheme", r.URL.Scheme)
  fmt.Println(r.Form["url_long"])       //输出指定的参数
  for k,v := range r.Form{              //遍历打印出表单的值
    fmt.Println("key",k)
    fmt.Println("val", strings.Join(v, ""))
  }
  fmt.Fprint(w, "hello gpf!")
}


func main(){
  http.HandleFunc("/", sayhelloName)        //绑定路由与方法
  err := http.ListenAndServe(":9090", nil)  //监听 tcp:9090 端口
  if err != nil {
    log.Fatal("ListenAndServe: ",err)
  }
}

文件目录下 go build http.go ./http 再直接访问http://localhost:9090就能看到页面了

运行流程是什么呢?

大体的运行流程是介样:

  1. 创建 Listen Socket,监听 tcp 协议的 9090 端口,等待请求
  2. 收到请求后创建一个 client socket 去解析请求(request)
  3. 将请求内容交给 handle request 去处理,处理后返回给 client socket(handler)
  4. client socket 收到后台的处理结果返回给用户(response)

我们80%的逻辑都是在第3步内进行的,其他的已经有底层包帮我们实现了

我们的路由绑定机制是怎么实现的?

当走到以上的第3步的时候将会进入自己独立的 goroutine ,这也是用户访问互不影响的原因, 这一点和 nginx 很像,都是异步非阻塞的处理方式用来应对高并发

在最开始的例子当中我们绑定路由是直接将路由和函数指定了的,这里面发生了什么呢? 当调用

1
http.HandleFunc("/", sayhelloName)   

时 因为没有对监听函数传递第二个参数,传了个 nil 进去

1
http.ListenAndServe(":9090", nil)

nil 的位置本应该是一个 handler,如果为空的话,将调用默认的 handler ,即 DefaultServeMux, 因为它实现了 Handler 接口所以能处理请求, Handler接口的内容是

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

这个方法就是我们的路由实现器,我们所有的路由指向的方法都是在这里处理的,

可是为什么我的sayhelloName()并没有实现 Handler的接口但还是正常运行了,这怎么回事呢? 这是因为 http.HandleFunc()帮你完成了这个接口的实现,文档

自己实现一个简易的路由

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

package main

import (
	"fmt"
	"net/http"
)

//设定一个空的接口体来承载接口
type MyMux struct{}

//实现 Handler 接口,在这里处理路由相关的内容
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	if r.URL.Path == "/" {
		sayHello(w, r)
		return
	}

	if r.URL.Path == "/login" {
		loginPage(w, r)
		return
	}

	http.NotFound(w, r)
	return
}

func sayHello(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "Hello route '/' !")
}

func loginPage(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "this is login page")
}

func main() {
  //实例化结构体
	mux := &MyMux{}
  //当做 handler 注入到监听服务当中去
	http.ListenAndServe(":9090", mux)
}

如果想自己实现路由的处理也可以这样试试,但是我不想平白的用一个空的 struct 去承载这个啊, 我们可以试试这个

ServeMux

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
package main

import (
	"fmt"
	"net/http"
)

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "this is login page")
	})

	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "hello your first page")
	})

	http.ListenAndServe(":9090", mux)
}

说了这么多其实还是云里雾里的,不过没关系,可以先放在这里,等用的多了自然也能想开了