从源码对比DefaultServeMux 与 gorilla/mux

DefaultServeMux

Golang自带的net/http库中包含了DefaultServeMux方法,以此可以搭建一个稳定的高并发的web server。

DefaultServeMux源码分析

  • 路由表是实现路由功能的重要结构。muxEntry是存放具体的路由信息的一个map,key是路径,而value是该路径所对应的处理函数。

      type ServeMux struct {
    mu sync.RWMutex
    m map[string]muxEntry
    }
    type muxEntry struct {
    explicit bool
    h Handler
    pattern string
    }
  • 路由注册就是往map中插入数据,如果注册路径在当前路由表中不存在,则会在路由表中增加这一条路径数据,且处理函数是重定向到该路径,要注意的是注册路径要以/结尾才会添加到路由表中。

      func (mux *ServeMux) Handle(pattern string, handler Handler) {
    mux.mu.Lock()
    defer mux.mu.Unlock()
    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
    n := len(pattern)
    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
    path := pattern
    fmt.Printf("redirect for :%s to :%s", pattern, path)
    mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(path, StatusMovedPermanently), pattern: pattern}
    }
    }
  • 路由查找的过程实际上就是遍历路由表的过程,返回最长匹配请求路径的路由信息,找不到则返回NotFoundHandler。如果路径以xxx/结尾,则只要满足/xxx/* 就符合该路由。

      func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
    mux.mu.RLock()
    defer mux.mu.RUnlock()
    if h == nil {
    h, pattern = mux.match(path)
    }
    if h == nil {
    h, pattern = NotFoundHandler(), ""
    }
    return
    } func (mux *ServeMux) match(path string) (h Handler, pattern string) {
    var n = 0
    for k, v := range mux.m {
    if !pathMatch(k, path) {
    continue
    }
    //找出匹配度最长的
    if h == nil || len(k) > n {
    n = len(k)
    h = v.h
    pattern = v.pattern
    }
    }
    return
    } func pathMatch(pattern, path string) bool {
    n := len(pattern)
    if pattern[n-1] != '/' {
    return pattern == path
    }
    return len(path) >= n && path[0:n] == pattern
    }

DefaultServeMux缺陷

  1. 不支持正则路由

  2. 只支持路径匹配,不支持按照Method,header,host等信息匹配,所以也就没法实现RESTful架构

gorilla/mux

  • 路由信息存放在数组中,每一条路由信息都包括了路由的约束条件以及上层处理函数,当请求到来时,路由会遍历数组,找到第一个匹配的路由,并执行对应的处理函数,如果找不到则执行NotFoundHandler。

      type Router struct {
    routes []*Route
    }
    // Match matches registered routes against the request.
    func (r *Router) Match(req *http.Request, match *RouteMatch) bool {
    for _, route := range r.routes {
    //Route.Match会检查http.Request是否满足其设定的各种条件(路径,Header,Host..)
    if route.Match(req, match) {
    return true
    }
    }
    return false
    }
    func (r *Router) ServeHTTP(w http.ResponseWriter, req *http.Request) {
    var match RouteMatch
    var handler http.Handler
    if r.Match(req, &match) {
    handler = match.Handler
    }
    if handler == nil {
    handler = http.NotFoundHandler()
    }
    handler.ServeHTTP(w, req)
    }
  • 当请求到来时,Route.Match()会遍历matcher数组,只有数组中所有的元素都返回true时则说明此请求满足该路由的限定条件。

      type Route struct {
    // Request handler for the route.
    handler http.Handler
    // List of matchers.
    matchers []matcher
    }
  • gorilla/mux使用了一个第三方模块gorilla/context。当http请求到来时,mux.Router会选择合适的路由,并提取出一些参数信息,将这些参数信息与http.Request对象在gorilla/context中建立映射关系,上层处理函数根据http.Request对象到context中找到该http.Request所对应的参数信息。

      var data  = make(map[*http.Request]map[interface{}]interface{})
    func Set(r *http.Request, key, val interface{}) {
    mutex.Lock()
    if data[r] == nil {
    data[r] = make(map[interface{}]interface{})
    datat[r] = time.Now().Unix()
    }
    data[r][key] = val
    mutex.Unlock()
    }
    func Get(r *http.Request, key interface{}) interface{} {
    mutex.RLock()
    if ctx := data[r]; ctx != nil {
    value := ctx[key]
    mutex.RUnlock()
    return value
    }
    mutex.RUnlock()
    return nil
    }
  • 上层处理函数中调用mux.Vars(r)则可以取出该http.Request所关联的参数信息。val实际上是一个map[string][string],存放该请求对应的变量值集合。

      func setVars(r *http.Request, val interface{}) {
    context.Set(r, varsKey, val)
    }
    func Vars(r *http.Request) map[string]string {
    if rv := context.Get(r, varsKey); rv != nil {
    //类型转换,如果失败直接panic
    return rv.(map[string]string)
    }
    return nil
    }

从源码对比DefaultServeMux 与 gorilla/mux的更多相关文章

  1. JDK(十)JDK1.7&1.8源码对比分析【集合】ConcurrentHashMap

    前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们对比分析了JDK1.7和1.8版本的HashMap源码,趁热打铁,这篇文章就来看看JDK1.7和1.8版本的Concurre ...

  2. git CVE-2014-9390 验证以及源码对比

    一 验证部分 首先在ubuntu下面建立如下工程 mkdir repo cd repo git init mkdir -p .GiT/hooks cp post-checkout .GiT/hooks ...

  3. JDK(八)JDK1.7&1.8源码对比分析【集合】HashMap

    前言 在JDK1.8源码分析[集合]HashMap文章中,我们分析了HashMap在JDK1.8中新增的特性(引进了红黑树数据结构),但是为什么要进行这个优化呢?这篇文章我们通过对比JDK1.7和1. ...

  4. frp源码剖析-frp中的mux模块

    前言 frp几乎所有的连接处理都是构建在mux模块之上的,重要性不必多说,来看一下这是个啥吧 ps: 安装方法 go get "github.com/fatedier/golib/net/m ...

  5. C# 30分钟完成百度人脸识别——进阶篇(文末附源码)

    距离上次入门篇时隔两个月才出这进阶篇,小编惭愧,对不住关注我的卡哇伊的小伙伴们,为此小编用这篇博来谢罪. 前面的准备工作我就不说了,注册百度账号api,创建web网站项目,引入动态链接库引入. 不了解 ...

  6. 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程

    一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...

  7. spring boot 2.0 源码分析(一)

    在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...

  8. JDK(九)JDK1.7源码分析【集合】HashMap的死循环

    前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们遗留了一个问题:为什么HashMap在调用resize() 方法时会出现死循环?这篇文章就通过JDK1.7的源码来分析并解释 ...

  9. 源码分析关于SpringBoot2.x版本与1.5版本之间的问题

    1.Social包在SpringBoot2.x移除问题 spring-boot-autoconfigure1.5x版本中支持facebook,领英和推特官方文档:https://docs.spring ...

随机推荐

  1. SGU - 186 - The Chain (贪心)

    186. The Chain time limit per test: 0.25 sec. memory limit per test: 4096 KB input: standard input o ...

  2. java-组合优于继承

    组合和继承.都能实现对类的扩展. 差别例如以下表所看到的 组合 继承 has-a关系 is-a关系 执行期决定 编译期决定 不破坏封装,总体和局部松耦合 破坏封装,子类依赖父类 支持扩展,任意添加组合 ...

  3. 朴素的标题:MVC中权限管理实践

    基于MVC的web项目最好的权限控制方式我认为是对Action的控制,实现思路记录于此,权限管理分成两个部分授权.认证. 一.授权 1.读取当前项目中的所有需要控制的Action /// <su ...

  4. [办公自动化]计算机突然断电,微软office文档(有asd文件)如何恢复?

    今天同事使用office软件时,突然故障.结果他忙了半天的word文档内容都找不见了. 经过查找,在其硬盘根目录找到了asd文档. 但是用当前版本的word和高版本的word软件都无法打开. 又查找了 ...

  5. 2016/05/10 thinkphp 3.2.2 ①系统常量信息 ②跨控制器调用 ③连接数据库配置及Model数据模型层 ④数据查询

    [系统常量信息] 获取系统常量信息: 如果加参数true,会分组显示: 显示如下: [跨控制器调用] 一个控制器在执行的时候,可以实例化另外一个控制,并通过对象访问其指定方法. 跨控制器调用可以节省我 ...

  6. 2016/2/26 <marquee></marquee>实现多种滚动效果

    页面的自动滚动效果,可由javascript来实现,但是有一个html标签 - <marquee></marquee>可以实现多种滚动效果,无需js控制.使用marquee标记 ...

  7. Hadoop0.20.203.0在关机重启后,namenode启动报错(/dfs/name is in an inconsistent state)

    Hadoop0.20.203.0在关机重启后,namenode启动报错: 2011-10-21 05:22:20,504 INFO org.apache.hadoop.hdfs.server.comm ...

  8. TCP Operational Overview and the TCP Finite State Machine (FSM) http://tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF.htm

    http://tcpipguide.com/free/t_TCPOperationalOverviewandtheTCPFiniteStateMachineF.htm   http://tcpipgu ...

  9. 设计模式-(9)中介者模式(swift)

    在对象去耦合的模式中,有两种模式:中介者模式,观察者模式 一,概念 用一个中介对象来封装一系列的对象交互.中介者使各对象不需要显式地相互引用,从而使其耦合松散,而且可以独立地改变它们之间的交互. 这个 ...

  10. Linux设备驱动--块设备(二)之相关结构体

    上回最后面介绍了相关数据结构,下面再详细介绍 块设备对象结构 block_device 内核用结构block_device实例代表一个块设备对象,如:整个硬盘或特定分区.如果该结构代表一个分区,则其成 ...