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

如何提高网站的自然排名asp网站文章自动更新

如何提高网站的自然排名,asp网站文章自动更新,大型门户网站建设所具有的功能模块主要有几种类型,带分页的wordpress模板go web框架 gin-gonic源码解读02————router 本来想先写context#xff0c;但是发现context能简单讲讲的东西不多#xff0c;就准备直接和router合在一起讲好了 router是web服务的路由#xff0c;是指讲来自客户端的http请求与服务器端的处理逻辑或者资源相映射的机制。但是发现context能简单讲讲的东西不多就准备直接和router合在一起讲好了 router是web服务的路由是指讲来自客户端的http请求与服务器端的处理逻辑或者资源相映射的机制。这里简单说说详细的定义网上都可以查到 那一个优秀的web router应该提供以下功能: URL解析路由的过程始于URL解析。URL是一个标识资源位置的字符串通常由协议、主机名、端口号和路径组成。服务器需要解析这个URL以便找到对应的处理程序或资源。路由规则在Web应用程序中通常会定义一组路由规则这些规则将特定的URL模式与相应的处理程序或资源进行绑定。路由规则可以基于URL路径、HTTP方法GET、POST等、查询参数和其他条件来匹配请求。动态路由除了静态路由固定的URL与处理程序映射外现代Web应用程序还支持动态路由。动态路由允许使用参数将URL与处理程序关联起来。这样同一种类型的请求可以通过不同的参数调用不同的处理程序实现更灵活和通用的路由。… 为了展示gin框架的router的优越性我们先看看go原生的net/http处理不是说net/http不好只是说说他的一些小缺点不要网暴我 这里摘抄部分net/http默认的路由器的代码 // GO\src\net\http\server.go// ServeMux就是我们net/http默认的router type ServeMux struct {mu sync.RWMutex // 大家都知道为了并发安全搞得读锁 m map[string]muxEntry // 这个map就是我们路由的核心es []muxEntry // slice of entries sorted from longest to shortest.// 为了前缀匹配搞得切片后面func match() 方法你一看就知道他是干什么用的了 hosts bool // whether any patterns contain hostnames }// 上一篇文章说过的每个Server都要实现的ServeHTTP(w ResponseWriter, r *Request)接口会调用这个方法来获取要执行的h Handler(可以理解为这个传参path的这个url对应的逻辑) func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()// Host-specific pattern takes precedence over generic ones// 本代码段行6的波尔值这个配置一般是有要区别不同主机会有不同逻辑的时候才会用if mux.hosts {// 这个match就是我们net/http的匹配算法h, pattern mux.match(host path)}if h nil {// 这个match就是我们net/http的匹配算法h, pattern mux.match(path)}if h nil {// 默认的404处理返回函数h, pattern NotFoundHandler(), }return }// 匹配算法 非常的简单特别是和后面的gin比起来 func (mux *ServeMux) match(path string) (h Handler, pattern string) {// Check for exact match first.// 很简单直接去map里find其实效率也还可以但是这里你就能看出来他只能支持静态路由map这里也不支持模糊搜索v, ok : mux.m[path]if ok {// return v.h, v.pattern}// Check for longest valid match. mux.es contains all patterns// that end in / sorted from longest to shortest.// 这里英文注释也蛮清楚的就是map里找不到这里找一下以入参path为前缀的url。// 并且这个mux.es还是有序的为了提升一点效率从这里看他似乎也不是完全静态的。for _, e : range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, } 这里篇幅有限只讲讲net/http的matchinster就不说了。 从上面的macth代码也可以看出net/http的路由存在以下缺点 缺乏灵活的路由定义net/http 包的路由定义相对简单只能通过 http.HandleFunc 或 http.Handle 来定义路由处理函数。这导致难以支持复杂的路由模式如正则表达式匹配、参数提取等 不支持动态路由这个包并没有原生支持动态路由即不能直接将路由和参数绑定起来需要手动解析 URL。 不支持中间件中间件是一种常用的扩展路由处理的方法可以在请求到达路由处理函数之前或之后执行一些操作。然而net/http 包没有内置的中间件支持需要手动编写和管理中间件。 不支持子路由在一些应用场景中需要将不同类型的请求映射到不同的处理函数这些处理函数可能共享某些共同的前缀。net/http 包并没有内置的子路由支持需要自己实现。 不支持路由组在一些情况下希望将一组相关的路由规则进行分组管理以便更好地组织代码。net/http 包没有原生支持路由组的功能。 接下来正片开始讲router主要讲两个函数match路由匹配insert路由注册 gin的路由数据结构 和大多说的web框架一样gin选择了使用前缀树算法来进行路由匹配因为确实蛮合适的。前缀树这边不细讲了蛮简单的大家可Google看看。这里直接撸gin的源码。 // ../gin/tree.go// static表示静态节点。静态节点是指路由路径中没有参数或通配符的节点其值是固定的字符串。例如路径 /home 中的 home 就是一个静态节点。 // root表示根节点。根节点是整个路由树的顶级节点它没有路径其作用是起始点用于构建路由树的根结构。 // param表示参数节点。参数节点是指路由路径中的一部分可以是变量的节点例如 /user/:id 中的 :id 就是一个参数节点可以匹配任意值。 // catchAll表示通配符节点。通配符节点是指路由路径中的一部分可以匹配任意内容的节点例如 /static/*filepath 中的 *filepath 就是一个通配符节点可以匹配以 /static/ 开头的所有路径。type nodeType uint8const (static nodeType iotarootparamcatchAll ) // 这个结构存放的是每个方法的根节点如GETPOSTPUT他们的根节点也是这种方式在Engine中存储的 // type Engine struct { // ... // // 简单来书就是每种请求方法是一颗独立的前缀树 // trees methodTrees // ... // } type methodTrees []methodTree // 一个简单的get方法 func (trees methodTrees) get(method string) *node {for _, tree : range trees {if tree.method method {return tree.root}}return nil }type methodTree struct {method stringroot *node }// 树的各个节点 type node struct {// 到该节点的路由路径片段例如/home那他就是hemopath string// 索引下文细讲indices string// 子节点中是否是有通配符节点有的话插入新的节点时要注意维护通配符节点是最后一个节点wildChild bool// 上文提到的该节点的类型nType nodeType// 优先级下文细讲priority uint32// 子节点gin是以字符串的每个字符当做节点的children []*node// 所有的中间件和对应的url处理的逻辑函数后续讲中间件的时候细讲为什么中间件和服务逻辑函数写在一个数组里handlers HandlersChain// 全路径fullPath string } 路由器的节点插入路由绑定 使用过gin框架的同学都知道我们在使用go-gin时只需要 gin.GET(“/hello/world”, helloworld) 这么一句简单的代码就可以实现url/hello/world 和 逻辑函数helloworld()的绑定接下来让我们看看func GET()里都发生了什么。 func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers ...HandlerFunc) IRoutes {if matched : regEnLetter.MatchString(httpMethod); !matched {panic(http method httpMethod is not valid)}return group.handle(httpMethod, relativePath, handlers) }// POST is a shortcut for router.Handle(POST, path, handlers). func (group *RouterGroup) POST(relativePath string, handlers ...HandlerFunc) IRoutes {return group.handle(http.MethodPost, relativePath, handlers) }// GET is a shortcut for router.Handle(GET, path, handlers). func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {return group.handle(http.MethodGet, relativePath, handlers) } 当然不止get和postgin还定义各种其他的http的请求方法但是都大同小异这边以get和post举例。 从这里可以看出来不管是什么http的方法最终调用的都是Handle 方法并且讲请求方法作以string的方式传入。比如注释上说的 // POST is a shortcut for router.Handle(“POST”, path, handlers) func (group *RouterGroup) Handle(httpMethod, relativePath string, handlers …HandlerFunc) IRoutes {} 而Handle()方法之中又调用了一个handle方法 // gin.gofunc (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {// (group *RouterGroup) RouterGroup 大家常用也很熟悉就是我们的url分组我们开发中会为每个url分组设置一个统一的前缀url// group.calculateAbsolutePath(relativePath)这一步是为了帮助我们拼接处正确的url全文absolutePath : group.calculateAbsolutePath(relativePath)// handlers 就是我们这篇文章上面所讲的node 结构体中的handlers 。// 这一步是为了把你注册路由绑定的服务逻辑方法绑定到调用链上这个下一章讲中间件的时候会细讲handlers group.combineHandlers(handlers)// 终于到了最终的一步AddRouter这个方法里就是我们路由器的节点插入路由绑定核心方法group.engine.addRoute(httpMethod, absolutePath, handlers)return group.returnObj() }// 核心方法 addRoute 这里为了篇幅我会做一些摘抄 func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {// 做一些断言检测一下输入的url是否合法比较越早阶段的暴露问题是程序的铁律assert1(path[0] /, path must begin with /)assert1(method ! , HTTP method can not be empty)assert1(len(handlers) 0, there must be at least one handler)// debug模式下一些日志打印不太重要debugPrintRoute(method, path, handlers)// engine.tree中存储了一个http请求方法的字符串为根节点的切片所以这里我们拿http请求方法method先get// 这个get方法在上文有大家可以自己拉上去看看root : engine.trees.get(method)// 一个好的设计肯定不可能一开始把所有的可能性都构造出来这边也是管你是get还是post都是用到了再创建然后插入。if root nil {root new(node)root.fullPath /// 这种http请求方法是第一次出现这边创建一个并且插入engine.trees append(engine.trees, methodTree{method: method, root: root})}// 核心的核心来了大家请看下面的代码块tree.goroot.addRoute(path, handlers)// Update maxParamsif paramsCount : countParams(path); paramsCount engine.maxParams {engine.maxParams paramsCount}if sectionsCount : countSections(path); sectionsCount engine.maxSections {engine.maxSections sectionsCount} }tree.go // tree.go// 核心的核心非常的长 func (n *node) addRoute(path string/*绑定的url路径*/, handlers HandlersChain/*绑定的逻辑函数*/) {fullPath : path// 优先级主要用于每个节点子节点排序提高查找的效率n.priority// Empty treeif len(n.path) 0 len(n.children) 0 {n.insertChild(path, fullPath, handlers)n.nType rootreturn}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.// 大家注意哈我们这里进循环了请开始转动你的小脑瓜// 就是找当前节点的路径和你patch最长的相同的前缀并且放回下标i : longestCommonPrefix(path, n.path)// Split edge// 显然现在的path和该节点的path有不相同的地方说明我们的前缀树要开始分叉了// 接下来我们要做的就是把我们传入的path从不相同的地方开始拆分成两个节点。if i len(n.path) {// 创建一个孩子节点 初始化的时候我们先把大部分的值设置的和我们的当前节点一致child : node{// 这里就是我说的拆分的地方你看从两者字符串不同的地方的下标剪断了path: n.path[i:],wildChild: n.wildChild,nType: static,indices: n.indices,children: n.children,handlers: n.handlers,// 这个开始拆分就表示他至少会有两个子节点优先级就降低了priority: n.priority - 1,fullPath: n.fullPath,}// 由于一个分支下的所有孩子节点都会有相同的前缀也就是他们父节点所记录的值这里字符出现了不同// 其实就是我们当前节点成了父节点我们要把相同的部分留给当前节点然后不同部分分成两个新的子节点// 所以这里先把当前节点拆了拆一个子节点出来。n.children []*node{child}// []byte for proper unicode char conversion, see #65// 这段代码看完也基本解开了indices这个索引变量的神秘面纱了他其实就是该节点的所有字节的首字符// 拼接在一起的一个字符串方便后面的查找这里只有一个i是因为他把当前节点拆成了0-i和i-len两个节点// 在我们path插入之前他肯定只有一个孩子节点。n.indices bytesconv.BytesToString([]byte{n.path[i]})n.path path[:i]// 当前节点现在被拆分了他现在只是前缀树上的一个分叉节点它本身并不代表某个url所以给他的这两个参数置为nil和falsen.handlers niln.wildChild falsen.fullPath fullPath[:parentFullPathIndexi]}// Make new node a child of this node// 当前节点虽然已经被拆成了两个节点父节点当前node与我们字符匹配的部分-- 子节点当前node与我们字符不匹配的部分// 当时我们自己的path还没有变成节点插入呢。这里会有两种情况一种是字符串匹配下标i小于path的长度或者大于等于path的长度// 这里我们分开处理先说 i len(path) 这种情况下我们tree会是下图这种情况// 父节点当前node与我们字符匹配的部分// |-- 子节点1当前node与我们字符不匹配的部分// |-- 字节点2我们的path// 这里其实就两种情况。1.ilen(path);2.ilen(path);1情况下面还有几种情况要处理// 而情况2相当于当前节点就是我们的path了需要给他绑定逻辑函数handlersif i len(path) {// path的前半段path[0-i]已经被当前节点node表示所以这里裁掉path path[i:]c : path[0]// / after param// 这里处理一种特殊情况当前节点是param参数节点且他只有一个子节点并且我们用来查找的字符串是/// 那就这个节点这里两者是可以匹配上的那我们直接把当前节点变成子节点继续匹配。if n.nType param c / len(n.children) 1 {parentFullPathIndex len(n.path)// continue了请大家带着当前所有的记忆返回 代码行walk:n n.children[0]n.prioritycontinue walk}// Check if a child with the next path byte exists// 遍历当前节点的所有索引其实就是看看孩子节点里有没有哪个首字符和我们path首字符一样的for i, max : 0, len(n.indices); i max; i {if c n.indices[i] {parentFullPathIndex len(n.path)// 有的话这个孩子就是我们的当前节点了所以我们要维护一下这个children节点并且再次拿到他的下标// 维护的方法incrementChildPrio()这个的代码我贴在下面了i n.incrementChildPrio(i)n n.children[i]// continue了请大家带着当前所有的记忆返回 代码行walk:continue walk}}// Otherwise insert it// 走到这一步说明当前节点的所有的子节点都没有匹配上我们先看看要插入的path是否是匹配路径(c ! : c ! *)// 再看看我们的当前节点是否是匹配节点(n.nType ! catchAll)if c ! : c ! * n.nType ! catchAll {// 如果都不是那说明是个正常节点和正常path,那我们把这个path当这个正常子节点先给他创造结构体后续统一插入// []byte for proper unicode char conversion, see #65// 更新当前节点的索引n.indices bytesconv.BytesToString([]byte{c})// 创建结构体等待后续插入child : node{fullPath: fullPath,}// 给当前节点插入子节点n.addChild(child)// 维护索引n.indices有序n.incrementChildPrio(len(n.indices) - 1)n child} else if n.wildChild {// 到这一步说明当前节点的子节点中有通配符节点那我们直接取子节点的最后一个节点// 插入子节点的时候我们会特意维护通配符节点的最后一个这样子取用起来也很方便// inserting a wildcard node, need to check if it conflicts with the existing wildcardn n.children[len(n.children)-1]n.priority// Check if the wildcard matches// 检查n是否和path匹配这里n n.children[len(n.children)-1]了if len(path) len(n.path) n.path path[:len(n.path)] // Adding a child to a catchAll is not possiblen.nType ! catchAll // Check for longer wildcard, e.g. :name and :names(len(n.path) len(path) || path[len(n.path)] /) {// 匹配上了我们直接continue 整个再来一次continue walk}// Wildcard conflict// 这都没匹配上说明出问题了这里拼接一下错误panic了// 一般是同一级分支下出现了两个同级的通配符 ,示例可以看下文的func TestTreePanic1(t *testing.T)pathSeg : pathif n.nType ! catchAll {pathSeg strings.SplitN(pathSeg, /, 2)[0]}prefix : fullPath[:strings.Index(fullPath, pathSeg)] n.pathpanic( pathSeg in new path fullPath conflicts with existing wildcard n.path in existing prefix prefix )}// 这里说明c是通配符(c : || c *)// 或n是全匹配节点(n.nType ! catchAll)// 并且都当前节点没有通配符子节点直接插入n.insertChild(path, fullPath, handlers)return}// Otherwise add handle to current nodeif n.handlers ! nil {panic(handlers are already registered for path fullPath )}// 迭代到这里了就是当前节点n就是我们path了需要最后做一些赋值n.handlers handlersn.fullPath fullPathreturn} }func longestCommonPrefix(a, b string) int {i : 0max : min(len(a), len(b))for i max a[i] b[i] {i}return i }// Increments priority of the given child and reorders if necessary // 这里主要做了一个排序操作维护了一下node的子节点切片使他们以priority的 大小为规则排序 // 排序之后我们要拿的那个儿子节点的下标有可能会改变所以还要返回一个维护过得newpos来保证返回的是正确的坐标 func (n *node) incrementChildPrio(pos int) int {cs : n.children// 优先级现先cs[pos].priorityprio : cs[pos].priority// Adjust position (move to front)newPos : pos// 经典冒泡根据优先级priority进行排序for ; newPos 0 cs[newPos-1].priority prio; newPos-- {// Swap node positionscs[newPos-1], cs[newPos] cs[newPos], cs[newPos-1]}// Build new index char stringif newPos ! pos {n.indices n.indices[:newPos] // Unchanged prefix, might be emptyn.indices[pos:pos1] // The index char we moven.indices[newPos:pos] n.indices[pos1:] // Rest without char at pos}return newPos } func findWildcard(path string) (wildcard string, i int, valid bool) {// Find startfor start, c : range []byte(path) {// A wildcard starts with : (param) or * (catch-all)if c ! : c ! * {continue}// Find end and check for invalid charactersvalid truefor end, c : range []byte(path[start1:]) {switch c {case /:return path[start : start1end], start, validcase :, *:valid false}}return path[start:], start, valid}return , -1, false } func (n *node) insertChild(path string, fullPath string, handlers HandlersChain) {for {// Find prefix until first wildcard// 这个就是查找path是否有通配符的*,:,/// 没有查到直接break// wildcard拿的是中间那段比如/:w/hello 那wildcard就是:wwildcard, i, valid : findWildcard(path)if i 0 { // No wildcard foundbreak}// The wildcard name must only contain one : or * characterif !valid {panic(only one wildcard per path segment is allowed, has: wildcard in path fullPath )}// check if the wildcard has a nameif len(wildcard) 2 {panic(wildcards must be named with a non-empty name in path fullPath )}// 如果wildcard首字符是:拼装child把wildcard之前的不是通配符区块的拼接到n的path中if wildcard[0] : { // paramif i 0 {// Insert prefix before the current wildcardn.path path[:i]path path[i:]}child : node{nType: param,path: wildcard,fullPath: fullPath,}n.addChild(child)n.wildChild truen childn.priority// if the path doesnt end with the wildcard, then there// will be another subpath starting with /if len(wildcard) len(path) {path path[len(wildcard):]child : node{priority: 1,fullPath: fullPath,}n.addChild(child)n childcontinue}// Otherwise were done. Insert the handle in the new leafn.handlers handlersreturn}// catchAllif ilen(wildcard) ! len(path) {panic(catch-all routes are only allowed at the end of the path in path fullPath )}if len(n.path) 0 n.path[len(n.path)-1] / {pathSeg : strings.SplitN(n.children[0].path, /, 2)[0]panic(catch-all wildcard path in new path fullPath conflicts with existing path segment pathSeg in existing prefix n.path pathSeg )}// currently fixed width 1 for /i--if path[i] ! / {panic(no / before catch-all in path fullPath )}n.path path[:i]// First node: catchAll node with empty pathchild : node{wildChild: true,nType: catchAll,fullPath: fullPath,}n.addChild(child)n.indices string(/)n childn.priority// second node: node holding the variablechild node{path: path[i:],nType: catchAll,handlers: handlers,priority: 1,fullPath: fullPath,}n.children []*node{child}return}// If no wildcard was found, simply insert the path and handlen.path pathn.handlers handlersn.fullPath fullPath }这里放一些panic的或者正常的测试代码实例方便大家理解 func TestTreePanic1(t *testing.T) {tree : node{}routes : [...]string{/hi/,/hi/:go,/hi/:go1,}for _, route : range routes {tree.addRoute(route, fakeHandler(route))} }
http://www.dnsts.com.cn/news/71007.html

