牛骨文教育服务平台(让学习变的简单)
博文笔记

go-restful实战与深入分析之基础篇

创建时间:2017-04-23 投稿人: 浏览次数:37348

如果想分析清楚go-restful的工作原理我们这篇先介绍一些go http的基本知识,我将通过多个例子把故事串起来,先看一个最基本的helloworld的例子:

func HelloServer(w http.ResponseWriter, req *http.Request) {
    io.WriteString(w, "hello, world!
")
}
func main() {
    http.HandleFunc("/hello/", HelloServer)
    err := http.ListenAndServe(":8080", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

测试:
curl 127.0.0.1:8080/hello/
hello, world!
上面这个例子里面通过HandleFunc注册方法和路由。
再看一个例子

package main

import (
    "io"
    "net/http"
)

type a struct{}

func (*a) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    path := r.URL.String() //获得访问的路径
    io.WriteString(w, path)
}

func main() {
    http.ListenAndServe(":8080", &a{})//第2个参数需要实现Hander接口的struct,a满足
}

测试
curl 127.0.0.1:8080/hello
/hello
通过上面两个例子读者可能已经知道http怎么样使用了。当然你可以通过mux统一包装一下:

package main

import (
    "net/http"
    "io"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/h", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "hello")
    })
    mux.HandleFunc("/bye", func(w http.ResponseWriter, r *http.Request) {
        io.WriteString(w, "byebye")
    })
    mux.HandleFunc("/hello", sayhello)
    http.ListenAndServe(":8080", mux)
}

func sayhello(w http.ResponseWriter, r *http.Request) {
    io.WriteString(w, "hello world")
}

这个和第一个用的http直接使用HandleFunc是不是很相似,其实mux只是包装了一下,本质是一回事,看看测试
curl 127.0.0.1:8080/hello
hello world
curl 127.0.0.1:8080/h
hello
三个http基本使用的例子已经讲完了。
在深入讲解代码之前先说一个golang编写一个装饰器模式:

package main

import "fmt"

type HandlerFunc func(a,b string)

func (f HandlerFunc) ServeTim(a,b string) {
    f(a, b)
}

type Handler interface {
    ServeTim(x, y string)
}

func Create(m,n string)  {
    fmt.Println(m,"Create",n)
}


func Delete(m,n string)  {
    fmt.Println(m,"Delete",n)
}

func main() {

    HandlerFunc(Create).ServeTim("333","444")

    HandlerFunc(Delete).ServeTim("333","444")
}

测试:
333 Create 444
333 Delete 444
为什么要写这个装饰器东西呢?应为这个就是http里面使用这种模式装饰各种CURD方法。如果理解了这个,我们接这说。看看第一个helloworld的底层是怎样实现的

func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

上面的HandlerFunc就是我上面说的装饰器模式,把具体一个handler方法放进去,后面只要通过

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

里面的ServeHTTP去统一调用就可以了,接着看Handle方法

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)
    }

    if mux.m == nil {
        mux.m = make(map[string]muxEntry)
    }
    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}
    }
}

这个ServeMux实体mux里面维护了一个map,这个map就是具体的pattern(路径)和调用方法的对于关系。定义如下

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

当服务ListenAndServe启动监听后会阻塞等待:

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
                }
                srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c := srv.newConn(rw)
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve(ctx)
    }

当接收到一个请求后Accept获取请求内容,go c.serve(ctx)启动协程去处理,在这个方法里面先解析参数后调用ServeHTTP处理

w, err := c.readRequest(ctx)
serverHandler{c.server}.ServeHTTP(w, w.req)

ServeHTTP进入

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
    handler := sh.srv.Handler
    if handler == nil {
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    handler.ServeHTTP(rw, req)
}

接口定义如下:

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

实现方式如下/usr/local/go/src/net/http/server.go:

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
    if r.RequestURI == "*" {
        if r.ProtoAtLeast(1, 1) {
            w.Header().Set("Connection", "close")
        }
        w.WriteHeader(StatusBadRequest)
        return
    }
    h, _ := mux.Handler(r)
    h.ServeHTTP(w, r)
}

这里面通过 mux.Handler获取handler,然后通过之前说的适配器模式ServeHTTP调用具体实现方法。
细节补充一下,怎么获取handler,这个和之前怎么注册相结合:

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
}

这个从之前注册的map里面获取pattern的方法,那么我们就找到了服务怎样调用的了。

声明:该文观点仅代表作者本人,牛骨文系教育信息发布平台,牛骨文仅提供信息存储空间服务。