GO Web基础| 青训营笔记

72 阅读2分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的第4篇笔记。

Web 基础

Go 搭建一个 Web 服务器

Go 搭建一个 Web 服务器

使用 http 包可以轻松的搭建一个 Web 服务器:

  • http.HandleFunc 函数:用于绑定路由。
  • http.ListenAndServe 函数:设置监听的端口。
package main
​
import (
    "fmt"
    "log"
    "net/http"
    "strings"
)
​
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)
​
    for k, v := range r.Form {
        fmt.Println("key:", k)
        fmt.Println("val:", strings.Join(v, ""))
    }
​
    fmt.Fprintf(w, "Hello astaxie!") // 这个写入到 w 的是输出到客户端的
}
​
func main() {
    http.HandleFunc("/", sayHelloName)       // 设置路由
    err := http.ListenAndServe(":9000", nil) // 设置监听端口
    if err != nil {
        log.Fatal(err)
    }
}

Go 中 Web 的工作方式

以下是服务器端的几个基本概念:

  • Request :用户的请求信息。
  • Response :服务器需要反馈给客户端的信息。
  • Conn :用户每次请求的链接。
  • Handler :处理请求和生成返回信息的处理逻辑。

http 包运行机制

Go 实现 Web 服务的工作模式如下:

  1. 创建 Listen Socket,监听指定的端口,等待客户端请求到来。
  2. Listen Socket 接收客户端的请求,得到 Client Socket,通过Client Socket 与客户端进行通信。
  3. 处理客户端的请求,交给 Handler进行处理,Handler 处理好数据之后通过 Client Socket 返回给客户端。

这整个的过程里面我们只要了解清楚下面三个问题,也就知道 Go 是如何让 Web 运行起来了:

  • 如何监听端口?

通过 http.ListenAndServe 函数,可以监听端口并分配handler,底层实现如下:初始化一个 server 对象,然后调用了 net.Listen("tcp", addr),也就是底层用 TCP 协议搭建了一个服务,然后监控我们设置的端口。

  • 如何接收客户端请求?

以下来自 http 包的源码,可以看到整个 http 的处理过程。

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        go c.serve()
    }
}

在监听完端口过后,调用了 srv.Serve(net.Listener) 方法,这个方法就是处理客户端的请求信息。在 Accept 之后,创建一个 Conn,然后开启一个协程来单独处理来自客户端的 http 请求:go c.serve()

  • 如何分配 handler?

Conn 首先会解析 request:c.readRequest(),然后获取相应的 handler:handler := c.server.Handerc.server.Handler 即为 http.ListenAndServe 的第二个参数;若为,则默认获取 handler = DefaultServeMux

DefaultServeMux 实际上是一个路由器,它用来匹配 url 跳转到其相应的 handle 函数,通过 http.HandleFunc 函数设置。DefaultServeMux 会调用 ServeHTTP方法,这个方法内部调用了 http.HandleFunc 函数定义的 handler。