GOLANG 中HTTP包默认路由匹配规则阅读笔记

一、执行流程

构建一个简单http server:

package main

import (
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/",func(w http.ResponseWriter,r *http.Request) {
        w.Write([]byte("hello world"))
    })
    log.Fatal(http.ListenAndServe(":8080",nil))
}

使用http://127.0.0.1:8080/ 就可以看到输出了

通过跟踪http.go包代码,可以发现执行流程基本如下:

1.创建一个Listener监听8080端口

2.进入for循环并Accept请求,没有请求则处于阻塞状态

3.接收到请求,并创建一个conn对象,放入goroutine处理(实现高并发关键)

4.解析请求来源信息获得请求路径等重要信息

5.请求ServerHTTP方法,已经通过上一步获得了ResponseWriter和Request对象

func (sh serverHandler) ServeHTTP(rw ResponseWriter,req *Request) {
    //此handler即为http.ListenAndServe 中的第二个参数
    handler := sh.srv.Handler 
    if handler == nil {
        //如果handler为空则使用内部的DefaultServeMux 进行处理
        handler = DefaultServeMux
    }
    if req.RequestURI == "*" && req.Method == "OPTIONS" {
        handler = globalOptionsHandler{}
    }
    //这里就开始处理http请求
    //如果需要使用自定义的mux,就需要实现ServeHTTP方法,即实现Handler接口。
    handler.ServeHTTP(rw,req)
}

6.进入DefaultServeMux中的逻辑就是根据请求path在map中匹配查找handler,并交由handler处理

http请求处理流程更多信息可以参考[《Go Web 编程
》3.3 Go如何使得Web工作](https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.3.md)

二、DefaultServeMux 路由匹配规则

先看几个路由规则:

package main

import (
    "log"
    "net/http"
)

func main() {
    //规则1
    http.HandleFunc("/",r *http.Request) {
        w.Write([]byte("hello world"))
    })
    
    //规则2
    http.HandleFunc("/path/",r *http.Request) {
        w.Write([]byte("pattern path: /path/ "))
    })

    //规则3
    http.HandleFunc("/path/subpath",r *http.Request) {
        w.Write([]byte("pattern path: /path/subpath"))
    })

    log.Fatal(http.ListenAndServe(":8080",nil))
}

情景一:

访问:http://127.0.0.1:8080/

返回:hello world

情景二:

访问:http://127.0.0.1:8080/path

返回:pattern path: /path/

情景三:

访问:http://127.0.0.1:8080/path/subpath/

返回:pattern path: /path/

情景四:

访问:http://127.0.0.1:8080/hahaha/

返回:hello world

先说明一些规则吧,再看代码是怎么实现的:

1.如果匹配路径中后带有/,则会自动增加一个匹配规则不带/后缀的,并跳转转到path/,解释了情景二的场景,为什么匹配到的/path/

2.我设置了这么多规则为什么规则一可以通用匹配未设置的路由信息,而且又不影响已经存在路由, 内部是怎么实现的?

2.1 添加路由规则

先看两个struct,这是存放默认路由规则的:

type ServeMux struct {
    mu    sync.RWMutex  //处理并发,增加读写锁
    m     map[string]muxEntry  //存放规则map,key即为设置的path
    hosts bool // whether any patterns contain hostnames(是否包含host)
}

type muxEntry struct {
    explicit bool //是否完全匹配
    h        Handler//相应匹配规则的handler
    pattern  string//匹配路径
}

通过跟踪http.HandleFunc定位到如下代码,正是往上面两个struct中增加规则:

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")
    }
    //如果已经匹配到了则panic
    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }
    
    //增加一个新的匹配规则
    mux.m[pattern] = muxEntry{explicit: true,h: handler,pattern: pattern}
    
    //根据path的第一个字母判断是否有host
    if pattern[0] != '/' {
        mux.hosts = true
    }

    //!!这里看清楚 就是实现了情景二的情况 ,看判断条件
    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}
    }
}

