随笔 - 21  文章 - 0  评论 - 0  阅读 - 6131

go http路由处理流程

(1)type Handler

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

该接口用于开发者能够实现自己的Handler,只要实现ServeHTTP(ResponseWriter, *Request)方法即可

实现了ServeHTTP方法的结构都能够称之为handler对象,ServeMux会使用handler(如它的函数func (*ServeMux) Handle)并调用其ServeHTTP方法处理请求并返回响应。 

(2)type HandlerFunc

type HandlerFunc func(ResponseWriter, *Request)

HandlerFunc 实现了ServeHTTP(w http.ResponseWrite, r *http.Request),因此可以将我们自己实现

func(w http.ResponseWrite, r *http.Request)类型的函数转化为实现了ServeHTTP的类型函数。

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

而ServeHTTP其实是调用f本身。

(3)type ServeMux--多路复用器

type ServeMux struct {
    mu    sync.RWMutex //锁,由于请求涉及到并发处理,因此这里需要一个锁机制
    m     map[string]muxEntry //路由规则,一个string对应一个mux实体,这里的string就是注册的路由
    hosts bool // whether any patterns contain hostnames
}
type muxEntry struct{
    explicit bool //是否精确匹配
    h          Handler //这个路由表达式对应哪个handler
}

ServeMux为什么被称为多路复用器,其实是一个分发器,ServeMux也实现了ServeHTTP,只不过它的作用是找到收到的请求对应的处理函数。

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)

举个例子:

请求来了-->被ServeMux收到-->ServeMux调用它实现的ServeHTTP函数来找到这个请求对应的处理函数(也就是在ServeMux的结构体成员map[string]muxEntry中找到-->然后调用这个处理函数。

func NewServeMux

func NewServeMux() *ServeMux  //NewServeMux创建并返回一个新的*ServeMux
var DefaultServeMux = NewServeMux()  //DefaultServeMux是用于Serve的默认ServeMux。

func (*ServeMux) Handle

(mux *ServeMux) Handle(pattern string, handler Handler)
Handle注册HTTP处理器handler和对应的模式pattern。如果该模式已经注册有一个处理器,Handle会panic。

func (*ServeMux) HandleFunc

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
//HandleFunc注册一个处理器函数handler和对应的模式pattern,其实至始至终就调用了上面的Handle
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    if handler == nil {
        panic("http: nil handler")
    }
    mux.Handle(pattern, HandlerFunc(handler))
}

既然说完了ServeMux的注册路由函数,就先举个例子:

复制代码
package main

import (
    "fmt"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/hello", Hello)
    server := &http.Server{
        Addr:    "127.0.0.1:8080",
        Handler: mux,
    }
    server.ListenAndServe()
}

func Hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "hello ", r.URL.Path)
}
复制代码

请求(Path:"/hello")-->mux在自己的map中找Path对应的Handler(mux调用HandelFunc()注册到map中)

-->找到后调用。

注意上面调用的mux.HandleFunc是由mux调用的,而平时我们使用的是

 http.HandleFunc("/", sayhelloName) 
 http.Handle("/", sayhelloName) 

http在包中声明并定义了一个ServeMux,名字叫DefaultServeMux,然后又声明了HandleFunc和Handle函数。

func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}

func ListenAndServe

func ListenAndServe(addr string, handler Handler) error

该代码的执行过程是:

1.首先调用http.HandleFunc时:

  • 调用了DefaultServeMux的HandleFunc
  • 然后调用了DefaultServeMux的Handle
  • 接着就是往DefaultServeMux的map[string]muxEntry中添加请求URL对应的路由规则,并且路由规则中存储对应的handler处理器

2.其次就是调用http.ListenAndServe(":12345", nil):

  • 首先实例化Server
  • 然后调用Server.ListenAndServe()
  • 再调用net.Listen("tcp", addr)监听端口启动一个for循环,在循环体中Accept请求
  • 然后对每个请求都实例化一个Conn,即srv.newConn(rw);并开启一个goroutine为这个请求进行服务go c.serve()
  • 读取每个请求的内容 w, err := c.readRequest()
  • 然后判断handler是否为空,如果没有设置handler(即第二个参数为nil时),handler就设置为DefaultServeMux
  • 然后要选择DefaultServeMux中合适的handler,即要判断是否有路由能够满足这个request(循环遍历ServerMux的muxEntry)。如果有路由满足,则调用该handler的ServeHTTP;如果没有路由满足,则调用NotFoundHandler的ServeHTTP

 

func (*ServeMux) Handler

func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)

Handler根据r.Method、r.Host和r.URL.Path等数据,返回将用于处理该请求的HTTP处理器。它总是返回一个非nil的处理器。如果路径不是它的规范格式,将返回内建的用于重定向到等价的规范路径的处理器。

Handler也会返回匹配该请求的的已注册模式;在内建重定向处理器的情况下,pattern会在重定向后进行匹配。如果没有已注册模式可以应用于该请求,本方法将返回一个内建的"404 page not found"处理器和一个空字符串模式。

 其源代码为:

复制代码
复制代码
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {

    // CONNECT requests are not canonicalized.
    if r.Method == "CONNECT" {
        // If r.URL.Path is /tree and its handler is not registered,
        // the /tree -> /tree/ redirect applies to CONNECT requests
        // but the path canonicalization does not.
        if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {
            return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
        }

        return mux.handler(r.Host, r.URL.Path)
    }

    // All other requests have any port stripped and path cleaned
    // before passing to mux.handler.
    host := stripHostPort(r.Host)
    path := cleanPath(r.URL.Path)

    // If the given path is /tree and its handler is not registered,
    // redirect for /tree/.
    if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
        return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
    }

    if path != r.URL.Path {
        _, pattern = mux.handler(host, path)
        url := *r.URL
        url.Path = path
        return RedirectHandler(url.String(), StatusMovedPermanently), pattern
    }

    return mux.handler(host, r.URL.Path)
}

// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()

    // Host-specific pattern takes precedence over generic ones
    if mux.hosts {
        h, pattern = mux.match(host + path)
    }
    if h == nil {
        h, pattern = mux.match(path)
    }
    if h == nil {
        h, pattern = NotFoundHandler(), ""
    }
    return
}

func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    // Check for exact match first.
    v, ok := mux.m[path] //匹配路由规则
    if ok {
        return v.h, v.pattern
    }

    // Check for longest valid match.  mux.es contains all patterns
    // that end in / sorted from longest to shortest.
    for _, e := range mux.es {
        if strings.HasPrefix(path, e.pattern) {
            return e.h, e.pattern
        }
    }
    return nil, ""
}
复制代码
复制代码

那么当路由器ServeMux里面存储好了相应的路由规则m后,具体的请求又是怎么分发的呢:

  • 当路由器ServeMux接收到请求request后就会调用
handler, _ : = mux.Handler(request)
handler.ServeHTTP(w, request)
  • 由上面源码中可见调用mux.Handler(request)的代码中又调用了mux.handler(host, r.URL.Path),他就是使用用户请求的URL和路由器中的路由规则map相匹配,匹配成功后返回map中的handler值
  • 然后再调用该handler的ServeHTTP即可

 


5)Server

type Server

复制代码
复制代码
type Server struct {
    Addr           string        // 监听的TCP地址,如果为空字符串会使用":http"
    Handler        Handler       // 调用的处理器,如为nil会调用http.DefaultServeMux
    ReadTimeout    time.Duration // 请求的读取操作在超时前的最大持续时间
    WriteTimeout   time.Duration // 回复的写入操作在超时前的最大持续时间
    MaxHeaderBytes int           // 请求的头域最大长度,如为0则用DefaultMaxHeaderBytes
    TLSConfig      *tls.Config   // 可选的TLS配置,用于ListenAndServeTLS方法
    // TLSNextProto(可选地)指定一个函数来在一个NPN型协议升级出现时接管TLS连接的所有权。
    // 映射的键为商谈的协议名;映射的值为函数,该函数的Handler参数应处理HTTP请求,
    // 并且初始化Handler.ServeHTTP的*Request参数的TLS和RemoteAddr字段(如果未设置)。
    // 连接在函数返回时会自动关闭。
    TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
    // ConnState字段指定一个可选的回调函数,该函数会在一个与客户端的连接改变状态时被调用。
    // 参见ConnState类型和相关常数获取细节。
    ConnState func(net.Conn, ConnState)
    // ErrorLog指定一个可选的日志记录器,用于记录接收连接时的错误和处理器不正常的行为。
    // 如果本字段为nil,日志会通过log包的标准日志记录器写入os.Stderr。
    ErrorLog *log.Logger
    // 内含隐藏或非导出字段
}
复制代码
复制代码

Server类型定义了运行HTTP服务端的参数。Server的零值是合法的配置。

func (*Server) Serve

func (srv *Server) Serve(l net.Listener) error

Serve会接手监听器l收到的每一个连接,并为每一个连接创建一个新的服务go程。该go程会读取请求,然后调用srv.Handler回复请求。

func (*Server) ListenAndServe

func (srv *Server) ListenAndServe() error

ListenAndServe监听srv.Addr指定的TCP地址,并且会调用Serve方法接收到的连接。如果srv.Addr为空字符串,会使用":http"。

func (*Server) ListenAndServeTLS

func (srv *Server) ListenAndServeTLS(certFile, keyFile string) error

ListenAndServeTLS监听srv.Addr确定的TCP地址,并且会调用Serve方法处理接收到的连接。必须提供证书文件和对应的私钥文件。如果证书是由权威机构签发的,certFile参数必须是顺序串联的服务端证书和CA证书。如果srv.Addr为空字符串,会使用":https"。

举例:

复制代码
复制代码
package main 
import(
    "fmt"
    "net/http"
    "time"
)

func sayhelloName(w http.ResponseWriter, req *http.Request){
    fmt.Fprintf(w, "hello web server") //将字符串写入到w,即在客户端输出
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", sayhelloName) //设置访问的路由
    // err := http.ListenAndServe(":9090", nil) //设置监听的端口
    // if err != nil {
    //     log.Fatal("ListenAndServe : ", err)
    // }
    //如果使用的是Server,则等价于:
    server := &http.Server{
        Addr: ":8000",
        ReadTimeout: 60 * time.Second,
        WriteTimeout: 60 * time.Second,
        Handler: mux,
    }
    server.ListenAndServe()
}
复制代码
复制代码

返回:

func (*Server) SetKeepAlivesEnabled

func (s *Server) SetKeepAlivesEnabled(v bool)

SetKeepAlivesEnabled控制是否允许HTTP闲置连接重用(keep-alive)功能。默认该功能总是被启用的。只有资源非常紧张的环境或者服务端在关闭进程中时,才应该关闭该功能。

posted on   博览天下with天涯海角  阅读(191)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 记一次.NET内存居高不下排查解决与启示
< 2025年3月 >
23 24 25 26 27 28 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 1 2 3 4 5

点击右上角即可分享
微信分享提示