golang如何将http请求流转到gin

gin是作为golang web开发中被广泛使用到的框架,了解其内部的实现有助于我们更好地理解gin的设计思想 。
这篇文章主要探讨两个问题 。

  • http请求如何流转到gin
  • gin为什么比golang的http路由寻找更快
开始之前我们先来看看分别用golang原生的http包实现一个http服务和使用gin实现的代码,先看看原生http包实现的http服务
package mainimport ("net/http")func main() {http.HandleFunc("/ping", func(writer http.ResponseWriter, request *http.Request) {writer.Write([]byte(`{"message":"ok"}`))})http.ListenAndServe(":9090", nil)}这段代码做了两件事情,注册路由、启动服务监听9090端口 。接下来我们对这段代码进一步分析,在第8行的地方是将路由/ping和对应的处理函数注册到http服务中,我们进入http.HandleFunc()函数看看该函数做了什么事情 。
// HandleFunc registers the handler function for the given pattern// in the DefaultServeMux.// The documentation for ServeMux explains how patterns are matched.func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {DefaultServeMux.HandleFunc(pattern, handler)}将路由和处理函数注册到了DefaultServeMux中,所以我们先看看DefaultServeMux的结构是什么 。
type ServeMux struct {musync.RWMutexmmap[string]muxEntryes[]muxEntry // slice of entries sorted from longest to shortest.hosts bool// whether any patterns contain hostnames}type muxEntry struct {hHandlerpattern string}// NewServeMux allocates and returns a new ServeMux.func NewServeMux() *ServeMux { return new(ServeMux) }// DefaultServeMux is the default ServeMux used by Serve.var DefaultServeMux = &defaultServeMuxvar defaultServeMux ServeMux第17行代码就是刚刚用来注册http路由的服务,通过第19行代码知道了他是一个ServeMux类型 。知道了DefaultServeMux的类型我们接着看具体的实现代码 。
// HandleFunc registers the handler function for the given pattern.func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {if handler == nil {panic("http: nil handler")}mux.Handle(pattern, HandlerFunc(handler))}// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.func (mux *ServeMux) Handle(pattern string, handler Handler) {mux.mu.Lock()defer mux.mu.Unlock()if pattern == "" {panic("http: invalid pattern")}if handler == nil {panic("http: nil handler")}if _, exist := mux.m[pattern]; exist {panic("http: multiple registrations for " + pattern)}if mux.m == nil {mux.m = make(map[string]muxEntry)}e := muxEntry{h: handler, pattern: pattern}mux.m[pattern] = eif pattern[len(pattern)-1] == '/' {mux.es = AppendSorted(mux.es, e)}if pattern[0] != '/' {mux.hosts = true}}主要的代码就是第29行,这里将路由和处理函数保存在了ServeMux的m中,通过前面的代码我们知道m是一个map,到这里路由注册的过程就分析完了 。接下来我们来看看 http.ListenAndServe()做了什么事情 。
// ListenAndServe listens on the TCP network address srv.Addr and then// calls Serve to handle requests on incoming connections.// Accepted connections are configured to enable TCP keep-alives.//// If srv.Addr is blank, ":http" is used.//// ListenAndServe always returns a non-nil error. After Shutdown or Close,// the returned error is ErrServerClosed.func (srv *Server) ListenAndServe() error {if srv.shuttingDown() {return ErrServerClosed}addr := srv.Addrif addr == "" {addr = ":http"}ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln)}第22行就是真正开始启动http服务,并接受请求的函数 。第17行创建了主动套接字并监听套接字,接着我们进入Serve()函数 。
func (srv *Server) Serve(l net.Listener) error {if fn := testHookServerServe; fn != nil {fn(srv, l) // call hook with unwrapped listener}origListener := ll = &onceCloseListener{Listener: l}defer l.Close()if err := srv.setupHTTP2_Serve(); err != nil {return err}if !srv.trackListener(&l, true) {return ErrServerClosed}defer srv.trackListener(&l, false)baseCtx := context.Background()if srv.BaseContext != nil {baseCtx = srv.BaseContext(origListener)if baseCtx == nil {panic("BaseContext returned a nil context")}}var tempDelay time.Duration // how long to sleep on accept failurectx := context.WithValue(baseCtx, ServerContextKey, srv)for {rw, err := l.Accept()if err != nil {select {case <-srv.getDoneChan():return ErrServerCloseddefault:}if ne, ok := err.(net.Error); ok && ne.Temporary() {if tempDelay == 0 {tempDelay = 5 * time.Millisecond} else {tempDelay *= 2}if max := 1 * time.Second; tempDelay > max {tempDelay = max}srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)time.Sleep(tempDelay)continue}return err}connCtx := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can returngo c.serve(connCtx)}}


推荐阅读