上面有个Helpful behavior的注释行为,就是实现了情景二的情况,他是判断如果匹配的路径中最后含有/,并且之前也不存在添加去除反斜杠的规则的话,就自动给他增加一个301的跳转指向/path/

2.2 查找路由规则

路由规则的查找就是从ServeMux 中的map去匹配查找的,的到这个handler并执行,只是会有一些处理机制,比如怎么样确保访问/path/subpath的时候是先匹配/path/subpath而不是匹配/path/呢?

当一个请求过来的时候,跟踪到了mux.match方法:

过程mux.ServerHTTP->mux.Handler->mux.handler->mux.match

func (mux *ServeMux) match(path string) (h Handler,pattern string) {
    var n = 0
    for k,v := range mux.m {
        if !pathMatch(k,path) {
            continue
        }
        //如果匹配到了一个规则,并没有马上返回handler,而且继续匹配并且判断path的长度是否是最长的,这是关键!!!
        if h == nil || len(k) > n {
            n = len(k)
            h = v.h
            pattern = v.pattern
        }
    }
    return
}

1.这里就解释了为什么设置的精确的path是最优匹配到的,因为它是根据path的长度判断。
当然也就解释了为什么/可以匹配所有(看pathMatch函数就知道了,/是匹配所有的,只是这是最后才被匹配成功)

2.得到了处理请求的handler,再调用h.ServeHTTP(w,r),去执行相应的handler方法。

等一下,handler中哪里有ServeHTTP这个方法??

因为在调用 http.HandleFunc的时候已经将自定义的handler处理函数,强制转为HandlerFunc类型的,就拥有了ServeHTTP方法:

type HandlerFunc func(ResponseWriter,*Request)

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

f(w,r)就实现了handler的执行。

原文地址:silenceper.com

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


类型转换 1、int转string 2、string转int 3、string转float 4、用户结构类型转换
package main import s "strings" import "fmt" var p = fmt.Println func main() { p("Contains: ", s.Contains("test&quo
类使用:实现一个people中有一个sayhi的方法调用功能,代码如下: 接口使用:实现上面功能,代码如下:
html代码: beego代码:
1、读取文件信息: 2、读取文件夹下的所有文件: 3、写入文件信息 4、删除文件,成功返回true,失败返回false
配置环境:Windows7+推荐IDE:LiteIDEGO下载地址:http://www.golangtc.com/downloadBeego开发文档地址:http://beego.me/docs/intro/ 安装步骤: 一、GO环境安装 二、配置系统变量 三、Beego安装 一、GO环境安装 根
golang获取程序运行路径:
Golang的文档和社区资源:为什么它可以帮助开发人员快速上手?
Golang:AI 开发者的实用工具
Golang的标准库:为什么它可以大幅度提高开发效率?
Golang的部署和运维:如何将应用程序部署到生产环境中?
高性能AI开发:Golang的优势所在
本篇文章和大家了解一下go语言开发优雅得关闭协程的方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。1.简介本文将介绍首先为什么需要主...
这篇文章主要介绍了Go关闭goroutine协程的方法,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。1.简介本文将介绍首先为什么需要主动关闭gor...
本篇文章和大家了解一下go关闭GracefulShutdown服务的几种方法。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。目录Shutdown方法Regi...
这篇文章主要介绍了Go语言如何实现LRU算法的核心思想和实现过程,具有一定借鉴价值,需要的朋友可以参考下。下面就和我一起来看看吧。GO实现Redis的LRU例子常
今天小编给大家分享的是Go简单实现多租户数据库隔离的方法,相信很多人都不太了解,为了让大家更加了解,所以给大家总结了以下内容,一起往下看吧。一定会...
这篇“Linux系统中怎么安装NSQ的Go语言客户端”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希
本文小编为大家详细介绍“怎么在Go语言中实现锁机制”,内容详细,步骤清晰,细节处理妥当,希望这篇“怎么在Go语言中实现锁机制”文章能帮助大家解决疑惑,下面...
今天小编给大家分享一下Go语言中interface类型怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考