当前位置: 首页 > news >正文

c mvc网站开发实例教程绍兴seo排名

c mvc网站开发实例教程,绍兴seo排名,网络营销的概念及功能,建工网和环球网哪个好4. gin框架源码–Engine引擎和压缩前缀树的建立 讲了这么多 到标题4才开始介绍源码#xff0c;主要原因还是想先在头脑中构建起 一个大体的框架 然后再填肉 这样不容易得脑血栓。标题四主要涉及标题2.3的步骤一 也就是 标题2.3中的 粗线框中的内容 4.1 Engine 引擎的建立 见…4. gin框架源码–Engine引擎和压缩前缀树的建立 讲了这么多 到标题4才开始介绍源码主要原因还是想先在头脑中构建起 一个大体的框架 然后再填肉 这样不容易得脑血栓。标题四主要涉及标题2.3的步骤一 也就是 标题2.3中的 粗线框中的内容 4.1 Engine 引擎的建立 见 TestGin的第 一行 我们按图索骥 一步步深入看看 只需关注 我中文注释的属性或代码就行 r : gin.Default() // 建立一个默认的 gin 引擎实例其中函数 Default() 代码 如下 // Default returns an Engine instance with the Logger and Recovery middleware already attached. func Default() *Engine {debugPrintWARNINGDefault() // 打印 警告信息engine : New() // 建立 engine 实例engine.Use(Logger(), Recovery()) // 添加中间件 函数 包括 日志和panic恢复函数return engine } 其中 New()函数 代码如下只关注我中文解释的参数就行 // New 返回一个不带任何中间件函数的新的engine实例 func New() *Engine {debugPrintWARNINGNew()engine : Engine{RouterGroup: RouterGroup{ // 建立默认根路由组 也就是说 所有的 路由 都需要经过本 在前面加上 “/” 后续路由使用Group(...)函数创建 会在其基础上 不断叠加更新 basePath和Handlers 但其功能还是一样的Handlers: nil,basePath: /,root: true,},FuncMap: template.FuncMap{},RedirectTrailingSlash: true,RedirectFixedPath: false,HandleMethodNotAllowed: false,ForwardedByClientIP: true,RemoteIPHeaders: []string{X-Forwarded-For, X-Real-IP},TrustedPlatform: defaultPlatform,UseRawPath: false,RemoveExtraSlash: false,UnescapePathValues: true,MaxMultipartMemory: defaultMultipartMemorytrees: make(methodTrees, 0, 9), // 初始化 方法树的 列表 包括 Get/Post/Delete等九中方法delims: render.Delims{Left: {{, Right: }}},secureJSONPrefix: while(1);,trustedProxies: []string{0.0.0.0/0, ::/0},trustedCIDRs: defaultTrustedCIDRs,}engine.RouterGroup.engine engine // 将新建的 本 engine 索引付给 engine.RouterGroup.engine 方便 路由组引用 本engine的 addRoute()方法 engine.pool.New func() any { // 初始化context池 因为涉及到大量客户端连接所以将context池化 减少对象的创建和回收次数 可以节省内存 减少垃圾回收时间 提高效率return engine.allocateContext(engine.maxParams)}return engine }其中 engine.Use(Logger(), Recovery()) 中 Use()函数 如下 func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {engine.RouterGroup.Use(middleware...) // 将中间件函数 加入到 RouterGroup的参数 Handlers 列表中 作为本路由组的整体的中间函数 engine.rebuild404Handlers() engine.rebuild405Handlers()return engine } 到这里 r : gin.Default() 这行代码就讲解完毕了 比较粗略 因为这部分源码不太难 主要是 完成了两件事 1 创建了一个默认路由组主要来存放根 路径 “/” 和 全局中间函数 包括 对日志和panic的处理 2 初始化了一个 methodTrees 结构体 用来 保存 get/post等9种方法树的根节点 还有其他初始参数 跟本文讲解的内容关系不大 不做讲解 感兴趣的可以自己谷歌上百度两下。 到这里 默认引擎就建立起来了 。 4.2 GET方法 路由节点树的逐步建立 4.2.1 树初始化 和 第一个路由节点的建立 见 TestGin的第 二行代码如下 r.GET(/aa/bb, func(c *gin.Context) { c.JSON(200, gin.H{route path : /aa/bb}) })第一次建立路由树时树是空的所以需要经过判断建立根节点和直接将路由和函数 挂到树上。 树建立后的结构如下(handler也就是 注册的函数 在路径插入时插入故不在图中展示只在有特殊情况时说明。) 知道了结果现在我们来梳理下代码 从 r.Get 追踪下来 可见如图代码 func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {absolutePath : group.calculateAbsolutePath(relativePath) // 组的根路径/用户路径/aa/bbhandlers group.combineHandlers(handlers) // 组的根方法组日志相关panic相关用户注册方法c.Json......group.engine.addRoute(httpMethod, absolutePath, handlers) // 构造路由树核心函数 将 absolutePath 和 handler 添加到 httpMathod 方法Get对应的 路由树上return group.returnObj() // 没看懂 不过不影响 哦 不对 主逻辑 ps: 不是能力不行 写这篇代码时 有点困 没深究呢 }接着追踪 group.engine.addRoute(httpMethod, absolutePath, handlers) func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {// 隐藏无关代码root : engine.trees.get(method)if root nil { // 第一次 建立路由 会走这里 主要是 初始化 路由树的 根节点将根节点挂载到 engine的 trees 列表中root new(node)root.fullPath /engine.trees append(engine.trees, methodTree{method: method, root: root})}root.addRoute(path, handlers) // 构造路由树核心函数将 路径/aa/b 和 方法(3个) 添加到路由树上 根节点是root// 隐藏无关代码 }接着追踪 root.addRoute(path, handlers) func (n *node) addRoute(path string, handlers HandlersChain) {fullPath : pathn.priority // priority 可以理解 以n为根节点的孩子节点的总个数 主要作用是 用来对 其第一层孩子节点进行重排按照值大小倒序排列。为什么这么做呢因为孩子节点多说明通过这个节点的路由就多 访问就越频繁 在for循环寻找路由时 排在前面的经常被访问的节点 可以快速找到 从而可以提高效率。后续会讲解 如果有疑惑 先放着// Empty treeif len(n.path) 0 len(n.children) 0 { // n 是上面代码中的 root 可以看到 只有 fullPath有值path和children都是 零值其含义见3.3n.insertChild(path, fullPath, handlers) // 为root根节点补充完整参数。因为其没有孩子节点 path也为空 则可以判断是空树 所以 root节点就是第一个节点 只需补充完整其参数就行n.nType rootreturn}parentFullPathIndex : 0// 以下代码是 gin框架构造路由树核心只是初始构造路由树用不到先排除干扰 }接着追踪 n.insertChild(path, fullPath, handlers) func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {for {// Find prefix until first wildcardwildcard, i, valid : findWildcard(path) // 寻找通配符 本文暂时只介绍 gin框架 精髓 无通配符路由节点建立 if i 0 { // No wildcard found //i-1 breakbreak}// 隐藏无关代码}// If no wildcard was found, simply insert the path and handle 没有通配符 简单的插入 路径和handlern.path path // /aa/bbn.handlers handlers // 3个 日志相关panic相关用户注册方法c.Json......n.fullPath fullPath // (/aa/bb) } 至此 第一个 节点便建立起来了TestGin的第 二行执行完毕后其engine结构如下图可以看到root节点确实如分析的一般。 4.2.2 第2个路由路径插入 代码 如图 这行代码执行完毕后得到的树如图 下面我们来具体分析下代码 通过代码追踪 可以看到 第二个节点建立时 跳过了 root节点初始化和 第一个节点建立的代码来到了 addRoute函数的核心部分 接着追踪 root.addRoute(path, handlers) func (n *node) addRoute(path string, handlers HandlersChain) {fullPath : pathn.priority // 隐藏无关代码parentFullPathIndex : 0walk:for {// Find the longest common prefix.// This also implies that the common prefix contains no : or *// since the existing key cant contain those chars.i : longestCommonPrefix(path, n.path) // n是4.2.1形成的第一个节点 则 n.path /aa/bb path/aa/bd longestCommonPrefix 函数是寻找两个字符串第一个不相同的字符的 索引 这里可以算出来 是 5。// 这个函数的作用决定父节点是否需要被拆分, 当n.path是path的子节点时 不被拆分否则就需要被拆分。// Split edgeif i len(n.path) { // 56 走这里 拆分父节点为两部分 /aa/b根节点 和 b第一个孩子节点 下面具体讲解child : node{ // b 孩子节点的创建 path: n.path[i:], // 将 b赋值给 pathwildChild: n.wildChild,nType: static,indices: n.indices, // child继承了 n的主要参数和功能children: n.children, // 将其孩子节点赋值给 其 分裂的前缀节点handlers: n.handlers,priority: n.priority - 1, // 没看懂fullPath: n.fullPath, // 全路径不变 还是/aa/bb}n.children []*node{child} // 初始化n节点并且将 child加入到n的孩子节点中这样 就形成了 n--n.children--n.children.children这样三层节点结构// []byte for proper unicode char conversion, see #65n.indices bytesconv.BytesToString([]byte{n.path[i]}) // 下面是 重新给n的属性赋值n.path path[:i]n.handlers niln.wildChild falsen.fullPath fullPath[:parentFullPathIndexi]}// Make new node a child of this node // 将n 的第二个节点d插入进去if i len(path) { // 56 将 新节点 d插入进去path path[i:] //新节点的 path参数是是 dc : path[0] // 获取path第一个 字符用于快速定位应该从哪一个节点向下匹配。// / after param// todo 没看懂if n.nType param c / len(n.children) 1 {// 隐藏无关代码}// Otherwise insert itif c ! : c ! * n.nType ! catchAll {// []byte for proper unicode char conversion, see #65n.indices bytesconv.BytesToString([]byte{c}) // 将 n的新孩子节点前缀字符加入到 indices 中child : node{fullPath: fullPath,}n.addChild(child) // 将孩子节点添加到 n的孩子节点数组中n.incrementChildPrio(len(n.indices) - 1) // 这里比较重要 用到了我们所说的 priority 按照 其值 大小进行子节点倒序排序。原因前文讲过了请自行查看。n child // 将 child地址 赋值给 n。} else if n.wildChild {// 这里是对通配符的处理本文暂不涉及略过。}n.insertChild(path, fullPath, handlers) // 完善nchild的 pathfullPath,handlers ,insertChild函数其实就是给节点n的几个属性赋值所以感觉起得名字有点误导。return // 返回 到这里 第二个节点就创建好了 }// Otherwise add handle to current nodeif n.handlers ! nil {panic(handlers are already registered for path fullPath )} // 如果ilen(path) 就可能是情况 n.path/aa/bb path/aa/b 。则分类后 一共 两个节点 没有 d节点。 则需要将 handler和fullPath 赋值当前n节点。n.handlers handlersn.fullPath fullPathreturn 返回} } 到这里第二个路由节点就插入进去了。 我们看下 debugger的结果 可以看到跟我们分析的是一致的。 4.2.3 第3-5个路由路径的插入 由于其 执行的动作跟 4.2.2一致所以略过。其构建树的过程如下图 4.2.4 第6个路由的插入 4.2.1–4.2.3是正常路由的插入一次循环就能完成。没有涉及到 addRoute() 函数的关键字 walk: 接下来 我们再引入一个路由 来触发 for 循环添加的路由如下图 这行代码执行完毕后得到的树如图 下面 我们来分析下具体的代码执行 还是直接 分析 root.addRoute(path, handlers) 第1层循环 func (n *node) addRoute(path string, handlers HandlersChain) {fullPath : pathn.priority// Empty tree// 隐藏无关代码parentFullPathIndex : 0 // 循环第一层 沿着 树节点 向下找 则n节点 是4.2.3最右边的图 以下 n的参数都可以参考这个图来获取 walk:for {// Find the longest common prefix.// This also implies that the common prefix contains no : or *// since the existing key cant contain those chars.i : longestCommonPrefix(path, n.path) // n.path / path/aa/be 如此i1// Split edgeif i len(n.path) { // 不符合 跳过// 隐藏无关代码}// Make new node a child of this nodeif i len(path) { // 符合path path[i:] // 获取 除了根节点“/”的 剩余部分 aa/bec : path[0] // 将 aa/be 的 第一个字符 a 提取出来 为了 快速在 n的孩子节点定位和将c插入n的 indices中如果没有匹配n的子节点// / after paramif n.nType param c / len(n.children) 1 {// 隐藏无关代码}// Check if a child with the next path byte existsfor i, max : 0, len(n.indices); i max; i { // n.indices{a,e}if c n.indices[i] { // 因为crune(a) 所以 走下面代码parentFullPathIndex len(n.path) // 下一个循环 中 节点的 fullPath在 全局全路径 中出现的索引。i n.incrementChildPrio(i) // 因为接下来的 新节点 要加入到 包含 n.children[i]节点的后续子节点中 所以其 孩子几点数priority 要增1然后返回排序后的 原始的n.children[i]节点的新的索引 给 in n.children[i] // 将孩子节点赋值给 n continue walk // 从 新节点开始继续循环 见 4.2.3最右边节点的第2层最左边节点}}// 隐藏无关代码} }第2-3层循环跟上面代码 原理相同 可参考 4.2.3最右边节点的第2-3层 第4层循环时 n.path“b” path“be” 其 执行逻辑可以按照 4.2.3执行 请自行验证 至此 树节点的建立就梳理完毕了注意只是梳理了不带通配符的路由处理逻辑关于通配符 例如 * 等特殊字符请自行梳理。 梳理了这么多知识点 都是属于 1.3 的圆圈1下面我们改建立起tcp监听了。 5. net/http框架源码-- 多路复用的实现 这块核心功能对应 1.3 的圆圈2所属代码如下图 未完待续… 6. gin框架源码–路由匹配压缩前缀树的查找 7. 收尾 ps: 本人菜鸟 不太专业 如果有错还请各位大侠指出免责声明凡是按照本八股文去面试被怼的本人概不承担责任面试通过请自行心理默念几遍博主最帅。 参考文章 https://juejin.cn/post/7263826380889915453
http://www.hkea.cn/news/14349582/

