shownb / shownb.github.com

shownb.github.io
shownb.github.io
5 stars 1 forks source link

golang 建立web服务器 http包源码详解 #15

Open shownb opened 6 years ago

shownb commented 6 years ago

转自 http://blog.csdn.net/qq_24850089/article/details/52713701

  1. 搭建简单的web服务器
    
    package main

import ( "fmt" "net/http" )

func HandleRequest(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "hello") }

func main() { http.HandleFunc("/", HandleRequest) err := http.ListenAndServe(":8080", nil) }

回到一开始的web服务器,仔细想想,基本上是两个函数撑起整个Web服务器啊!
居然如此简洁!!
```golang
http.HandleFunc("/", HandleRequest)
http.ListenAndServe(":8080", nil)

首先,先分析一下http.HandleFunc()这个函数。 直接进入函数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调用了HandleFunc() 参数就是传进来的"/"和HandleRequest 对于DefaultServeMux在源代码中的声明如下

type ServeMux struct {
    mu    sync.RWMutex
    m     map[string]muxEntry
    hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
    explicit bool
    h        Handler
    pattern  string
}

// NewServeMux allocates and returns a new ServeMux.
func NewServeMux() *ServeMux { 
    return &ServeMux{m: make(map[string]muxEntry)} 
}

// DefaultServeMux is the default ServeMux used by Serve.
var DefaultServeMux = NewServeMux()

可以看到,DefaultServeMux是一个默认的ServerMux,而ServeMux的结构体中,mu sync.RWMutex是一个并发控制的锁,一个装有muxEntry结构的map,一个hosts判断是否包含主机名。

!!!仔细一看,这个muxEntry结构体,其中一个变量h Handler和另一个变量pattern string。

而这个Handler定义如下

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

是一个接口!!方法是ServeHTTP(ResponseWriter, *Request)。

总结一下 DefaultServeMux的代码看完,大概清楚了。DefaultServeMux是一个ServerMux结构,这个结构里面最最主要包含一个map[string]muxEntry,map里面的每个muxEntry又包含一个string类型的变量pattern和一个接口Handler,这个接口里面的方法是ServeHTTP(ResponseWriter, *Request)。

好了,了解完DefaultServeMux,继续进入它方法。

// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

这里相当于调用了ServeMux的方法Handle()。(涉及到golang中的面向对象的方法)

这里先分析HandlerFunc(handler),也就是把func(ResponseWriter, *Request)函数类型转换为HandlerFunc类型(注意!是HandlerFunc,不是HandleFunc)这里有点像python的装饰器的意思

先来看看HandlerFunc是什么。

// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers.  If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler object that calls f.
type HandlerFunc func(ResponseWriter, *Request)

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

HandlerFunc这里居然定义了一个func(ResponseWriter, *Request)的函数类型,HandlerFunc(handler)实际上就是handler本身。为什么这么做?(因为接口,因为接口)

接看下面的函数。

HandlerFunc实现了ServeHTTP(w ResponseWriter, r *Request) 这个方法!!!!里面只有一行代码f(w, r),也就是说实际上是调用handler,也就是我们一开始在http.HandleFunc("/", HandleRequest)中自己定义的函数HandleRequest。

关键来了!!!!

ServeHTTP(w ResponseWriter, r *Request)这个方法不就是上面Handler接口里面的方法吗!

HandlerFunc实现Handler接口里面的方法。跟Java里面的接口一样,任何实现接口的类型,都可以向上转换为该接口类型。这就意味着HandlerFunc类型的值可以自动转换为Handler类型的值作为参数传进任何函数中。这很重要!回头看看muxEntry结构体里面的两个变量pattern string和h Handler,不正好对应刚才传入的参数pattern, HandlerFunc(handler)吗!!

总结一下 所以,HandlerFunc(handler)就是一个适配器模式!HandlerFunc实现Handler接口,ServeHTTP方法里面调用的实际上是我们一开始定义的函数HandleRequest。 这样的一个好处就是,func(ResponseWriter, *Request) -> HandlerFunc -> Handler ,那定义的函数HandleRequest可以作为Handler类型的一个参数。调用Handler的ServeHTTP方法,也就是调用定义的函数HandleRequest。

理解完HandlerFunc(handler),再来看看整句mux.Handle(pattern, HandlerFunc(handler))

Handle函数源码如下:

// 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 " + pattern)
    }
    if handler == nil {
        panic("http: nil handler")
    }
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}

    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern
        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }
        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

这里的代码直接关注到第17行和37行

由于37行的代码是经过判断后进入if的语句,所以别扣细节,直接关注第17行。

mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern} 都是赋值语句,基本逻辑都是为DefaultServeMux里面的map赋值。 总结一下 这里就是把传进来的pattern和handler保存在muxEntry结构中,并且pattern作为key,把muxEntry装入到DefaultServeMux的Map里面。

至此,第一个关键的函数http.HandleFunc("/", HandleRequest)就分析完了,就是把当前的参数"/", HandleRequest保存在http包里面的默认的一个ServeMux结构中的map中,简单来说就是保存当前路由和自己定义的那个处理函数。虽然理顺思路感觉挺简单,但是不得不赞叹设计得实在太妙了

那么接下来就是http.ListenAndServe(":8080", nil) http://blog.csdn.net/qq_24850089/article/details/52713701