Golang HTTP源码 笔记

Go HTTP源码的学习笔记

(参考自build-web-application-with-golang

假设有如下代码:

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.Fprintf(w, "Hello astaxie!") //这个写入到w的是输出到客户端的
}

func main() {
    http.HandleFunc("/", sayhelloName) //设置访问的路由
    err := http.ListenAndServe(":9090", nil) //设置监听的端口,第二个参数是路由,为空
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

我们从http.ListenAndServe()开始深入探索。

先给出HTTP包工作的的大致流程,如下

http.HandleFunc("/", sayhelloName) //设置访问的路由
http.ListenAndServe(":9090", nil)
    server := &Server{Addr: addr, Handler: handler} // addr即参数":9090",handler即参数nil
    server.ListenAndServe()
        ln, err := net.Listen("tcp", addr)  // 调用net包函数, Listen
        server.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
            for {
                rw, err := ln.Accept()   // TCP中的Accept
                c := server.newConn(rw)  // 建立新连接
                go c.serve()  // serve,这里的goroutine支持高并发
                    for {
                        w, err := c.readRequest()
                        serverHandler{c.server}.ServeHTTP(w, w.req)
                            // 这里令serverHandler{c.server}为sh吧
                            handler := sh.srv.Handler
                            if handler == nil {  // 因为本例传入的handler参数为nil
                                handler = DefaultServeMux   // 故调用默认路由,DefaultServeMux见下文
                            }
                            handler.ServeHTTP(rw, req)  // rw就是传入的参数w,req就是参数w.req
                    }
            }

Go是通过一个函数ListenAndServe来完成Web运作的,这个底层是这样处理的:初始化一个server对象,然后调用了net.Listen("tcp", addr),也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。

监控端口之后,调用了srv.Serve(net.Listener)函数,这个函数就是处理接收客户端的请求信息。简化后,关键代码如下:

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    for {                        // Listen之后,循环(Accept->newConn->serve)
        rw, err := l.Accept()    // Accept
        c, err := srv.newConn(rw)   // 创建Connection,这里的c保存了每次的请求信息
        go c.serve()             // 处理请求,相当于创建一个新线程(go routine),实现高并发
    }

}

若路由为空,则使用默认路由DefaultServeMux,我们来看下其定义

var DefaultServeMux = &defaultServeMux

var defaultServeMux ServeMux

DefaultServeMux是一个ServeMux类型

阅读ServeMux的注释,可以知道:

  • ServeMux是HTTP 请求的multiplexer. 它将每个请求的URL与注册过的路径(pattern)进行最长匹配。例如刚才有http.HandleFunc("/", sayhelloName), 则"/"是注册过的路径;当访问http://localhost/时, 路由器将会把该URL与"/"匹配。
  • 最长匹配是指,当有handlers分别注册了路径/repos/image//repos/时,当你访问/repos/image/开头的路径,前者handler会被调用,当你访问/image/时,后者handler才被调用。
  • 路径(pattern)最好都以/结尾(我这样理解)。因为,假设路径/image/被注册了,当你访问/image时(没有/结尾),路由器会帮你重定向至/image/路径,除非/image被注册过。
type ServeMux struct {
    mu    sync.RWMutex   //锁,由于请求涉及到并发处理,因此这里需要一个锁机制
    m     map[string]muxEntry  // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
    hosts bool // whether any patterns contain hostnames
}

其中muxEntry定义为:

type muxEntry struct {
    explicit bool   // 是否精确匹配;如果是,说明该pattern被注册过了,不能再注册
    h        Handler // 这个路由表达式对应哪个handler
    pattern  string  //匹配字符串
}

其中Handler定义为:

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)  // 路由实现器
}

可见其面向接口编程思想。

DefaultServeMuxhandler赋值的是http.HandleFunc("/", sayHelloName)这一函数。

http.HandleFunc()大致过程如下:

http.HandleFunc("/", sayhelloName)  // 设置访问的路由
    // 函数原型是HandleFunc(pattern string, handler func(ResponseWriter, *Request))
    // DefaultServeMux在包中已被声明
    DefaultServeMux.HandleFunc(pattern, handler)  // pattern="/", handler=sayhelloName
        DefaultServeMux.Handle(pattern, HandlerFunc(handler))  // 第二个参数是类型转换,见下文
            // 这里用mux代替DefaultServeMux
            mux.mu.Lock()  // 加锁
            if mux.m == nil {
                mux.m = make(map[string]muxEntry)
            }
            // 将pattern与函数匹配,在刚才的例子中,是将"/"和sayhelloName匹配
            mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
            ...

其中,HandlerFunc是一个类型声明

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

所以刚才DefaultServeMux.Handle(pattern, HandlerFunc(handler))是把handler(即sayhelloName)强制转换为HandlerFunc类型。
看回文章开头的流程,里面的handler.ServeHTTP(rw, req)就会调用到这个f(w, r)

待续

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值