ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

路由规则匹配

2021-10-11 15:03:09  阅读:119  来源: 互联网

标签:Group Request URI 规则 匹配 节点 路由


路由规则设计

路由

HTTP请求包含请求头和请求体 请求体一般存放业务数据,请求头存放和请求状态有关的信息,比如User-Agent浏览器信息,Accept支持返回的文本类型
RequestLine Method Method、Request-URI、HTTP-Version
Request-URI 请求路径 浏览器域名外的剩余部分
HTTP-Version 1.0/1.1/2.0
GET /home.html HTTP/1.1
Host: developer.mozilla.org
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.9; rv:50.0) Gecko/20100101 Firefox/50.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Referer: https://developer.mozilla.org/testpage.html

路由设计和匹配规则

路由设计
REST 业务代码倾向设计REST接口风格
前缀匹配 定制URI倾向于同类型URI归于一类

需求 1:HTTP 方法匹配早期的 WebService 比较简单,HTTP 请求体中的 Request Line 或许只会使用到 Request-URI 部分,但是随着 REST 风格 WebService 的流行,为了让 URI 更具可读性,在现在的路由输入中,HTTP Method 也是很重要的一部分了,所以,我们框架也需要支持多种 HTTP Method,比如 GET、POST、PUT、DELETE。
需求 2:静态路由匹配静态路由匹配是一个路由的基本功能,指的是路由规则中没有可变参数,即路由规则地址是固定的,与 Request-URI 完全匹配。我们提到的 DefaultServerMux 这个路由器,从内部的 map 中直接根据 key 寻找 value ,这种查找路由的方式就是静态路由匹配。
需求 3:批量通用前缀因为业务模块的划分,我们会同时为某个业务模块注册一批路由,所以在路由注册过程中,为了路由的可读性,一般习惯统一定义这批路由的通用前缀。比如 /user/info、/user/login 都是以 /user 开头,很方便使用者了解页面所属模块。所以如果路由有能力统一定义批量的通用前缀,那么在注册路由的过程中,会带来很大的便利。
需求 4:动态路由匹配这个需求是针对需求 2 改进的,因为 URL 中某个字段或者某些字段并不是固定的,是按照一定规则(比如是数字)变化的。那么,我们希望路由也能够支持这个规则,将这个动态变化的路由 URL 匹配出来。所以我们需要,使用自己定义的路由来补充,只支持静态匹配的 DefaultServerMux 默认路由。

需求1+需求2 : 
有两个待匹配的规则,Request-URI 和 Method,所以自然联想到可以使用两级哈希表来创建映射。
第一级 hash 是请求 Method,第二级 hash 是 Request-URI。
需求3 批量通用前缀:
通过一个 Group 方法归拢路由前缀地址,处理批量前缀问题
Group 方法,它的参数是一个前缀字符串,返回值应该是包含 Get、Post、Put、Delete 方法的一个结构,我们给这个结构命名 Group,在其中实现各种方法。
这么设计直接返回 Group 结构,确实可以实现功能,但试想一下,随着框架发展,如果我们发现 Group 结构的具体实现并不符合我们的要求了,需要引入实现另一个 Group2 结构,该怎么办?
直接修改 Group 结构的具体实现么?其实更好的办法是使用接口来替代结构定义。在框架设计之初,我们要保证框架使用者,在最少的改动中,就能流畅迁移到 Group2
如果返回接口 IGroup,而不是直接返回 Group 结构,就不需要修改 core.Group 的定义了,只需要修改 core.Group 的具体实现,返回 Group2 就可以。尽量使用接口来解耦合,是一种比较好的设计思路。怎么实现呢,这里我们定义 IGroup 接口来作为 Group 方法的返回值
接口是一种协议,它忽略具体的实现,定义的是两个逻辑结构的交互,因为两个函数之间定义的是一种约定,不依赖具体的实现。你可以这么判断:如果你觉得这个模块是完整的,而且后续希望有扩展的可能性,那么就应该尽量使用接口来替代实现。
多大程度使用接口进行逻辑结构的交互,是评价框架代码可扩展性的一个很好的标准
使用 IGroup 接口后,core.Group 这个方法返回的是一个约定,而不依赖具体的 Group 实现
需求4 动态路由匹配:
一旦引入了动态路由匹配的规则,之前使用的哈希规则就无法使用了。因为有通配符,在匹配 Request-URI 的时候,请求 URI 的某个字符或者某些字符是动态变化的,无法使用 URI 做为 key 来匹配。那么,我们就需要其他的算法来支持路由匹配
这个问题本质是一个字符串匹配,而字符串匹配,比较通用的高效方法就是字典树,也叫 trie 树。这里,我们先简单梳理下 trie 树的数据结构。trie 树不同于二叉树,它是多叉的树形结构,根节点一般是空字符串,而叶子节点保存的通常是字符串,一个节点的所有子孙节点都有相同的字符串前缀。
这个 trie 树是按照路由地址的每个段 (segment) 来切分的,每个 segment 在 trie 树中都能找到对应节点,每个节点保存一个 segment。树中,每个叶子节点都代表一个 URI,对于中间节点来说,有的中间节点代表一个 URI(比如上图中的 /subject/name),而有的中间节点并不是一个 URI(因为没有路由规则对应这个 URI)
可以用 matchNode 方法,寻找某个路由在 trie 树中匹配的节点,如果有匹配节点,返回节点指针,否则返回 nil。matchNode 方法的参数是一个 URI,返回值是指向 node 的指针,它的实现思路是使用函数递归

修改文件

定义路由 map
两级Map处理REST风格+Request-URI
注册路由
这里将 URL 全部转换为大写了,在后续匹配路由的时候,也要记得把匹配的 URL 进行大写转换,这样我们的路由就会是“大小写不敏感”的,对使用者的容错性就大大增加了
匹配路由
填充 ServeHTTP 方法

标签:Group,Request,URI,规则,匹配,节点,路由
来源: https://blog.csdn.net/qq_38684512/article/details/120697422

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有