相关文章:

  • 视频背景做网站背景郑州专业手机网站制作
  • 免费行情软件网站下载大全福建网站开发
  • 个人网站备案简介怎么写国外网站域名
  • 购物网站 设计网站实名认证流程
  • 广州市城乡建设部网站首页微营销方案
  • 无锡网站制作公司青岛网页设计公司
  • 潍坊网站建设一站式服务有了页游源代码如何做网站
  • 长沙宁乡建设网站钓鱼网站链接怎么做
  • 百度大数据分析网站推广优化外包便宜
  • dede如何手机网站和电脑网站的数据同步更新企业做网站哪家便宜
  • 泉州手机端建站模板wordpress更新的文章编辑器不好用
  • 做网站 赚钱多吗网站运营是具体的
  • 如何制作wordpress网站地图网站开发定价
  • 做营销型网站要多少钱圣辉友联刘金鹏做网站
  • 石家庄企业网站wordpress 获取自定义字段值
  • 哈尔滨模板建站服务商手机网站什么意思
  • 优质高职院校建设网站网站策划与建设阶段的推广方法
  • 外贸cms建站wordpress seo怎么
  • 海珠网站建设公阿里巴巴网站建设要多少钱
  • linux做网站南沙门户网站建设
  • 做音乐相册的网站小红书推广方式如何引流
  • 廉江网站建设微博推广有用吗
  • 洪梅镇网站建设公司西安煤炭建设监理中心网站
  • 深圳百度推广网站建设网店代运营的公司有哪些
  • 电子商务推荐类网站建设的目的西安企业网站设计哪家专业
  • 免费网站模板html深圳网站建设 公司
  • 网站分析报告范文网站开发用哪个软件方便
  • phpcms网站备份竹林wordpress主题
  • 廊坊市 广阳区城市建设局网站淮南市网站建设
  • 网站建设首选云端高科中国设计之家