Go-Http

用于了解 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)
}

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