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

大型的网站开发宁波seo外包公司

大型的网站开发,宁波seo外包公司,政务网站开发,wordpress仿站价格文章目录 首先回顾一下gin框架的路由如何使用的从源码分析一下gin框架gin的路由实现前缀树前缀树的实现压缩前缀树--Radix TrieTrie VS Map 首先回顾一下gin框架的路由如何使用的 package mainimport ("fmt""github.com/gin-gonic/gin""net/http&quo…

文章目录

  • 首先回顾一下gin框架的路由如何使用的
  • 从源码分析一下gin框架
  • gin的路由实现
    • 前缀树
    • 前缀树的实现
    • 压缩前缀树--Radix Trie
    • Trie VS Map

首先回顾一下gin框架的路由如何使用的

package mainimport ("fmt""github.com/gin-gonic/gin""net/http"
)func main() {//创建一个gin Engine ,Engine实现了net/http包下Handler接口,本质上是一个http Handlerr := gin.Default()//注册中间件r.Use(middleware1)r.Use(middleware2)//注册一个path为/hello的处理函数,将/hello域名下的get请求路由到后面的handler函数来处理r.GET("/hello", handler)r.Run()}
func middleware1(c *gin.Context) {fmt.Println("Executing middleware1")c.Next() //将控制权传递给下一个中间件或者处理函数//c.Abort() //在请求处理过程中发现错误或异常情况时,立即终止处理并返回错误信息,后续的处理程序或者中间件不在执行//Executing middleware1//middle1 endfmt.Println("middle1 end")
}func middleware2(c *gin.Context) {fmt.Println("Executing middleware2")c.Next()fmt.Println("middle2 end")
}func handler(c *gin.Context) {fmt.Println("Executing handler")fmt.Println("handler end")c.JSON(http.StatusOK, gin.H{"message": "hello word"})}/*
Executing middleware1
Executing middleware2
Executing handler
handler end
middle2 end
middle1 end*/

运行程序
在浏览器输入“http://127.0.0.1:8080/hello”可以看到
在这里插入图片描述
http的请求有9种,GET\HEAD\POST\PUT\PATCH\DELETE\CONNECT\OPTIONS\TRACE

从源码分析一下gin框架

首先通过“gin.Default()”创建一个Engine实例并附带有“Logger\Recovery”中间件

// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {debugPrintWARNINGDefault()engine := New()engine.Use(Logger(), Recovery())return engine
}

Default()实际上是调用了New(),并使用use注册“Logger\Recovery”两个中间件

func New() *Engine {debugPrintWARNINGNew()engine := &Engine{RouterGroup: RouterGroup{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:     defaultMultipartMemory,trees:                  make(methodTrees, 0, 9),delims:                 render.Delims{Left: "{{", Right: "}}"},secureJSONPrefix:       "while(1);",trustedProxies:         []string{"0.0.0.0/0", "::/0"},trustedCIDRs:           defaultTrustedCIDRs,}engine.RouterGroup.engine = engineengine.pool.New = func() any {return engine.allocateContext(engine.maxParams)}return engine
}

New()函数初始化Engine实例并返回,接下来看一下Engine struct都有些什么

type Engine struct {RouterGroup// RedirectTrailingSlash enables automatic redirection if the current route can't be matched but a// handler for the path with (without) the trailing slash exists.// For example if /foo/ is requested but a route only exists for /foo, the// client is redirected to /foo with http status code 301 for GET requests// and 307 for all other request methods.RedirectTrailingSlash bool// RedirectFixedPath if enabled, the router tries to fix the current request path, if no// handle is registered for it.// First superfluous path elements like ../ or // are removed.// Afterwards the router does a case-insensitive lookup of the cleaned path.// If a handle can be found for this route, the router makes a redirection// to the corrected path with status code 301 for GET requests and 307 for// all other request methods.// For example /FOO and /..//Foo could be redirected to /foo.// RedirectTrailingSlash is independent of this option.RedirectFixedPath bool// HandleMethodNotAllowed if enabled, the router checks if another method is allowed for the// current route, if the current request can not be routed.// If this is the case, the request is answered with 'Method Not Allowed'// and HTTP status code 405.// If no other Method is allowed, the request is delegated to the NotFound// handler.HandleMethodNotAllowed bool// ForwardedByClientIP if enabled, client IP will be parsed from the request's headers that// match those stored at `(*gin.Engine).RemoteIPHeaders`. If no IP was// fetched, it falls back to the IP obtained from// `(*gin.Context).Request.RemoteAddr`.ForwardedByClientIP bool// AppEngine was deprecated.// Deprecated: USE `TrustedPlatform` WITH VALUE `gin.PlatformGoogleAppEngine` INSTEAD// #726 #755 If enabled, it will trust some headers starting with// 'X-AppEngine...' for better integration with that PaaS.AppEngine bool// UseRawPath if enabled, the url.RawPath will be used to find parameters.UseRawPath bool// UnescapePathValues if true, the path value will be unescaped.// If UseRawPath is false (by default), the UnescapePathValues effectively is true,// as url.Path gonna be used, which is already unescaped.UnescapePathValues bool// RemoveExtraSlash a parameter can be parsed from the URL even with extra slashes.// See the PR #1817 and issue #1644RemoveExtraSlash bool// RemoteIPHeaders list of headers used to obtain the client IP when// `(*gin.Engine).ForwardedByClientIP` is `true` and// `(*gin.Context).Request.RemoteAddr` is matched by at least one of the// network origins of list defined by `(*gin.Engine).SetTrustedProxies()`.RemoteIPHeaders []string// TrustedPlatform if set to a constant of value gin.Platform*, trusts the headers set by// that platform, for example to determine the client IPTrustedPlatform string// MaxMultipartMemory value of 'maxMemory' param that is given to http.Request's ParseMultipartForm// method call.MaxMultipartMemory int64// UseH2C enable h2c support.UseH2C bool// ContextWithFallback enable fallback Context.Deadline(), Context.Done(), Context.Err() and Context.Value() when Context.Request.Context() is not nil.ContextWithFallback booldelims           render.DelimssecureJSONPrefix stringHTMLRender       render.HTMLRenderFuncMap          template.FuncMapallNoRoute       HandlersChainallNoMethod      HandlersChainnoRoute          HandlersChainnoMethod         HandlersChainpool             sync.Pooltrees            methodTreesmaxParams        uint16maxSections      uint16trustedProxies   []stringtrustedCIDRs     []*net.IPNet
}

从结构体中发现,Engine有一个“sync.Pool”类型的pool变量:
sync.Pool是sync包下的一个内存池组件用来实现对象的复用,避免重复创建相同的对象,造成频繁的内存分配和gc,以达到提升程序性能的目的,虽然池子中的对象可以被复用,
但是sync.Pool并不会永久保存这个对象,池子中的对象会在一定时间后被gc回收,这个时间是随机的。所以用sync.Pool持久化存储对象不可取
sync.Pool本身是线程安全的,支持多个goroutine并发的往sync.Pool存取数据

sync.Pool的使用例子

package main
import ("fmt""sync"
)type Student struct {Name stringAge  int
}func main() {pool := sync.Pool{New: func() interface{} {return &Student{Name: "zhangsan",Age:  23,}},}st := pool.Get().(*Student)fmt.Println(st.Age, st.Name)fmt.Printf("%p\n", &st)pool.Put(st)st = pool.Get().(*Student)fmt.Println(st.Age, st.Name)fmt.Printf("%p\n", &st)}
//23 zhangsan
//0xc00000a028
//23 zhangsan 
//0xc00000a028

Engine结构RouterGroup结构体,接下来看一下“RouterGroup”结构体

type RouterGroup struct {//HandlersChain defines a HandlerFunc slice//HandlerFunc defines the handler used by gin middleware as return valueHandlers HandlersChainbasePath stringengine   *Engineroot     bool
}

Engine中还有一个“methodTrees”类型的变量trees

trees            methodTrees
type methodTrees []methodTree
type methodTree struct{method stringroot *node
}
type node struct {path      stringindices   stringwildChild boolnType     nodeTypepriority  uint32children  []*node // child nodes, at most 1 :param style node at the end of the arrayhandlers  HandlersChainfullPath  string
}

node 中最重要的两个结构就是

type node struct{wildChild boolchildren []*node
}
//其实这个就是前缀树实现的比较重要的两个数据变量

gin的路由实现

gin的每种请求,都是一个域名对应着一个路由处理函数,就是一种映射关系;直观上可以使用map存储这种映射关系,key存储域名,value存储域名对应的处理函数;
但实际上,gin路由底层实现这个映射使用的是压缩前缀树,首先介绍一下前缀树

前缀树

前缀树就是trie树,是一种树形结构,用于高效地存储和检索字符串集合。基本思想就是将字符串中的每个字符作为树的一个节点,从根节点开始,每个节点代表代表字符串中的一个前缀。在Trie树,每个节点都包含一个指向子节点的指针数组,数组的大小等于字符集的大小。如果某个节点代表的字符串是一个单词的结尾,可以在该节点上做一个标记。
以下是前缀树的一些特性:

  • 前缀匹配。前缀树可以高效地实现前缀匹配。给定一个前缀,可以在O(k)的时间复杂度内找到所有以该前缀开头的字符串,k是前缀的长度
  • 高效存储和检索。前缀树可以高效地存储和检索字符串集合。在插入和查找字符串时,时间复杂度为O(k),k是字符串的长度。相比于哈希表和二叉搜索树,前缀树在字符串存储和检索方面具有更好的性能
  • 消耗较大的空间。空间复杂度较高,每个节点都需要存储指向子节点的指针数组,节点的数量可能会非常多。空间复杂度为O(n*m)其中n为字符串集合中字符串的平均长度,m为字符串数量
  • 支持删除操作。删除操作相对比较复杂,删除一个字符串需要同时删除相应的节点,需要处理节点合并的情况。
  • 应用场景。单词查询,自动补全,模糊匹配

前缀树的实现

前缀树的实现可以参考力扣上的一道代码题
题目链接
大致的思路就是

go 语言实现

type Trie struct {trie [26]*Trieflag bool
}func Constructor() Trie {return Trie{}
}func (this *Trie) Insert(word string)  {tr:= thisfor i:=0;i<len(word);i++{if tr.trie[word[i]-'a']==nil{tr.trie[word[i]-'a'] = &Trie{}}tr=tr.trie[word[i]-'a']}tr.flag=true
}func (this *Trie) Search(word string) bool {tr:=this.startpre(word)return tr!=nil && tr.flag
}func (this *Trie) StartsWith(prefix string) bool {return this.startpre(prefix)!=nil
}
func (this *Trie)startpre(pre string)*Trie{tr:=this    for i:=0;i<len(pre);i++{if tr.trie[pre[i]-'a']==nil{return nil}tr=tr.trie[pre[i]-'a']}return tr
}/*** Your Trie object will be instantiated and called as such:* obj := Constructor();* obj.Insert(word);* param_2 := obj.Search(word);* param_3 := obj.StartsWith(prefix);*/

C++实现版本,由于C++没有回收内存的机制所以需要手动释放内存

class Trie {
public:~Trie(){//析构函数释放内存del(this);}Trie():trie(26),flag(false){}void insert(string word) {Trie *tr = this;for(const char&c:word){if(!tr->trie[c-'a']) tr->trie[c-'a'] = new Trie();tr=tr->trie[c-'a'];}tr->flag=true;}bool search(string word) {Trie *tr = this;for(const char&c:word){if(!tr->trie[c-'a']) return false;tr=tr->trie[c-'a'];}return tr->flag;}bool startsWith(string prefix) {Trie *tr = this;for(const char&c:prefix){if(!tr->trie[c-'a']) return false;tr=tr->trie[c-'a'];}return true;}
private:vector<Trie*> trie;bool flag;
private:void del(Trie *tr){for(int i=0;i<26;++i){delete(tr->trie[i]);tr->trie[i]=nullptr;}}
};/*** Your Trie object will be instantiated and called as such:* Trie* obj = new Trie();* obj->insert(word);* bool param_2 = obj->search(word);* bool param_3 = obj->startsWith(prefix);*/

解释一下其中的关键代码,del

	void del(Trie *tr){//由于每个Trie节点都有一个长度为26的指向子节点的指针数组,所以写了一个长度为26的for循环for(int i=0;i<26;++i){//由于tr->trie[i]是一个Trie的数据类型变量,所以在delete tr->trie[i]的时候会触发tr-trie[i]的析构函数,往下递归的进行内存释放delete(tr->trie[i]);//释放完之后为了防止野指针,将tr->trie[i]设置为nullptrtr->trie[i]=nullptr;}}

压缩前缀树–Radix Trie

从上述的分析和代码实现可以看出,前缀树占用的空间特别大。那么为了优化空间的利用率,gin的路由采用了“压缩前缀树”。

使用如下方法进行压缩:

  • 合并相邻的只有一个子节点的节点:遍历前缀树,当发现一个节点只有一个子节点时,将该节点与其子节点合并。合并时,将子节点的字符添加到父节点的字符中,形成新的字符
  • 递归地压缩子树,在合并节点后,可能会出现新的节点也只有一个子节点的情况,可以递归的对子树进行压缩,直到无法压缩为止
  • 使用特殊字符表示合并,为了在压缩前缀树中表示节点的合并,可以使用一些特殊的字符进行标记。例如可以使用$表示节点的合并

接下来,利用数据结构可视化工具查看两个结构的不同
依次插入[“apple”,“apricot”,“banana”,“bat”]

前缀树的结构如下

在这里插入图片描述

压缩前缀树的结构如下

在这里插入图片描述

从两图可以看出,Radix Trie对Trie进行了压缩:遍历Trie发现某个节点只有一个子节点,就与子节点进行合并,减少指针数组的占用,优化空间

Trie VS Map

接下来说一下为什么不用map实现路径和路由函数之间的映射。

  • map并不支持模糊匹配和参数提取;前缀树可以根据前缀进行参数提取,而使用map进行参数提取需要额外的步骤
  • 前缀树可以根据前缀进行分组管理,而map不行

文章转载自:
http://wanjiacyanurate.gthc.cn
http://wanjiaprimitively.gthc.cn
http://wanjiapolluted.gthc.cn
http://wanjiapuglia.gthc.cn
http://wanjiamanway.gthc.cn
http://wanjiaanecdotage.gthc.cn
http://wanjiakeratoconus.gthc.cn
http://wanjiaenforcement.gthc.cn
http://wanjiaadrenalectomize.gthc.cn
http://wanjiabacklash.gthc.cn
http://wanjiaimbibition.gthc.cn
http://wanjiabusinessmen.gthc.cn
http://wanjiagroschen.gthc.cn
http://wanjialehua.gthc.cn
http://wanjiaunencumbered.gthc.cn
http://wanjiabacteric.gthc.cn
http://wanjiachoirgirl.gthc.cn
http://wanjiaconfluent.gthc.cn
http://wanjiaanthropophuistic.gthc.cn
http://wanjiasubrent.gthc.cn
http://wanjiapraelector.gthc.cn
http://wanjiaracially.gthc.cn
http://wanjiaropedancing.gthc.cn
http://wanjiabiorheology.gthc.cn
http://wanjiammhg.gthc.cn
http://wanjiasmug.gthc.cn
http://wanjialiege.gthc.cn
http://wanjiamystical.gthc.cn
http://wanjialippitude.gthc.cn
http://wanjianominalistic.gthc.cn
http://wanjiabrochure.gthc.cn
http://wanjiaoutgrow.gthc.cn
http://wanjiaquinquepartite.gthc.cn
http://wanjiaseptenate.gthc.cn
http://wanjiacowfish.gthc.cn
http://wanjiagrossdeutsch.gthc.cn
http://wanjiaultrasound.gthc.cn
http://wanjiademonism.gthc.cn
http://wanjiafora.gthc.cn
http://wanjiafantom.gthc.cn
http://wanjialonely.gthc.cn
http://wanjiamesmerization.gthc.cn
http://wanjiapediculicide.gthc.cn
http://wanjiainterstadial.gthc.cn
http://wanjiabeetsugar.gthc.cn
http://wanjiadari.gthc.cn
http://wanjiafront.gthc.cn
http://wanjiarobinsonite.gthc.cn
http://wanjiainbeing.gthc.cn
http://wanjiaorganogenesis.gthc.cn
http://wanjiasanguine.gthc.cn
http://wanjiaoccasion.gthc.cn
http://wanjiacringe.gthc.cn
http://wanjiaprebend.gthc.cn
http://wanjiawailful.gthc.cn
http://wanjiaskillet.gthc.cn
http://wanjiaetymologize.gthc.cn
http://wanjiamayanist.gthc.cn
http://wanjiaantiglobulin.gthc.cn
http://wanjiahuff.gthc.cn
http://wanjianu.gthc.cn
http://wanjiareadset.gthc.cn
http://wanjiafloralize.gthc.cn
http://wanjiaquarrelsomely.gthc.cn
http://wanjiaincorporable.gthc.cn
http://wanjiaunbirthday.gthc.cn
http://wanjiachronicity.gthc.cn
http://wanjiaanimal.gthc.cn
http://wanjiabavarian.gthc.cn
http://wanjiacerium.gthc.cn
http://wanjiasabina.gthc.cn
http://wanjiawoden.gthc.cn
http://wanjiadampness.gthc.cn
http://wanjiacabasset.gthc.cn
http://wanjiadipteral.gthc.cn
http://wanjiaardently.gthc.cn
http://wanjiadermatological.gthc.cn
http://wanjiabemoist.gthc.cn
http://wanjiamexicali.gthc.cn
http://wanjiaplim.gthc.cn
http://www.15wanjia.com/news/116248.html

相关文章:

  • 国外做任务赚钱的网站有哪些哪家竞价托管专业
  • 乌鲁木齐做网站广州网站建设推广专家
  • 用wordpress做的网站有哪些怎么做网络宣传推广
  • 滨州做网站的公司全网推广哪家正宗可靠
  • 做机械设计的网站市场调研报告word模板
  • 网站建设与管理代码南宁白帽seo技术
  • 专门做调研的网站举例说明什么是seo
  • 喀什哪有做网站的seo培训学院
  • 做任务的奖金网站青岛网站快速排名提升
  • 网站建设公司价位搭建网站的软件
  • 专业电商网站建设青山seo排名公司
  • 精英学校老师给学生做的网站乐山网站seo
  • wordpress漏洞webshell湖北seo整站优化
  • 江苏住房和城乡建设厅官方网站长沙网络推广营销
  • 用v9做的网站上传服务器网站设计开发网站
  • 在西安市建设工程交易中心网站上百度推广的优化软件
  • 硅胶 技术支持 东莞网站建设今日国内新闻热点
  • 请问怎么做网站关键词搜索工具好站网
  • 厦门网站建设找哪家比较好专业竞价托管哪家好
  • 广州番禺营销型网站建设百度推广开户费用
  • 2019做网站seo行不行软文广告文案
  • 做kegg通路富集的网站网页制作接单平台
  • 怎样如何做网站赚钱统计网站流量的网站
  • 十大不收费看盘软件排名搜外seo
  • 什么网站做新产品代理网站推广策划报告
  • axure做网站首页东莞百度推广排名优化
  • 外贸网站建设要注意什么永久免费二级域名申请
  • 网站开发平台介绍长沙seo服务哪个公司好
  • 哈尔滨网站建设 seo最新全国疫情消息
  • 深圳做网站建设月薪多少百度电视剧风云榜