相关文章:

  • 网站哪里可以做学网站制作
  • 个人网站建设规划表深圳营销网站设计
  • 网站排名网络推广做金融资讯用什么网站程序
  • 厦门开发网站公司那个软件可以做三个视频网站
  • 深圳住房和建设局网站预约网站开发的pc或移动端
  • 通城做网站的网站构建的基本流程
  • 网站 建设设计新手怎么从1688拿货
  • 上海物流公司网站建设建设网站包括哪些费用
  • 网站制作维护费 归属南昌做seo的公司有哪些
  • 一个公司网站备案吗网站建站售后服务
  • 成华区微信网站建设公龙岗网站制作
  • 国内贸易在那个网站上做凡客建站免费的可以用多久
  • 网站手机客户端如何开发佛山网站建设公司怎么选
  • 建一个网站的手机电脑网站收录查询网
  • 自己做的网站 怎么在网上销售wordpress生成缩略图
  • 做明信片的网站学校做网站的软件
  • 建站优化办事效率高wordpress uedito
  • primefaces做网站小程序是什么
  • 营销型网站建设 高校邦中企建网站
  • 网站域名放国外建设人才网站
  • 公司不需要做网站了企业培训心得体会
  • 网站建设需求调研问卷什么网站可以查房屋建筑面积
  • 建立网站一般那些阶段建设网站有哪些目的
  • 网站上线备案o2o的含义
  • 小语种外贸网站建设个人养老保险怎么买最划算
  • 网站关键字可以修改吗上海网站建设加q.479185700
  • 响应式网站设计的规范网页打不开怎么解决方法
  • 域名被墙检测网站技术类网站模板
  • 做网站需要交维护费么卖货到海外的免费平台
  • 企业网站无线端怎么做北京网站制作飞沐