go web framework gin 路由表的设计
在上一篇go web framework gin 启动流程分析这一篇文章中,我分析了go gin启动的过程,在这一篇文章中我将继续上面的分析,讨论gin 中路由表是如何设计的?
首先查看engine.handleHTTPRequest() 这个方法的定义:
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method //获取Request method
path := c.Request.URL.Path //获取 Request URL Path
unescape := false
if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 {
path = c.Request.URL.RawPath
unescape = engine.UnescapePathValues
} // Find root of the tree for the given HTTP method
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ { //遍历每一个methord tree
if t[i].method != httpMethod {
continue
}
root := t[i].root
// Find route in tree
handlers, params, tsr := root.getValue(path, c.Params, unescape) //如果找到这个method tree, 那么就从这个tree中根据path,params, unescape 找到对应的handlers
if handlers != nil { //执行handler
c.handlers = handlers
c.Params = params
c.Next()
c.writermem.WriteHeaderNow()
return
}
//异常处理部分
if httpMethod != "CONNECT" && path != "/" {
if tsr && engine.RedirectTrailingSlash {
redirectTrailingSlash(c)
return
}
if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {
return
}
}
break
}
//处理不能响应的method
if engine.HandleMethodNotAllowed {
for _, tree := range engine.trees {
if tree.method == httpMethod {
continue
}
if handlers, _, _ := tree.root.getValue(path, nil, unescape); handlers != nil {
c.handlers = engine.allNoMethod
serveError(c, http.StatusMethodNotAllowed, default405Body)
return
}
}
}
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
抛开其它的部分不看,只看如何根据path, nil, unescape 获取到handlers.
// getValue returns the handle registered with the given path (key). The values of
// wildcards are saved to a map.
// If no handle can be found, a TSR (trailing slash redirect) recommendation is
// made if a handle exists with an extra (without the) trailing slash for the
// given path.
func (n *node) getValue(path string, po Params, unescape bool) (handlers HandlersChain, p Params, tsr bool) {
p = po
walk: // Outer loop for walking the tree
for {
if len(path) > len(n.path) {
if path[:len(n.path)] == n.path {
path = path[len(n.path):]
// If this node does not have a wildcard (param or catchAll)
// child, we can just look up the next child node and continue
// to walk down the tree
if !n.wildChild {
c := path[0]
for i := 0; i < len(n.indices); i++ {
if c == n.indices[i] {
n = n.children[i]
continue walk
}
} // Nothing found.
// We can recommend to redirect to the same URL without a
// trailing slash if a leaf exists for that path.
tsr = path == "/" && n.handlers != nil
return
} // handle wildcard child
n = n.children[0]
switch n.nType {
case param:
// find param end (either '/' or path end)
end := 0
for end < len(path) && path[end] != '/' {
end++
} // save param value
if cap(p) < int(n.maxParams) {
p = make(Params, 0, n.maxParams)
}
i := len(p)
p = p[:i+1] // expand slice within preallocated capacity
p[i].Key = n.path[1:]
val := path[:end]
if unescape {
var err error
if p[i].Value, err = url.QueryUnescape(val); err != nil {
p[i].Value = val // fallback, in case of error
}
} else {
p[i].Value = val
} // we need to go deeper!
if end < len(path) {
if len(n.children) > 0 {
path = path[end:]
n = n.children[0]
continue walk
} // ... but we can't
tsr = len(path) == end+1
return
} if handlers = n.handlers; handlers != nil {
return
}
if len(n.children) == 1 {
// No handle found. Check if a handle for this path + a
// trailing slash exists for TSR recommendation
n = n.children[0]
tsr = n.path == "/" && n.handlers != nil
} return case catchAll:
// save param value
if cap(p) < int(n.maxParams) {
p = make(Params, 0, n.maxParams)
}
i := len(p)
p = p[:i+1] // expand slice within preallocated capacity
p[i].Key = n.path[2:]
if unescape {
var err error
if p[i].Value, err = url.QueryUnescape(path); err != nil {
p[i].Value = path // fallback, in case of error
}
} else {
p[i].Value = path
} handlers = n.handlers
return default:
panic("invalid node type")
}
}
} else if path == n.path {
// We should have reached the node containing the handle.
// Check if this node has a handle registered.
if handlers = n.handlers; handlers != nil {
return
} if path == "/" && n.wildChild && n.nType != root {
tsr = true
return
} // No handle found. Check if a handle for this path + a
// trailing slash exists for trailing slash recommendation
for i := 0; i < len(n.indices); i++ {
if n.indices[i] == '/' {
n = n.children[i]
tsr = (len(n.path) == 1 && n.handlers != nil) ||
(n.nType == catchAll && n.children[0].handlers != nil)
return
}
} return
} // Nothing found. We can recommend to redirect to the same URL with an
// extra trailing slash if a leaf exists for that path
tsr = (path == "/") ||
(len(n.path) == len(path)+1 && n.path[len(path)] == '/' &&
path == n.path[:len(n.path)-1] && n.handlers != nil)
return
}
}
实际上这部分的实现以及insertChild 和 addRoute部分都是基于基树(Radix tree)实现的。关于基树的知识,参考:待研究的那些经典算法
再看看net/http中路由表的设计原理:参考,https://github.com/astaxie/build-web-application-with-golang/blob/master/zh/03.4.md
它是基于Map去实现的,我们知道Map的实现原理是基于哈希表+红黑树来实现,这种设计的好处就是在数据量少的情况下很快,但是占用空间很多。
相比而言,gin的路由表的设计占用的内存就很少,同时查找数据也很快。
go web framework gin 路由表的设计的更多相关文章
- go web framework gin group api 设计
假如让你来设计group api, 你该怎么设计呢? group api 和普通api的区别在于前缀不同,如果group api的版本为v1.0 那么相对应的url为/v1.0/xxx, 如果是普通a ...
- go web framework gin 启动流程分析
最主要的package : gin 最主要的struct: Engine Engine 是整个framework的实例,它包含了muxer, middleware, configuration set ...
- go web framework gin middleware 设计原理
场景:一个middleware可以具体为一个函数,而由前面的gin 路由分析可得,每一个路径都对有一个HandlersChain 与其对应. 那么实际上增加一个middleware的过程,就是将每一个 ...
- 介绍一个python的新的web framework——karloop框架
karloop是一款轻型的web framework,和tornado.webpy类似.mvc分层设计,眼下已经公布早期版本号了,使用方便, 下载地址例如以下:https://github.com/k ...
- Jena 简介:通过 Jena Semantic Web Framework 在 Jave 应用程序中使用 RDF 模型
简介: RDF 越来越被认为是表示和处理半结构化数据的一种极好选择.本文中,Web 开发人员 Philip McCarthy 向您展示了如何使用 Jena Semantic Web Toolkit,以 ...
- web server && web framework角色区分
问题 web framework是否包括webserver? 是否可以包括? webserver 和 framework的关系是? https://www.quora.com/What-is-the- ...
- TCP/IP协议原理与应用笔记23:路由选择模块 和 路由表的设计
1. 路由选择模块 和 路由表的设计 2. 路由选择算法(路由模块在路由表中查找算法) (1)用IP分组中的目的IP地址查找路由表,使用匹配表项的下一跳地址完成分组交付 (2)匹配条件: dIP &a ...
- Web API核查表:设计、测试、发布API时需思考的43件事[转]
Web API核查表:设计.测试.发布API时需思考的43件事 当设计.测试或发布一个新的Web API时,你是在一个原有的复杂系统上构建新的系统.那么至少,你也要建立在HTTP上,而HTTP则是 ...
- Node.js: What is the best "full stack web framework" (with scaffolding, MVC, ORM, etc.) based on Node.js / server-side JavaScript? - Quora
Node.js: What is the best "full stack web framework" (with scaffolding, MVC, ORM, etc.) ba ...
随机推荐
- linux进程虚拟地址空间
转载源 在多任务操作系统中,每个进程都运行在属于自己的内存沙盘中.这个沙盘就是虚拟地址空间(Virtual Address Space),在32位模式下它是一个4GB的内存地址块.在Linux系统中, ...
- windows安装composer总结
1.直接去网吧下载windows安装EXE程序,傻瓜式安装,so easy. 2.通过命令行安装,可以直接在php目录跑起来 php -r "readfile('https://getcom ...
- c#高级编程第七版 学习笔记 第三章 对象和类型
第三章 对象和类型 本章的内容: 类和结构的区别 类成员 按值和按引用传送参数 方法重载 构造函数和静态构造函数 只读字段 部分类 静态类 Object类,其他类型都从该类派生而来 3.1 类和结构 ...
- flutter 获取设备屏幕大小
import 'dart:ui'; var s = window.physicalSize;print(s);
- HAProxy + Keepalived + Flume 构建高性能高可用分布式日志系统
一.HAProxy简介 HAProxy提供高可用性.负载均衡以及基于TCP和HTTP应用的代 理,支持虚拟主机,它是免费.快速并且可靠的一种解决方案.HAProxy特别适用于那些负载特大的web站点, ...
- Oracle 客户端 NLS_LANG 的设置
参考链接1: https://blog.csdn.net/xinzhan0/article/details/78311417#t3 参考链接2: https://blog.csdn.net/xinzh ...
- “Material Design”设计规范在 ComponentOne For WinForm 的全新尝试!
概述 Material Design设计规范的受欢迎程度和实用性已经引起了 ComponentOne 技术团队的重视.ComponentOne Enterprise 2018V3 版本将全面支持Mat ...
- 远程连接报错“This could be due to CredSSP encryption oracle remediation.”
description: 当远程服务器上没有“Encryption Oracle Remediation" setting in local group policy. 尝试远程连接会出现上 ...
- 05. .stop、.prevent、.capture、.self、.once、
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- 【转载】Selenium WebDriver的简单操作说明
转载自:http://blog.csdn.net/xiao190128/article/details/49784121 1.打开一个测试浏览器 对浏览器进行操作首先需要打开一个浏览器,接下来才能对浏 ...