从源码对比DefaultServeMux 与 gorilla/mux
从源码对比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缺陷
不支持正则路由
只支持路径匹配,不支持按照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的更多相关文章
- JDK(十)JDK1.7&1.8源码对比分析【集合】ConcurrentHashMap
前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们对比分析了JDK1.7和1.8版本的HashMap源码,趁热打铁,这篇文章就来看看JDK1.7和1.8版本的Concurre ...
- git CVE-2014-9390 验证以及源码对比
一 验证部分 首先在ubuntu下面建立如下工程 mkdir repo cd repo git init mkdir -p .GiT/hooks cp post-checkout .GiT/hooks ...
- JDK(八)JDK1.7&1.8源码对比分析【集合】HashMap
前言 在JDK1.8源码分析[集合]HashMap文章中,我们分析了HashMap在JDK1.8中新增的特性(引进了红黑树数据结构),但是为什么要进行这个优化呢?这篇文章我们通过对比JDK1.7和1. ...
- frp源码剖析-frp中的mux模块
前言 frp几乎所有的连接处理都是构建在mux模块之上的,重要性不必多说,来看一下这是个啥吧 ps: 安装方法 go get "github.com/fatedier/golib/net/m ...
- C# 30分钟完成百度人脸识别——进阶篇(文末附源码)
距离上次入门篇时隔两个月才出这进阶篇,小编惭愧,对不住关注我的卡哇伊的小伙伴们,为此小编用这篇博来谢罪. 前面的准备工作我就不说了,注册百度账号api,创建web网站项目,引入动态链接库引入. 不了解 ...
- 框架源码系列九:依赖注入DI、三种Bean配置方式的注册和实例化过程
一.依赖注入DI 学习目标1)搞清楚构造参数依赖注入的过程及类2)搞清楚注解方式的属性依赖注入在哪里完成的.学习思路1)思考我们手写时是如何做的2)读 spring 源码对比看它的实现3)Spring ...
- spring boot 2.0 源码分析(一)
在学习spring boot 2.0源码之前,我们先利用spring initializr快速地创建一个基本的简单的示例: 1.先从创建示例中的main函数开始读起: package com.exam ...
- JDK(九)JDK1.7源码分析【集合】HashMap的死循环
前言 在JDK1.7&1.8源码对比分析[集合]HashMap中我们遗留了一个问题:为什么HashMap在调用resize() 方法时会出现死循环?这篇文章就通过JDK1.7的源码来分析并解释 ...
- 源码分析关于SpringBoot2.x版本与1.5版本之间的问题
1.Social包在SpringBoot2.x移除问题 spring-boot-autoconfigure1.5x版本中支持facebook,领英和推特官方文档:https://docs.spring ...
随机推荐
- 轻量级批量Omnitty工具安装和简单使用
一.Omnitty简单介绍 在实际工作需要同时对多台docker进行批量处理,为了节省时间,这个运维轻量级工具解决此问题 二.Omnitty简单按照 下载需要安装包和依赖包: omnitty-0.3. ...
- PHP出现Warning: A non-numeric value encountered问题的原因及解决方法
本文介绍php出现Warning: A non-numeric value encountered问题,用实例分析出现这种错误的原因,并提供避免及解决问题的方法. <?php error_rep ...
- 在VC6.0中多线程编程演示样例(带同步信号量)
直接上代码: #include <windows.h>//必要的头文件,使用Windows API函数 #include <stdio.h> int index = 0; in ...
- vue入门教程 (vueJS2.X)
vue入门教程vueJS2.X 写在前面 看完此教程可以达到:能看懂并能修改简单的vue项目. 看的过程中,请把所有例子都放到html文件中跑一遍. Vue.js 是什么 Vue.js(读音 /vju ...
- [办公应用]从美国带回来的兄弟牌brother打印机如何处理
周末帮朋友去调一台国外带回来的brother一体机,型号MFC-J630W.这是她单位老板从国外带回来的,说是便宜:不过她说只有英文说明书,她不太会操作.我想这还不容易么,就满口答应下来了.我先到br ...
- HDU2389 Rain on your Parade —— 二分图最大匹配 HK算法
题目链接:https://vjudge.net/problem/HDU-2389 Rain on your Parade Time Limit: 6000/3000 MS (Java/Others) ...
- [转]python的startswith()方法
描述 Python startswith() 方法用于检查字符串是否是以指定子字符串开头,如果是则返回 True,否则返回 False.如果参数 beg 和 end 指定值,则在指定范围内检查. 语法 ...
- 点击button传递消息,但是页面不跳转的解决方法
最近在做一个物联网的项目时遇到的问题:界面上有很多控制开/关灯的button,通过点击button来控制各个灯的亮灭.我需要将获取的不同的点击事件消息,以Socket通信的方式发送给硬件端的服务监听程 ...
- python 面向对象三 访问权限 下划线 双下划线
一.双下划线 如果要让内部属性不被外部访问,可以把属性的名称前加上两个下划线__,在Python中,实例的变量名如果以__开头,就变成了一个私有变量(private),只有内部可以访问,外部不能访问. ...
- VMware 12安装Mac OS X 10.11&解决上网的问题
近日想在Win10上安装Mac OS 玩玩,于是上网搜了相关资源,查看了相关经验分享,开始着手安装.系统很快成功安装,但最大问题是虚拟机中的Mac OS无法上网.费了很长时间,最终看到Ping通结果, ...