GO正则匹配路由处理和gozero的请求处理源码浅析


先简单实现一个 http 服务端,这个服务支持正则匹配路由处理:

package main

import (
    "fmt"
    "net/http"
    "regexp"
)

// https://www.jianshu.com/p/4e8cdf3b2f88

// client -> Request -> Multiplexer(router)->handler ->Response -> client

// 路由定义
type routeItem struct {
    pattern string                                       // 正则表达式
    f       func(w http.ResponseWriter, r *http.Request) //Controller函数
}

// 路由添加
var routePath = []routeItem{
    {"^/user.*$", UserHandler},
    {"^/company.*$", CompanyHandler},
}

func UserHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello user"))
}
func CompanyHandler(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("hello company"))
}

func IndexHandler(w http.ResponseWriter, r *http.Request) {
    isFound := false
    for _, p := range routePath {
        // 这里循环匹配Path,先添加的先匹配
        reg, err := regexp.Compile(p.pattern)
        if err != nil {
            continue
        }
        if reg.MatchString(r.URL.Path) {
            isFound = true
            p.f(w, r)
        }
    }
    if !isFound {
        // 未匹配到路由
        fmt.Fprint(w, "404 Page Not Found!")
    }
}

func main() {
    http.HandleFunc("/", IndexHandler)
    err := http.ListenAndServe("127.0.0.1:8899", nil)
    if err != nil {
        fmt.Println(err.Error())
        return
    }
}

上面的例子就得到一个结论:注册路由的方式可以是 HandleFunc ,其中直接将 /  匹配的请求全部交给 IndexHandler处理,在函数里面决定处理结果

先是注册不同路由对应的handler,然后是 listen address port 

开始阅读 go-zero的源代码:

无非两行:

// 注册路由
handler.RegisterHandlers(server, ctx)

// 启动服务
server.Start()

最终启动服务 go-zero 框架用到的也是 *http.Server.ListenAndServe  

其中 http.Server 对象是

&http.Server{
    Addr:      fmt.Sprintf("%s:%d", host, port),
    Handler: handler,
}

注册的 handler 也就是路由的处理函数

github.com/zeromicro/go-zero/rest/engine.go 的 internal.StartHttp 

传递的第三个参数 Router 是 http.Handler 的继承

所以很明显,整个 go-zero的web系统的路由注册在这里

那么,gozero究竟是否支持模糊匹配呢?是否支持正则匹配呢?

寻找的思路是:go的net/http最终都会转给

func(w http.ResponseWriter, r *http.Request)

的实现处理,gozero的模块划分比较清晰很容易定位到:

/github.com/zeromicro/go-zero/rest/router/patrouter.go   ->   ServeHTTP(w http.ResponseWriter, r *http.Request)

这个函数是每次请求的最终处理函数,那么它是否支持了正则匹配呢?

 很明显,主要取决于工具包

func (t *Tree) Search(route string) (Result, bool)

 Search的实现逻辑

然后是按照分隔符 '/' 严格匹配查找路径对应的handler的,所以不支持正则路由匹配

Search的代码

// Search searches item that associates with given route.
func (t *Tree) Search(route string) (Result, bool) {
    if len(route) == 0 || route[0] != slash {
        return NotFound, false
    }

    var result Result
    ok := t.next(t.root, route[1:], &result)
    return result, ok
}

func (t *Tree) next(n *node, route string, result *Result) bool {
    log.Println("当前的存储的路径解析的参数结果集",result)
    log.Println("next route = ",route)
    log.Println("n.item = ",n.item)
    if len(route) == 0 && n.item != nil {
        result.Item = n.item
        return true
    }
    for i := range route {
        log.Println("i",i)
        if route[i] != slash {
            continue
        }
        token := route[:i]
        return n.forEach(func(k string, v *node) bool {
            fmt.Println("token=",token,"k",k)
            // 判定k的第一个字符是不是:,如果是将作为一个请求参数
            // if pat[0] == colon { // colon是:
            r := match(k, token)
            // 截断已经读取过的
            log.Println("last route is",route[i+1:])
            if !r.found || !t.next(v, route[i+1:], result) {
                return false
            }
            log.Println("r.named",r.named)
            if r.named {
                addParam(result, r.key, r.value)
            }
            return true
        })
    }
}

写的有点冗余也挺绕的,就是循环每一个路径的字符,不断和常量 : / 比对,找到路径对应的 handler 的同时获取 :path 这种占位符的变量

/github.com/zeromicro/go-zero/core/search/tree.go

上面是比对的字符的两个常量;

TRANSLATE with x English
Arabic Hebrew Polish
Bulgarian Hindi Portuguese
Catalan Hmong Daw Romanian
Chinese Simplified Hungarian Russian
Chinese Traditional Indonesian Slovak
Czech Italian Slovenian
Danish Japanese Spanish
Dutch Klingon Swedish
English Korean Thai
Estonian Latvian Turkish
Finnish Lithuanian Ukrainian
French Malay Urdu
German Maltese Vietnamese
Greek Norwegian Welsh
Haitian Creole Persian  
Bing Webmaster Portal Back

相关